roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = 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        
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         this.initEvents();
141         
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229         return ret;
230     },
231     
232     addxtypeChild : function (tree, cntr)
233     {
234         Roo.debug && Roo.log('addxtypeChild:' + cntr);
235         var cn = this;
236         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
237         
238         
239         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
240                     (typeof(tree['flexy:foreach']) != 'undefined');
241           
242         
243         
244          skip_children = false;
245         // render the element if it's not BODY.
246         if (tree.xtype != 'Body') {
247            
248             cn = Roo.factory(tree);
249            
250             cn.parentType = this.xtype; //??
251             cn.parentId = this.id;
252             
253             var build_from_html =  Roo.XComponent.build_from_html;
254             
255             
256             // does the container contain child eleemnts with 'xtype' attributes.
257             // that match this xtype..
258             // note - when we render we create these as well..
259             // so we should check to see if body has xtype set.
260             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
261                
262                 var self_cntr_el = Roo.get(this[cntr](false));
263                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
264                 if (echild) { 
265                     Roo.log(Roo.XComponent.build_from_html);
266                     Roo.log("got echild:");
267                     Roo.log(echild);
268                 }
269                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
270                 // and are not displayed -this causes this to use up the wrong element when matching.
271                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
272                 
273                 
274                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
275                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
276                   
277                   
278                   
279                     cn.el = echild;
280                   //  Roo.log("GOT");
281                     //echild.dom.removeAttribute('xtype');
282                 } else {
283                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
284                     Roo.debug && Roo.log(self_cntr_el);
285                     Roo.debug && Roo.log(echild);
286                     Roo.debug && Roo.log(cn);
287                 }
288             }
289            
290             
291            
292             // if object has flexy:if - then it may or may not be rendered.
293             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
294                 // skip a flexy if element.
295                 Roo.debug && Roo.log('skipping render');
296                 Roo.debug && Roo.log(tree);
297                 if (!cn.el) {
298                     Roo.debug && Roo.log('skipping all children');
299                     skip_children = true;
300                 }
301                 
302              } else {
303                  
304                 // actually if flexy:foreach is found, we really want to create 
305                 // multiple copies here...
306                 //Roo.log('render');
307                 //Roo.log(this[cntr]());
308                 cn.render(this[cntr](true));
309              }
310             // then add the element..
311         }
312         
313         
314         // handle the kids..
315         
316         var nitems = [];
317         /*
318         if (typeof (tree.menu) != 'undefined') {
319             tree.menu.parentType = cn.xtype;
320             tree.menu.triggerEl = cn.el;
321             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
322             
323         }
324         */
325         if (!tree.items || !tree.items.length) {
326             cn.items = nitems;
327             return cn;
328         }
329         var items = tree.items;
330         delete tree.items;
331         
332         //Roo.log(items.length);
333             // add the items..
334         if (!skip_children) {    
335             for(var i =0;i < items.length;i++) {
336                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
337             }
338         }
339         
340         cn.items = nitems;
341         
342         this.fireEvent('childrenrendered', this);
343         
344         return cn;
345     } 
346     
347     
348 });
349
350  /*
351  * - LGPL
352  *
353  * Body
354  * 
355  */
356
357 /**
358  * @class Roo.bootstrap.Body
359  * @extends Roo.bootstrap.Component
360  * Bootstrap Body class
361  * 
362  * @constructor
363  * Create a new body
364  * @param {Object} config The config object
365  */
366
367 Roo.bootstrap.Body = function(config){
368     Roo.bootstrap.Body.superclass.constructor.call(this, config);
369     this.el = Roo.get(document.body);
370     if (this.cls && this.cls.length) {
371         Roo.get(document.body).addClass(this.cls);
372     }
373 };
374
375 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
376       
377         autoCreate : {
378         cls: 'container'
379     },
380     onRender : function(ct, position)
381     {
382        /* Roo.log("Roo.bootstrap.Body - onRender");
383         if (this.cls && this.cls.length) {
384             Roo.get(document.body).addClass(this.cls);
385         }
386         // style??? xttr???
387         */
388     }
389     
390     
391  
392    
393 });
394
395  /*
396  * - LGPL
397  *
398  * button group
399  * 
400  */
401
402
403 /**
404  * @class Roo.bootstrap.ButtonGroup
405  * @extends Roo.bootstrap.Component
406  * Bootstrap ButtonGroup class
407  * @cfg {String} size lg | sm | xs (default empty normal)
408  * @cfg {String} align vertical | justified  (default none)
409  * @cfg {String} direction up | down (default down)
410  * @cfg {Boolean} toolbar false | true
411  * @cfg {Boolean} btn true | false
412  * 
413  * 
414  * @constructor
415  * Create a new Input
416  * @param {Object} config The config object
417  */
418
419 Roo.bootstrap.ButtonGroup = function(config){
420     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
421 };
422
423 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
424     
425     size: '',
426     align: '',
427     direction: '',
428     toolbar: false,
429     btn: true,
430
431     getAutoCreate : function(){
432         var cfg = {
433             cls: 'btn-group',
434             html : null
435         }
436         
437         cfg.html = this.html || cfg.html;
438         
439         if (this.toolbar) {
440             cfg = {
441                 cls: 'btn-toolbar',
442                 html: null
443             }
444             
445             return cfg;
446         }
447         
448         if (['vertical','justified'].indexOf(this.align)!==-1) {
449             cfg.cls = 'btn-group-' + this.align;
450             
451             if (this.align == 'justified') {
452                 console.log(this.items);
453             }
454         }
455         
456         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
457             cfg.cls += ' btn-group-' + this.size;
458         }
459         
460         if (this.direction == 'up') {
461             cfg.cls += ' dropup' ;
462         }
463         
464         return cfg;
465     }
466    
467 });
468
469  /*
470  * - LGPL
471  *
472  * button
473  * 
474  */
475
476 /**
477  * @class Roo.bootstrap.Button
478  * @extends Roo.bootstrap.Component
479  * Bootstrap Button class
480  * @cfg {String} html The button content
481  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
482  * @cfg {String} size ( lg | sm | xs)
483  * @cfg {String} tag ( a | input | submit)
484  * @cfg {String} href empty or href
485  * @cfg {Boolean} disabled default false;
486  * @cfg {Boolean} isClose default false;
487  * @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)
488  * @cfg {String} badge text for badge
489  * @cfg {String} theme default 
490  * @cfg {Boolean} inverse 
491  * @cfg {Boolean} toggle 
492  * @cfg {String} ontext text for on toggle state
493  * @cfg {String} offtext text for off toggle state
494  * @cfg {Boolean} defaulton 
495  * @cfg {Boolean} preventDefault  default true
496  * @cfg {Boolean} removeClass remove the standard class..
497  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
498  * 
499  * @constructor
500  * Create a new button
501  * @param {Object} config The config object
502  */
503
504
505 Roo.bootstrap.Button = function(config){
506     Roo.bootstrap.Button.superclass.constructor.call(this, config);
507     this.addEvents({
508         // raw events
509         /**
510          * @event click
511          * When a butotn is pressed
512          * @param {Roo.bootstrap.Button} this
513          * @param {Roo.EventObject} e
514          */
515         "click" : true,
516          /**
517          * @event toggle
518          * After the button has been toggles
519          * @param {Roo.EventObject} e
520          * @param {boolean} pressed (also available as button.pressed)
521          */
522         "toggle" : true
523     });
524 };
525
526 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
527     html: false,
528     active: false,
529     weight: '',
530     size: '',
531     tag: 'button',
532     href: '',
533     disabled: false,
534     isClose: false,
535     glyphicon: '',
536     badge: '',
537     theme: 'default',
538     inverse: false,
539     
540     toggle: false,
541     ontext: 'ON',
542     offtext: 'OFF',
543     defaulton: true,
544     preventDefault: true,
545     removeClass: false,
546     name: false,
547     target: false,
548     
549     
550     pressed : null,
551      
552     
553     getAutoCreate : function(){
554         
555         var cfg = {
556             tag : 'button',
557             cls : 'roo-button',
558             html: ''
559         };
560         
561         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
562             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
563             this.tag = 'button';
564         } else {
565             cfg.tag = this.tag;
566         }
567         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
568         
569         if (this.toggle == true) {
570             cfg={
571                 tag: 'div',
572                 cls: 'slider-frame roo-button',
573                 cn: [
574                     {
575                         tag: 'span',
576                         'data-on-text':'ON',
577                         'data-off-text':'OFF',
578                         cls: 'slider-button',
579                         html: this.offtext
580                     }
581                 ]
582             };
583             
584             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
585                 cfg.cls += ' '+this.weight;
586             }
587             
588             return cfg;
589         }
590         
591         if (this.isClose) {
592             cfg.cls += ' close';
593             
594             cfg["aria-hidden"] = true;
595             
596             cfg.html = "&times;";
597             
598             return cfg;
599         }
600         
601          
602         if (this.theme==='default') {
603             cfg.cls = 'btn roo-button';
604             
605             //if (this.parentType != 'Navbar') {
606             this.weight = this.weight.length ?  this.weight : 'default';
607             //}
608             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
609                 
610                 cfg.cls += ' btn-' + this.weight;
611             }
612         } else if (this.theme==='glow') {
613             
614             cfg.tag = 'a';
615             cfg.cls = 'btn-glow roo-button';
616             
617             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
618                 
619                 cfg.cls += ' ' + this.weight;
620             }
621         }
622    
623         
624         if (this.inverse) {
625             this.cls += ' inverse';
626         }
627         
628         
629         if (this.active) {
630             cfg.cls += ' active';
631         }
632         
633         if (this.disabled) {
634             cfg.disabled = 'disabled';
635         }
636         
637         if (this.items) {
638             Roo.log('changing to ul' );
639             cfg.tag = 'ul';
640             this.glyphicon = 'caret';
641         }
642         
643         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
644          
645         //gsRoo.log(this.parentType);
646         if (this.parentType === 'Navbar' && !this.parent().bar) {
647             Roo.log('changing to li?');
648             
649             cfg.tag = 'li';
650             
651             cfg.cls = '';
652             cfg.cn =  [{
653                 tag : 'a',
654                 cls : 'roo-button',
655                 html : this.html,
656                 href : this.href || '#'
657             }];
658             if (this.menu) {
659                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
660                 cfg.cls += ' dropdown';
661             }   
662             
663             delete cfg.html;
664             
665         }
666         
667        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
668         
669         if (this.glyphicon) {
670             cfg.html = ' ' + cfg.html;
671             
672             cfg.cn = [
673                 {
674                     tag: 'span',
675                     cls: 'glyphicon glyphicon-' + this.glyphicon
676                 }
677             ];
678         }
679         
680         if (this.badge) {
681             cfg.html += ' ';
682             
683             cfg.tag = 'a';
684             
685 //            cfg.cls='btn roo-button';
686             
687             cfg.href=this.href;
688             
689             var value = cfg.html;
690             
691             if(this.glyphicon){
692                 value = {
693                             tag: 'span',
694                             cls: 'glyphicon glyphicon-' + this.glyphicon,
695                             html: this.html
696                         };
697                 
698             }
699             
700             cfg.cn = [
701                 value,
702                 {
703                     tag: 'span',
704                     cls: 'badge',
705                     html: this.badge
706                 }
707             ];
708             
709             cfg.html='';
710         }
711         
712         if (this.menu) {
713             cfg.cls += ' dropdown';
714             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
715         }
716         
717         if (cfg.tag !== 'a' && this.href !== '') {
718             throw "Tag must be a to set href.";
719         } else if (this.href.length > 0) {
720             cfg.href = this.href;
721         }
722         
723         if(this.removeClass){
724             cfg.cls = '';
725         }
726         
727         if(this.target){
728             cfg.target = this.target;
729         }
730         
731         return cfg;
732     },
733     initEvents: function() {
734        // Roo.log('init events?');
735 //        Roo.log(this.el.dom);
736         // add the menu...
737         
738         if (typeof (this.menu) != 'undefined') {
739             this.menu.parentType = this.xtype;
740             this.menu.triggerEl = this.el;
741             this.addxtype(Roo.apply({}, this.menu));
742         }
743
744
745        if (this.el.hasClass('roo-button')) {
746             this.el.on('click', this.onClick, this);
747        } else {
748             this.el.select('.roo-button').on('click', this.onClick, this);
749        }
750        
751        if(this.removeClass){
752            this.el.on('click', this.onClick, this);
753        }
754        
755        this.el.enableDisplayMode();
756         
757     },
758     onClick : function(e)
759     {
760         if (this.disabled) {
761             return;
762         }
763         
764         
765         Roo.log('button on click ');
766         if(this.preventDefault){
767             e.preventDefault();
768         }
769         if (this.pressed === true || this.pressed === false) {
770             this.pressed = !this.pressed;
771             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
772             this.fireEvent('toggle', this, e, this.pressed);
773         }
774         
775         
776         this.fireEvent('click', this, e);
777     },
778     
779     /**
780      * Enables this button
781      */
782     enable : function()
783     {
784         this.disabled = false;
785         this.el.removeClass('disabled');
786     },
787     
788     /**
789      * Disable this button
790      */
791     disable : function()
792     {
793         this.disabled = true;
794         this.el.addClass('disabled');
795     },
796      /**
797      * sets the active state on/off, 
798      * @param {Boolean} state (optional) Force a particular state
799      */
800     setActive : function(v) {
801         
802         this.el[v ? 'addClass' : 'removeClass']('active');
803     },
804      /**
805      * toggles the current active state 
806      */
807     toggleActive : function()
808     {
809        var active = this.el.hasClass('active');
810        this.setActive(!active);
811        
812         
813     },
814     setText : function(str)
815     {
816         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
817     },
818     getText : function()
819     {
820         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
821     },
822     hide: function() {
823        
824      
825         this.el.hide();   
826     },
827     show: function() {
828        
829         this.el.show();   
830     }
831     
832     
833 });
834
835  /*
836  * - LGPL
837  *
838  * column
839  * 
840  */
841
842 /**
843  * @class Roo.bootstrap.Column
844  * @extends Roo.bootstrap.Component
845  * Bootstrap Column class
846  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
847  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
848  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
849  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
850  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
851  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
852  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
853  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
854  *
855  * 
856  * @cfg {Boolean} hidden (true|false) hide the element
857  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
858  * @cfg {String} fa (ban|check|...) font awesome icon
859  * @cfg {Number} fasize (1|2|....) font awsome size
860
861  * @cfg {String} icon (info-sign|check|...) glyphicon name
862
863  * @cfg {String} html content of column.
864  * 
865  * @constructor
866  * Create a new Column
867  * @param {Object} config The config object
868  */
869
870 Roo.bootstrap.Column = function(config){
871     Roo.bootstrap.Column.superclass.constructor.call(this, config);
872 };
873
874 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
875     
876     xs: false,
877     sm: false,
878     md: false,
879     lg: false,
880     xsoff: false,
881     smoff: false,
882     mdoff: false,
883     lgoff: false,
884     html: '',
885     offset: 0,
886     alert: false,
887     fa: false,
888     icon : false,
889     hidden : false,
890     fasize : 1,
891     
892     getAutoCreate : function(){
893         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
894         
895         cfg = {
896             tag: 'div',
897             cls: 'column'
898         };
899         
900         var settings=this;
901         ['xs','sm','md','lg'].map(function(size){
902             //Roo.log( size + ':' + settings[size]);
903             
904             if (settings[size+'off'] !== false) {
905                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
906             }
907             
908             if (settings[size] === false) {
909                 return;
910             }
911             Roo.log(settings[size]);
912             if (!settings[size]) { // 0 = hidden
913                 cfg.cls += ' hidden-' + size;
914                 return;
915             }
916             cfg.cls += ' col-' + size + '-' + settings[size];
917             
918         });
919         
920         if (this.hidden) {
921             cfg.cls += ' hidden';
922         }
923         
924         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
925             cfg.cls +=' alert alert-' + this.alert;
926         }
927         
928         
929         if (this.html.length) {
930             cfg.html = this.html;
931         }
932         if (this.fa) {
933             var fasize = '';
934             if (this.fasize > 1) {
935                 fasize = ' fa-' + this.fasize + 'x';
936             }
937             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
938             
939             
940         }
941         if (this.icon) {
942             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
943         }
944         
945         return cfg;
946     }
947    
948 });
949
950  
951
952  /*
953  * - LGPL
954  *
955  * page container.
956  * 
957  */
958
959
960 /**
961  * @class Roo.bootstrap.Container
962  * @extends Roo.bootstrap.Component
963  * Bootstrap Container class
964  * @cfg {Boolean} jumbotron is it a jumbotron element
965  * @cfg {String} html content of element
966  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
967  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
968  * @cfg {String} header content of header (for panel)
969  * @cfg {String} footer content of footer (for panel)
970  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
971  * @cfg {String} tag (header|aside|section) type of HTML tag.
972  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
973  * @cfg {String} fa (ban|check|...) font awesome icon
974  * @cfg {String} icon (info-sign|check|...) glyphicon name
975  * @cfg {Boolean} hidden (true|false) hide the element
976
977  *     
978  * @constructor
979  * Create a new Container
980  * @param {Object} config The config object
981  */
982
983 Roo.bootstrap.Container = function(config){
984     Roo.bootstrap.Container.superclass.constructor.call(this, config);
985 };
986
987 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
988     
989     jumbotron : false,
990     well: '',
991     panel : '',
992     header: '',
993     footer : '',
994     sticky: '',
995     tag : false,
996     alert : false,
997     fa: false,
998     icon : false,
999   
1000      
1001     getChildContainer : function() {
1002         
1003         if(!this.el){
1004             return false;
1005         }
1006         
1007         if (this.panel.length) {
1008             return this.el.select('.panel-body',true).first();
1009         }
1010         
1011         return this.el;
1012     },
1013     
1014     
1015     getAutoCreate : function(){
1016         
1017         var cfg = {
1018             tag : this.tag || 'div',
1019             html : '',
1020             cls : ''
1021         };
1022         if (this.jumbotron) {
1023             cfg.cls = 'jumbotron';
1024         }
1025         
1026         
1027         
1028         // - this is applied by the parent..
1029         //if (this.cls) {
1030         //    cfg.cls = this.cls + '';
1031         //}
1032         
1033         if (this.sticky.length) {
1034             
1035             var bd = Roo.get(document.body);
1036             if (!bd.hasClass('bootstrap-sticky')) {
1037                 bd.addClass('bootstrap-sticky');
1038                 Roo.select('html',true).setStyle('height', '100%');
1039             }
1040              
1041             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1042         }
1043         
1044         
1045         if (this.well.length) {
1046             switch (this.well) {
1047                 case 'lg':
1048                 case 'sm':
1049                     cfg.cls +=' well well-' +this.well;
1050                     break;
1051                 default:
1052                     cfg.cls +=' well';
1053                     break;
1054             }
1055         }
1056         
1057         if (this.hidden) {
1058             cfg.cls += ' hidden';
1059         }
1060         
1061         
1062         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1063             cfg.cls +=' alert alert-' + this.alert;
1064         }
1065         
1066         var body = cfg;
1067         
1068         if (this.panel.length) {
1069             cfg.cls += ' panel panel-' + this.panel;
1070             cfg.cn = [];
1071             if (this.header.length) {
1072                 cfg.cn.push({
1073                     
1074                     cls : 'panel-heading',
1075                     cn : [{
1076                         tag: 'h3',
1077                         cls : 'panel-title',
1078                         html : this.header
1079                     }]
1080                     
1081                 });
1082             }
1083             body = false;
1084             cfg.cn.push({
1085                 cls : 'panel-body',
1086                 html : this.html
1087             });
1088             
1089             
1090             if (this.footer.length) {
1091                 cfg.cn.push({
1092                     cls : 'panel-footer',
1093                     html : this.footer
1094                     
1095                 });
1096             }
1097             
1098         }
1099         
1100         if (body) {
1101             body.html = this.html || cfg.html;
1102             // prefix with the icons..
1103             if (this.fa) {
1104                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1105             }
1106             if (this.icon) {
1107                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1108             }
1109             
1110             
1111         }
1112         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1113             cfg.cls =  'container';
1114         }
1115         
1116         return cfg;
1117     },
1118     
1119     titleEl : function()
1120     {
1121         if(!this.el || !this.panel.length || !this.header.length){
1122             return;
1123         }
1124         
1125         return this.el.select('.panel-title',true).first();
1126     },
1127     
1128     setTitle : function(v)
1129     {
1130         var titleEl = this.titleEl();
1131         
1132         if(!titleEl){
1133             return;
1134         }
1135         
1136         titleEl.dom.innerHTML = v;
1137     },
1138     
1139     getTitle : function()
1140     {
1141         
1142         var titleEl = this.titleEl();
1143         
1144         if(!titleEl){
1145             return '';
1146         }
1147         
1148         return titleEl.dom.innerHTML;
1149     },
1150     
1151     show : function() {
1152         this.el.removeClass('hidden');
1153     },
1154     hide: function() {
1155         if (!this.el.hasClass('hidden')) {
1156             this.el.addClass('hidden');
1157         }
1158         
1159     }
1160    
1161 });
1162
1163  /*
1164  * - LGPL
1165  *
1166  * image
1167  * 
1168  */
1169
1170
1171 /**
1172  * @class Roo.bootstrap.Img
1173  * @extends Roo.bootstrap.Component
1174  * Bootstrap Img class
1175  * @cfg {Boolean} imgResponsive false | true
1176  * @cfg {String} border rounded | circle | thumbnail
1177  * @cfg {String} src image source
1178  * @cfg {String} alt image alternative text
1179  * @cfg {String} href a tag href
1180  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1181  * 
1182  * @constructor
1183  * Create a new Input
1184  * @param {Object} config The config object
1185  */
1186
1187 Roo.bootstrap.Img = function(config){
1188     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1189     
1190     this.addEvents({
1191         // img events
1192         /**
1193          * @event click
1194          * The img click event for the img.
1195          * @param {Roo.EventObject} e
1196          */
1197         "click" : true
1198     });
1199 };
1200
1201 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1202     
1203     imgResponsive: true,
1204     border: '',
1205     src: '',
1206     href: false,
1207     target: false,
1208
1209     getAutoCreate : function(){
1210         
1211         var cfg = {
1212             tag: 'img',
1213             cls: (this.imgResponsive) ? 'img-responsive' : '',
1214             html : null
1215         }
1216         
1217         cfg.html = this.html || cfg.html;
1218         
1219         cfg.src = this.src || cfg.src;
1220         
1221         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1222             cfg.cls += ' img-' + this.border;
1223         }
1224         
1225         if(this.alt){
1226             cfg.alt = this.alt;
1227         }
1228         
1229         if(this.href){
1230             var a = {
1231                 tag: 'a',
1232                 href: this.href,
1233                 cn: [
1234                     cfg
1235                 ]
1236             }
1237             
1238             if(this.target){
1239                 a.target = this.target;
1240             }
1241             
1242         }
1243         
1244         
1245         return (this.href) ? a : cfg;
1246     },
1247     
1248     initEvents: function() {
1249         
1250         if(!this.href){
1251             this.el.on('click', this.onClick, this);
1252         }
1253     },
1254     
1255     onClick : function(e)
1256     {
1257         Roo.log('img onclick');
1258         this.fireEvent('click', this, e);
1259     }
1260    
1261 });
1262
1263  /*
1264  * - LGPL
1265  *
1266  * image
1267  * 
1268  */
1269
1270
1271 /**
1272  * @class Roo.bootstrap.Link
1273  * @extends Roo.bootstrap.Component
1274  * Bootstrap Link Class
1275  * @cfg {String} alt image alternative text
1276  * @cfg {String} href a tag href
1277  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1278  * @cfg {String} html the content of the link.
1279  * @cfg {String} anchor name for the anchor link
1280
1281  * @cfg {Boolean} preventDefault (true | false) default false
1282
1283  * 
1284  * @constructor
1285  * Create a new Input
1286  * @param {Object} config The config object
1287  */
1288
1289 Roo.bootstrap.Link = function(config){
1290     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1291     
1292     this.addEvents({
1293         // img events
1294         /**
1295          * @event click
1296          * The img click event for the img.
1297          * @param {Roo.EventObject} e
1298          */
1299         "click" : true
1300     });
1301 };
1302
1303 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1304     
1305     href: false,
1306     target: false,
1307     preventDefault: false,
1308     anchor : false,
1309     alt : false,
1310
1311     getAutoCreate : function()
1312     {
1313         
1314         var cfg = {
1315             tag: 'a'
1316         };
1317         // anchor's do not require html/href...
1318         if (this.anchor === false) {
1319             cfg.html = this.html || 'html-missing';
1320             cfg.href = this.href || '#';
1321         } else {
1322             cfg.name = this.anchor;
1323             if (this.html !== false) {
1324                 cfg.html = this.html;
1325             }
1326             if (this.href !== false) {
1327                 cfg.href = this.href;
1328             }
1329         }
1330         
1331         if(this.alt !== false){
1332             cfg.alt = this.alt;
1333         }
1334         
1335         
1336         if(this.target !== false) {
1337             cfg.target = this.target;
1338         }
1339         
1340         return cfg;
1341     },
1342     
1343     initEvents: function() {
1344         
1345         if(!this.href || this.preventDefault){
1346             this.el.on('click', this.onClick, this);
1347         }
1348     },
1349     
1350     onClick : function(e)
1351     {
1352         if(this.preventDefault){
1353             e.preventDefault();
1354         }
1355         //Roo.log('img onclick');
1356         this.fireEvent('click', this, e);
1357     }
1358    
1359 });
1360
1361  /*
1362  * - LGPL
1363  *
1364  * header
1365  * 
1366  */
1367
1368 /**
1369  * @class Roo.bootstrap.Header
1370  * @extends Roo.bootstrap.Component
1371  * Bootstrap Header class
1372  * @cfg {String} html content of header
1373  * @cfg {Number} level (1|2|3|4|5|6) default 1
1374  * 
1375  * @constructor
1376  * Create a new Header
1377  * @param {Object} config The config object
1378  */
1379
1380
1381 Roo.bootstrap.Header  = function(config){
1382     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1383 };
1384
1385 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1386     
1387     //href : false,
1388     html : false,
1389     level : 1,
1390     
1391     
1392     
1393     getAutoCreate : function(){
1394         
1395         
1396         
1397         var cfg = {
1398             tag: 'h' + (1 *this.level),
1399             html: this.html || ''
1400         } ;
1401         
1402         return cfg;
1403     }
1404    
1405 });
1406
1407  
1408
1409  /*
1410  * Based on:
1411  * Ext JS Library 1.1.1
1412  * Copyright(c) 2006-2007, Ext JS, LLC.
1413  *
1414  * Originally Released Under LGPL - original licence link has changed is not relivant.
1415  *
1416  * Fork - LGPL
1417  * <script type="text/javascript">
1418  */
1419  
1420 /**
1421  * @class Roo.bootstrap.MenuMgr
1422  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1423  * @singleton
1424  */
1425 Roo.bootstrap.MenuMgr = function(){
1426    var menus, active, groups = {}, attached = false, lastShow = new Date();
1427
1428    // private - called when first menu is created
1429    function init(){
1430        menus = {};
1431        active = new Roo.util.MixedCollection();
1432        Roo.get(document).addKeyListener(27, function(){
1433            if(active.length > 0){
1434                hideAll();
1435            }
1436        });
1437    }
1438
1439    // private
1440    function hideAll(){
1441        if(active && active.length > 0){
1442            var c = active.clone();
1443            c.each(function(m){
1444                m.hide();
1445            });
1446        }
1447    }
1448
1449    // private
1450    function onHide(m){
1451        active.remove(m);
1452        if(active.length < 1){
1453            Roo.get(document).un("mouseup", onMouseDown);
1454             
1455            attached = false;
1456        }
1457    }
1458
1459    // private
1460    function onShow(m){
1461        var last = active.last();
1462        lastShow = new Date();
1463        active.add(m);
1464        if(!attached){
1465           Roo.get(document).on("mouseup", onMouseDown);
1466            
1467            attached = true;
1468        }
1469        if(m.parentMenu){
1470           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1471           m.parentMenu.activeChild = m;
1472        }else if(last && last.isVisible()){
1473           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1474        }
1475    }
1476
1477    // private
1478    function onBeforeHide(m){
1479        if(m.activeChild){
1480            m.activeChild.hide();
1481        }
1482        if(m.autoHideTimer){
1483            clearTimeout(m.autoHideTimer);
1484            delete m.autoHideTimer;
1485        }
1486    }
1487
1488    // private
1489    function onBeforeShow(m){
1490        var pm = m.parentMenu;
1491        if(!pm && !m.allowOtherMenus){
1492            hideAll();
1493        }else if(pm && pm.activeChild && active != m){
1494            pm.activeChild.hide();
1495        }
1496    }
1497
1498    // private
1499    function onMouseDown(e){
1500         Roo.log("on MouseDown");
1501         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1502            hideAll();
1503         }
1504         
1505         
1506    }
1507
1508    // private
1509    function onBeforeCheck(mi, state){
1510        if(state){
1511            var g = groups[mi.group];
1512            for(var i = 0, l = g.length; i < l; i++){
1513                if(g[i] != mi){
1514                    g[i].setChecked(false);
1515                }
1516            }
1517        }
1518    }
1519
1520    return {
1521
1522        /**
1523         * Hides all menus that are currently visible
1524         */
1525        hideAll : function(){
1526             hideAll();  
1527        },
1528
1529        // private
1530        register : function(menu){
1531            if(!menus){
1532                init();
1533            }
1534            menus[menu.id] = menu;
1535            menu.on("beforehide", onBeforeHide);
1536            menu.on("hide", onHide);
1537            menu.on("beforeshow", onBeforeShow);
1538            menu.on("show", onShow);
1539            var g = menu.group;
1540            if(g && menu.events["checkchange"]){
1541                if(!groups[g]){
1542                    groups[g] = [];
1543                }
1544                groups[g].push(menu);
1545                menu.on("checkchange", onCheck);
1546            }
1547        },
1548
1549         /**
1550          * Returns a {@link Roo.menu.Menu} object
1551          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1552          * be used to generate and return a new Menu instance.
1553          */
1554        get : function(menu){
1555            if(typeof menu == "string"){ // menu id
1556                return menus[menu];
1557            }else if(menu.events){  // menu instance
1558                return menu;
1559            }
1560            /*else if(typeof menu.length == 'number'){ // array of menu items?
1561                return new Roo.bootstrap.Menu({items:menu});
1562            }else{ // otherwise, must be a config
1563                return new Roo.bootstrap.Menu(menu);
1564            }
1565            */
1566            return false;
1567        },
1568
1569        // private
1570        unregister : function(menu){
1571            delete menus[menu.id];
1572            menu.un("beforehide", onBeforeHide);
1573            menu.un("hide", onHide);
1574            menu.un("beforeshow", onBeforeShow);
1575            menu.un("show", onShow);
1576            var g = menu.group;
1577            if(g && menu.events["checkchange"]){
1578                groups[g].remove(menu);
1579                menu.un("checkchange", onCheck);
1580            }
1581        },
1582
1583        // private
1584        registerCheckable : function(menuItem){
1585            var g = menuItem.group;
1586            if(g){
1587                if(!groups[g]){
1588                    groups[g] = [];
1589                }
1590                groups[g].push(menuItem);
1591                menuItem.on("beforecheckchange", onBeforeCheck);
1592            }
1593        },
1594
1595        // private
1596        unregisterCheckable : function(menuItem){
1597            var g = menuItem.group;
1598            if(g){
1599                groups[g].remove(menuItem);
1600                menuItem.un("beforecheckchange", onBeforeCheck);
1601            }
1602        }
1603    };
1604 }();/*
1605  * - LGPL
1606  *
1607  * menu
1608  * 
1609  */
1610
1611 /**
1612  * @class Roo.bootstrap.Menu
1613  * @extends Roo.bootstrap.Component
1614  * Bootstrap Menu class - container for MenuItems
1615  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1616  * 
1617  * @constructor
1618  * Create a new Menu
1619  * @param {Object} config The config object
1620  */
1621
1622
1623 Roo.bootstrap.Menu = function(config){
1624     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1625     if (this.registerMenu) {
1626         Roo.bootstrap.MenuMgr.register(this);
1627     }
1628     this.addEvents({
1629         /**
1630          * @event beforeshow
1631          * Fires before this menu is displayed
1632          * @param {Roo.menu.Menu} this
1633          */
1634         beforeshow : true,
1635         /**
1636          * @event beforehide
1637          * Fires before this menu is hidden
1638          * @param {Roo.menu.Menu} this
1639          */
1640         beforehide : true,
1641         /**
1642          * @event show
1643          * Fires after this menu is displayed
1644          * @param {Roo.menu.Menu} this
1645          */
1646         show : true,
1647         /**
1648          * @event hide
1649          * Fires after this menu is hidden
1650          * @param {Roo.menu.Menu} this
1651          */
1652         hide : true,
1653         /**
1654          * @event click
1655          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1656          * @param {Roo.menu.Menu} this
1657          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1658          * @param {Roo.EventObject} e
1659          */
1660         click : true,
1661         /**
1662          * @event mouseover
1663          * Fires when the mouse is hovering over this menu
1664          * @param {Roo.menu.Menu} this
1665          * @param {Roo.EventObject} e
1666          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1667          */
1668         mouseover : true,
1669         /**
1670          * @event mouseout
1671          * Fires when the mouse exits this menu
1672          * @param {Roo.menu.Menu} this
1673          * @param {Roo.EventObject} e
1674          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1675          */
1676         mouseout : true,
1677         /**
1678          * @event itemclick
1679          * Fires when a menu item contained in this menu is clicked
1680          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1681          * @param {Roo.EventObject} e
1682          */
1683         itemclick: true
1684     });
1685     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1686 };
1687
1688 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1689     
1690    /// html : false,
1691     //align : '',
1692     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1693     type: false,
1694     /**
1695      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1696      */
1697     registerMenu : true,
1698     
1699     menuItems :false, // stores the menu items..
1700     
1701     hidden:true,
1702     
1703     parentMenu : false,
1704     
1705     getChildContainer : function() {
1706         return this.el;  
1707     },
1708     
1709     getAutoCreate : function(){
1710          
1711         //if (['right'].indexOf(this.align)!==-1) {
1712         //    cfg.cn[1].cls += ' pull-right'
1713         //}
1714         
1715         
1716         var cfg = {
1717             tag : 'ul',
1718             cls : 'dropdown-menu' ,
1719             style : 'z-index:1000'
1720             
1721         }
1722         
1723         if (this.type === 'submenu') {
1724             cfg.cls = 'submenu active';
1725         }
1726         if (this.type === 'treeview') {
1727             cfg.cls = 'treeview-menu';
1728         }
1729         
1730         return cfg;
1731     },
1732     initEvents : function() {
1733         
1734        // Roo.log("ADD event");
1735        // Roo.log(this.triggerEl.dom);
1736         this.triggerEl.on('click', this.onTriggerPress, this);
1737         this.triggerEl.addClass('dropdown-toggle');
1738         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1739
1740         this.el.on("mouseover", this.onMouseOver, this);
1741         this.el.on("mouseout", this.onMouseOut, this);
1742         
1743         
1744     },
1745     findTargetItem : function(e){
1746         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1747         if(!t){
1748             return false;
1749         }
1750         //Roo.log(t);         Roo.log(t.id);
1751         if(t && t.id){
1752             //Roo.log(this.menuitems);
1753             return this.menuitems.get(t.id);
1754             
1755             //return this.items.get(t.menuItemId);
1756         }
1757         
1758         return false;
1759     },
1760     onClick : function(e){
1761         Roo.log("menu.onClick");
1762         var t = this.findTargetItem(e);
1763         if(!t || t.isContainer){
1764             return;
1765         }
1766         Roo.log(e);
1767         /*
1768         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1769             if(t == this.activeItem && t.shouldDeactivate(e)){
1770                 this.activeItem.deactivate();
1771                 delete this.activeItem;
1772                 return;
1773             }
1774             if(t.canActivate){
1775                 this.setActiveItem(t, true);
1776             }
1777             return;
1778             
1779             
1780         }
1781         */
1782        
1783         Roo.log('pass click event');
1784         
1785         t.onClick(e);
1786         
1787         this.fireEvent("click", this, t, e);
1788         
1789         this.hide();
1790     },
1791      onMouseOver : function(e){
1792         var t  = this.findTargetItem(e);
1793         //Roo.log(t);
1794         //if(t){
1795         //    if(t.canActivate && !t.disabled){
1796         //        this.setActiveItem(t, true);
1797         //    }
1798         //}
1799         
1800         this.fireEvent("mouseover", this, e, t);
1801     },
1802     isVisible : function(){
1803         return !this.hidden;
1804     },
1805      onMouseOut : function(e){
1806         var t  = this.findTargetItem(e);
1807         
1808         //if(t ){
1809         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1810         //        this.activeItem.deactivate();
1811         //        delete this.activeItem;
1812         //    }
1813         //}
1814         this.fireEvent("mouseout", this, e, t);
1815     },
1816     
1817     
1818     /**
1819      * Displays this menu relative to another element
1820      * @param {String/HTMLElement/Roo.Element} element The element to align to
1821      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1822      * the element (defaults to this.defaultAlign)
1823      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1824      */
1825     show : function(el, pos, parentMenu){
1826         this.parentMenu = parentMenu;
1827         if(!this.el){
1828             this.render();
1829         }
1830         this.fireEvent("beforeshow", this);
1831         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1832     },
1833      /**
1834      * Displays this menu at a specific xy position
1835      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1836      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1837      */
1838     showAt : function(xy, parentMenu, /* private: */_e){
1839         this.parentMenu = parentMenu;
1840         if(!this.el){
1841             this.render();
1842         }
1843         if(_e !== false){
1844             this.fireEvent("beforeshow", this);
1845             //xy = this.el.adjustForConstraints(xy);
1846         }
1847         
1848         //this.el.show();
1849         this.hideMenuItems();
1850         this.hidden = false;
1851         this.triggerEl.addClass('open');
1852         
1853         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
1854             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
1855         }
1856         
1857         this.el.setXY(xy);
1858         this.focus();
1859         this.fireEvent("show", this);
1860     },
1861     
1862     focus : function(){
1863         return;
1864         if(!this.hidden){
1865             this.doFocus.defer(50, this);
1866         }
1867     },
1868
1869     doFocus : function(){
1870         if(!this.hidden){
1871             this.focusEl.focus();
1872         }
1873     },
1874
1875     /**
1876      * Hides this menu and optionally all parent menus
1877      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1878      */
1879     hide : function(deep){
1880         
1881         this.hideMenuItems();
1882         if(this.el && this.isVisible()){
1883             this.fireEvent("beforehide", this);
1884             if(this.activeItem){
1885                 this.activeItem.deactivate();
1886                 this.activeItem = null;
1887             }
1888             this.triggerEl.removeClass('open');;
1889             this.hidden = true;
1890             this.fireEvent("hide", this);
1891         }
1892         if(deep === true && this.parentMenu){
1893             this.parentMenu.hide(true);
1894         }
1895     },
1896     
1897     onTriggerPress  : function(e)
1898     {
1899         
1900         Roo.log('trigger press');
1901         //Roo.log(e.getTarget());
1902        // Roo.log(this.triggerEl.dom);
1903         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1904             return;
1905         }
1906         if (this.isVisible()) {
1907             Roo.log('hide');
1908             this.hide();
1909         } else {
1910             this.show(this.triggerEl, false, false);
1911         }
1912         
1913         
1914     },
1915     
1916          
1917        
1918     
1919     hideMenuItems : function()
1920     {
1921         //$(backdrop).remove()
1922         Roo.select('.open',true).each(function(aa) {
1923             
1924             aa.removeClass('open');
1925           //var parent = getParent($(this))
1926           //var relatedTarget = { relatedTarget: this }
1927           
1928            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1929           //if (e.isDefaultPrevented()) return
1930            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1931         })
1932     },
1933     addxtypeChild : function (tree, cntr) {
1934         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1935           
1936         this.menuitems.add(comp);
1937         return comp;
1938
1939     },
1940     getEl : function()
1941     {
1942         Roo.log(this.el);
1943         return this.el;
1944     }
1945 });
1946
1947  
1948  /*
1949  * - LGPL
1950  *
1951  * menu item
1952  * 
1953  */
1954
1955
1956 /**
1957  * @class Roo.bootstrap.MenuItem
1958  * @extends Roo.bootstrap.Component
1959  * Bootstrap MenuItem class
1960  * @cfg {String} html the menu label
1961  * @cfg {String} href the link
1962  * @cfg {Boolean} preventDefault (true | false) default true
1963  * @cfg {Boolean} isContainer (true | false) default false
1964  * 
1965  * 
1966  * @constructor
1967  * Create a new MenuItem
1968  * @param {Object} config The config object
1969  */
1970
1971
1972 Roo.bootstrap.MenuItem = function(config){
1973     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1974     this.addEvents({
1975         // raw events
1976         /**
1977          * @event click
1978          * The raw click event for the entire grid.
1979          * @param {Roo.bootstrap.MenuItem} this
1980          * @param {Roo.EventObject} e
1981          */
1982         "click" : true
1983     });
1984 };
1985
1986 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1987     
1988     href : false,
1989     html : false,
1990     preventDefault: true,
1991     isContainer : false,
1992     
1993     getAutoCreate : function(){
1994         
1995         if(this.isContainer){
1996             return {
1997                 tag: 'li',
1998                 cls: 'dropdown-menu-item'
1999             };
2000         }
2001         
2002         var cfg= {
2003             tag: 'li',
2004             cls: 'dropdown-menu-item',
2005             cn: [
2006                     {
2007                         tag : 'a',
2008                         href : '#',
2009                         html : 'Link'
2010                     }
2011                 ]
2012         };
2013         if (this.parent().type == 'treeview') {
2014             cfg.cls = 'treeview-menu';
2015         }
2016         
2017         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2018         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2019         return cfg;
2020     },
2021     
2022     initEvents: function() {
2023         
2024         //this.el.select('a').on('click', this.onClick, this);
2025         
2026     },
2027     onClick : function(e)
2028     {
2029         Roo.log('item on click ');
2030         //if(this.preventDefault){
2031         //    e.preventDefault();
2032         //}
2033         //this.parent().hideMenuItems();
2034         
2035         this.fireEvent('click', this, e);
2036     },
2037     getEl : function()
2038     {
2039         return this.el;
2040     }
2041 });
2042
2043  
2044
2045  /*
2046  * - LGPL
2047  *
2048  * menu separator
2049  * 
2050  */
2051
2052
2053 /**
2054  * @class Roo.bootstrap.MenuSeparator
2055  * @extends Roo.bootstrap.Component
2056  * Bootstrap MenuSeparator class
2057  * 
2058  * @constructor
2059  * Create a new MenuItem
2060  * @param {Object} config The config object
2061  */
2062
2063
2064 Roo.bootstrap.MenuSeparator = function(config){
2065     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2066 };
2067
2068 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2069     
2070     getAutoCreate : function(){
2071         var cfg = {
2072             cls: 'divider',
2073             tag : 'li'
2074         };
2075         
2076         return cfg;
2077     }
2078    
2079 });
2080
2081  
2082
2083  
2084 /*
2085 * Licence: LGPL
2086 */
2087
2088 /**
2089  * @class Roo.bootstrap.Modal
2090  * @extends Roo.bootstrap.Component
2091  * Bootstrap Modal class
2092  * @cfg {String} title Title of dialog
2093  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2094  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2095  * @cfg {Boolean} specificTitle default false
2096  * @cfg {Array} buttons Array of buttons or standard button set..
2097  * @cfg {String} buttonPosition (left|right|center) default right
2098  * @cfg {Boolean} animate default true
2099  * @cfg {Boolean} allow_close default true
2100  * 
2101  * @constructor
2102  * Create a new Modal Dialog
2103  * @param {Object} config The config object
2104  */
2105
2106 Roo.bootstrap.Modal = function(config){
2107     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2108     this.addEvents({
2109         // raw events
2110         /**
2111          * @event btnclick
2112          * The raw btnclick event for the button
2113          * @param {Roo.EventObject} e
2114          */
2115         "btnclick" : true
2116     });
2117     this.buttons = this.buttons || [];
2118      
2119     if (this.tmpl) {
2120         this.tmpl = Roo.factory(this.tmpl);
2121     }
2122     
2123 };
2124
2125 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2126     
2127     title : 'test dialog',
2128    
2129     buttons : false,
2130     
2131     // set on load...
2132      
2133     html: false,
2134     
2135     tmp: false,
2136     
2137     specificTitle: false,
2138     
2139     buttonPosition: 'right',
2140     
2141     allow_close : true,
2142     
2143     animate : true,
2144     
2145     
2146      // private
2147     bodyEl:  false,
2148     footerEl:  false,
2149     titleEl:  false,
2150     closeEl:  false,
2151     
2152     
2153     onRender : function(ct, position)
2154     {
2155         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2156      
2157         if(!this.el){
2158             var cfg = Roo.apply({},  this.getAutoCreate());
2159             cfg.id = Roo.id();
2160             //if(!cfg.name){
2161             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2162             //}
2163             //if (!cfg.name.length) {
2164             //    delete cfg.name;
2165            // }
2166             if (this.cls) {
2167                 cfg.cls += ' ' + this.cls;
2168             }
2169             if (this.style) {
2170                 cfg.style = this.style;
2171             }
2172             this.el = Roo.get(document.body).createChild(cfg, position);
2173         }
2174         //var type = this.el.dom.type;
2175         
2176         
2177         
2178         
2179         if(this.tabIndex !== undefined){
2180             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2181         }
2182         
2183         
2184         this.bodyEl = this.el.select('.modal-body',true).first();
2185         this.closeEl = this.el.select('.modal-header .close', true).first();
2186         this.footerEl = this.el.select('.modal-footer',true).first();
2187         this.titleEl = this.el.select('.modal-title',true).first();
2188         
2189         
2190          
2191         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2192         this.maskEl.enableDisplayMode("block");
2193         this.maskEl.hide();
2194         //this.el.addClass("x-dlg-modal");
2195     
2196         if (this.buttons.length) {
2197             Roo.each(this.buttons, function(bb) {
2198                 b = Roo.apply({}, bb);
2199                 b.xns = b.xns || Roo.bootstrap;
2200                 b.xtype = b.xtype || 'Button';
2201                 if (typeof(b.listeners) == 'undefined') {
2202                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2203                 }
2204                 
2205                 var btn = Roo.factory(b);
2206                 
2207                 btn.onRender(this.el.select('.modal-footer div').first());
2208                 
2209             },this);
2210         }
2211         // render the children.
2212         var nitems = [];
2213         
2214         if(typeof(this.items) != 'undefined'){
2215             var items = this.items;
2216             delete this.items;
2217
2218             for(var i =0;i < items.length;i++) {
2219                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2220             }
2221         }
2222         
2223         this.items = nitems;
2224         
2225         // where are these used - they used to be body/close/footer
2226         
2227        
2228         this.initEvents();
2229         //this.el.addClass([this.fieldClass, this.cls]);
2230         
2231     },
2232     getAutoCreate : function(){
2233         
2234         
2235         var bdy = {
2236                 cls : 'modal-body',
2237                 html : this.html || ''
2238         };
2239         
2240         var title = {
2241             tag: 'h4',
2242             cls : 'modal-title',
2243             html : this.title
2244         };
2245         
2246         if(this.specificTitle){
2247             title = this.title;
2248             
2249         };
2250         
2251         var header = [];
2252         if (this.allow_close) {
2253             header.push({
2254                 tag: 'button',
2255                 cls : 'close',
2256                 html : '&times'
2257             });
2258         }
2259         header.push(title);
2260         
2261         var modal = {
2262             cls: "modal",
2263             style : 'display: none',
2264             cn : [
2265                 {
2266                     cls: "modal-dialog",
2267                     cn : [
2268                         {
2269                             cls : "modal-content",
2270                             cn : [
2271                                 {
2272                                     cls : 'modal-header',
2273                                     cn : header
2274                                 },
2275                                 bdy,
2276                                 {
2277                                     cls : 'modal-footer',
2278                                     cn : [
2279                                         {
2280                                             tag: 'div',
2281                                             cls: 'btn-' + this.buttonPosition
2282                                         }
2283                                     ]
2284                                     
2285                                 }
2286                                 
2287                                 
2288                             ]
2289                             
2290                         }
2291                     ]
2292                         
2293                 }
2294             ]
2295         };
2296         
2297         if(this.animate){
2298             modal.cls += ' fade';
2299         }
2300         
2301         return modal;
2302           
2303     },
2304     getChildContainer : function() {
2305          
2306          return this.bodyEl;
2307         
2308     },
2309     getButtonContainer : function() {
2310          return this.el.select('.modal-footer div',true).first();
2311         
2312     },
2313     initEvents : function()
2314     {
2315         if (this.allow_close) {
2316             this.closeEl.on('click', this.hide, this);
2317         }
2318
2319     },
2320     show : function() {
2321         
2322         if (!this.rendered) {
2323             this.render();
2324         }
2325         
2326         this.el.setStyle('display', 'block');
2327         
2328         if(this.animate){
2329             var _this = this;
2330             (function(){ _this.el.addClass('in'); }).defer(50);
2331         }else{
2332             this.el.addClass('in');
2333         }
2334         
2335         // not sure how we can show data in here.. 
2336         //if (this.tmpl) {
2337         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2338         //}
2339         
2340         Roo.get(document.body).addClass("x-body-masked");
2341         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2342         this.maskEl.show();
2343         this.el.setStyle('zIndex', '10001');
2344        
2345         this.fireEvent('show', this);
2346         
2347         
2348     },
2349     hide : function()
2350     {
2351         this.maskEl.hide();
2352         Roo.get(document.body).removeClass("x-body-masked");
2353         this.el.removeClass('in');
2354         
2355         if(this.animate){
2356             var _this = this;
2357             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2358         }else{
2359             this.el.setStyle('display', 'none');
2360         }
2361         
2362         this.fireEvent('hide', this);
2363     },
2364     
2365     addButton : function(str, cb)
2366     {
2367          
2368         
2369         var b = Roo.apply({}, { html : str } );
2370         b.xns = b.xns || Roo.bootstrap;
2371         b.xtype = b.xtype || 'Button';
2372         if (typeof(b.listeners) == 'undefined') {
2373             b.listeners = { click : cb.createDelegate(this)  };
2374         }
2375         
2376         var btn = Roo.factory(b);
2377            
2378         btn.onRender(this.el.select('.modal-footer div').first());
2379         
2380         return btn;   
2381        
2382     },
2383     
2384     setDefaultButton : function(btn)
2385     {
2386         //this.el.select('.modal-footer').()
2387     },
2388     resizeTo: function(w,h)
2389     {
2390         // skip..
2391     },
2392     setContentSize  : function(w, h)
2393     {
2394         
2395     },
2396     onButtonClick: function(btn,e)
2397     {
2398         //Roo.log([a,b,c]);
2399         this.fireEvent('btnclick', btn.name, e);
2400     },
2401      /**
2402      * Set the title of the Dialog
2403      * @param {String} str new Title
2404      */
2405     setTitle: function(str) {
2406         this.titleEl.dom.innerHTML = str;    
2407     },
2408     /**
2409      * Set the body of the Dialog
2410      * @param {String} str new Title
2411      */
2412     setBody: function(str) {
2413         this.bodyEl.dom.innerHTML = str;    
2414     },
2415     /**
2416      * Set the body of the Dialog using the template
2417      * @param {Obj} data - apply this data to the template and replace the body contents.
2418      */
2419     applyBody: function(obj)
2420     {
2421         if (!this.tmpl) {
2422             Roo.log("Error - using apply Body without a template");
2423             //code
2424         }
2425         this.tmpl.overwrite(this.bodyEl, obj);
2426     }
2427     
2428 });
2429
2430
2431 Roo.apply(Roo.bootstrap.Modal,  {
2432     /**
2433          * Button config that displays a single OK button
2434          * @type Object
2435          */
2436         OK :  [{
2437             name : 'ok',
2438             weight : 'primary',
2439             html : 'OK'
2440         }], 
2441         /**
2442          * Button config that displays Yes and No buttons
2443          * @type Object
2444          */
2445         YESNO : [
2446             {
2447                 name  : 'no',
2448                 html : 'No'
2449             },
2450             {
2451                 name  :'yes',
2452                 weight : 'primary',
2453                 html : 'Yes'
2454             }
2455         ],
2456         
2457         /**
2458          * Button config that displays OK and Cancel buttons
2459          * @type Object
2460          */
2461         OKCANCEL : [
2462             {
2463                name : 'cancel',
2464                 html : 'Cancel'
2465             },
2466             {
2467                 name : 'ok',
2468                 weight : 'primary',
2469                 html : 'OK'
2470             }
2471         ],
2472         /**
2473          * Button config that displays Yes, No and Cancel buttons
2474          * @type Object
2475          */
2476         YESNOCANCEL : [
2477             {
2478                 name : 'yes',
2479                 weight : 'primary',
2480                 html : 'Yes'
2481             },
2482             {
2483                 name : 'no',
2484                 html : 'No'
2485             },
2486             {
2487                 name : 'cancel',
2488                 html : 'Cancel'
2489             }
2490         ]
2491 });
2492  
2493  /*
2494  * - LGPL
2495  *
2496  * messagebox - can be used as a replace
2497  * 
2498  */
2499 /**
2500  * @class Roo.MessageBox
2501  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2502  * Example usage:
2503  *<pre><code>
2504 // Basic alert:
2505 Roo.Msg.alert('Status', 'Changes saved successfully.');
2506
2507 // Prompt for user data:
2508 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2509     if (btn == 'ok'){
2510         // process text value...
2511     }
2512 });
2513
2514 // Show a dialog using config options:
2515 Roo.Msg.show({
2516    title:'Save Changes?',
2517    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2518    buttons: Roo.Msg.YESNOCANCEL,
2519    fn: processResult,
2520    animEl: 'elId'
2521 });
2522 </code></pre>
2523  * @singleton
2524  */
2525 Roo.bootstrap.MessageBox = function(){
2526     var dlg, opt, mask, waitTimer;
2527     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2528     var buttons, activeTextEl, bwidth;
2529
2530     
2531     // private
2532     var handleButton = function(button){
2533         dlg.hide();
2534         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2535     };
2536
2537     // private
2538     var handleHide = function(){
2539         if(opt && opt.cls){
2540             dlg.el.removeClass(opt.cls);
2541         }
2542         //if(waitTimer){
2543         //    Roo.TaskMgr.stop(waitTimer);
2544         //    waitTimer = null;
2545         //}
2546     };
2547
2548     // private
2549     var updateButtons = function(b){
2550         var width = 0;
2551         if(!b){
2552             buttons["ok"].hide();
2553             buttons["cancel"].hide();
2554             buttons["yes"].hide();
2555             buttons["no"].hide();
2556             //dlg.footer.dom.style.display = 'none';
2557             return width;
2558         }
2559         dlg.footerEl.dom.style.display = '';
2560         for(var k in buttons){
2561             if(typeof buttons[k] != "function"){
2562                 if(b[k]){
2563                     buttons[k].show();
2564                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2565                     width += buttons[k].el.getWidth()+15;
2566                 }else{
2567                     buttons[k].hide();
2568                 }
2569             }
2570         }
2571         return width;
2572     };
2573
2574     // private
2575     var handleEsc = function(d, k, e){
2576         if(opt && opt.closable !== false){
2577             dlg.hide();
2578         }
2579         if(e){
2580             e.stopEvent();
2581         }
2582     };
2583
2584     return {
2585         /**
2586          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2587          * @return {Roo.BasicDialog} The BasicDialog element
2588          */
2589         getDialog : function(){
2590            if(!dlg){
2591                 dlg = new Roo.bootstrap.Modal( {
2592                     //draggable: true,
2593                     //resizable:false,
2594                     //constraintoviewport:false,
2595                     //fixedcenter:true,
2596                     //collapsible : false,
2597                     //shim:true,
2598                     //modal: true,
2599                   //  width:400,
2600                   //  height:100,
2601                     //buttonAlign:"center",
2602                     closeClick : function(){
2603                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2604                             handleButton("no");
2605                         }else{
2606                             handleButton("cancel");
2607                         }
2608                     }
2609                 });
2610                 dlg.render();
2611                 dlg.on("hide", handleHide);
2612                 mask = dlg.mask;
2613                 //dlg.addKeyListener(27, handleEsc);
2614                 buttons = {};
2615                 this.buttons = buttons;
2616                 var bt = this.buttonText;
2617                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2618                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2619                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2620                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2621                 Roo.log(buttons)
2622                 bodyEl = dlg.bodyEl.createChild({
2623
2624                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2625                         '<textarea class="roo-mb-textarea"></textarea>' +
2626                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2627                 });
2628                 msgEl = bodyEl.dom.firstChild;
2629                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2630                 textboxEl.enableDisplayMode();
2631                 textboxEl.addKeyListener([10,13], function(){
2632                     if(dlg.isVisible() && opt && opt.buttons){
2633                         if(opt.buttons.ok){
2634                             handleButton("ok");
2635                         }else if(opt.buttons.yes){
2636                             handleButton("yes");
2637                         }
2638                     }
2639                 });
2640                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2641                 textareaEl.enableDisplayMode();
2642                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2643                 progressEl.enableDisplayMode();
2644                 var pf = progressEl.dom.firstChild;
2645                 if (pf) {
2646                     pp = Roo.get(pf.firstChild);
2647                     pp.setHeight(pf.offsetHeight);
2648                 }
2649                 
2650             }
2651             return dlg;
2652         },
2653
2654         /**
2655          * Updates the message box body text
2656          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2657          * the XHTML-compliant non-breaking space character '&amp;#160;')
2658          * @return {Roo.MessageBox} This message box
2659          */
2660         updateText : function(text){
2661             if(!dlg.isVisible() && !opt.width){
2662                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2663             }
2664             msgEl.innerHTML = text || '&#160;';
2665       
2666             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2667             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2668             var w = Math.max(
2669                     Math.min(opt.width || cw , this.maxWidth), 
2670                     Math.max(opt.minWidth || this.minWidth, bwidth)
2671             );
2672             if(opt.prompt){
2673                 activeTextEl.setWidth(w);
2674             }
2675             if(dlg.isVisible()){
2676                 dlg.fixedcenter = false;
2677             }
2678             // to big, make it scroll. = But as usual stupid IE does not support
2679             // !important..
2680             
2681             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2682                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2683                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2684             } else {
2685                 bodyEl.dom.style.height = '';
2686                 bodyEl.dom.style.overflowY = '';
2687             }
2688             if (cw > w) {
2689                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2690             } else {
2691                 bodyEl.dom.style.overflowX = '';
2692             }
2693             
2694             dlg.setContentSize(w, bodyEl.getHeight());
2695             if(dlg.isVisible()){
2696                 dlg.fixedcenter = true;
2697             }
2698             return this;
2699         },
2700
2701         /**
2702          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2703          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2704          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2705          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2706          * @return {Roo.MessageBox} This message box
2707          */
2708         updateProgress : function(value, text){
2709             if(text){
2710                 this.updateText(text);
2711             }
2712             if (pp) { // weird bug on my firefox - for some reason this is not defined
2713                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2714             }
2715             return this;
2716         },        
2717
2718         /**
2719          * Returns true if the message box is currently displayed
2720          * @return {Boolean} True if the message box is visible, else false
2721          */
2722         isVisible : function(){
2723             return dlg && dlg.isVisible();  
2724         },
2725
2726         /**
2727          * Hides the message box if it is displayed
2728          */
2729         hide : function(){
2730             if(this.isVisible()){
2731                 dlg.hide();
2732             }  
2733         },
2734
2735         /**
2736          * Displays a new message box, or reinitializes an existing message box, based on the config options
2737          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2738          * The following config object properties are supported:
2739          * <pre>
2740 Property    Type             Description
2741 ----------  ---------------  ------------------------------------------------------------------------------------
2742 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2743                                    closes (defaults to undefined)
2744 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2745                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2746 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2747                                    progress and wait dialogs will ignore this property and always hide the
2748                                    close button as they can only be closed programmatically.
2749 cls               String           A custom CSS class to apply to the message box element
2750 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2751                                    displayed (defaults to 75)
2752 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2753                                    function will be btn (the name of the button that was clicked, if applicable,
2754                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2755                                    Progress and wait dialogs will ignore this option since they do not respond to
2756                                    user actions and can only be closed programmatically, so any required function
2757                                    should be called by the same code after it closes the dialog.
2758 icon              String           A CSS class that provides a background image to be used as an icon for
2759                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2760 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2761 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2762 modal             Boolean          False to allow user interaction with the page while the message box is
2763                                    displayed (defaults to true)
2764 msg               String           A string that will replace the existing message box body text (defaults
2765                                    to the XHTML-compliant non-breaking space character '&#160;')
2766 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2767 progress          Boolean          True to display a progress bar (defaults to false)
2768 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2769 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2770 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2771 title             String           The title text
2772 value             String           The string value to set into the active textbox element if displayed
2773 wait              Boolean          True to display a progress bar (defaults to false)
2774 width             Number           The width of the dialog in pixels
2775 </pre>
2776          *
2777          * Example usage:
2778          * <pre><code>
2779 Roo.Msg.show({
2780    title: 'Address',
2781    msg: 'Please enter your address:',
2782    width: 300,
2783    buttons: Roo.MessageBox.OKCANCEL,
2784    multiline: true,
2785    fn: saveAddress,
2786    animEl: 'addAddressBtn'
2787 });
2788 </code></pre>
2789          * @param {Object} config Configuration options
2790          * @return {Roo.MessageBox} This message box
2791          */
2792         show : function(options)
2793         {
2794             
2795             // this causes nightmares if you show one dialog after another
2796             // especially on callbacks..
2797              
2798             if(this.isVisible()){
2799                 
2800                 this.hide();
2801                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2802                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2803                 Roo.log("New Dialog Message:" +  options.msg )
2804                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2805                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2806                 
2807             }
2808             var d = this.getDialog();
2809             opt = options;
2810             d.setTitle(opt.title || "&#160;");
2811             d.closeEl.setDisplayed(opt.closable !== false);
2812             activeTextEl = textboxEl;
2813             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2814             if(opt.prompt){
2815                 if(opt.multiline){
2816                     textboxEl.hide();
2817                     textareaEl.show();
2818                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2819                         opt.multiline : this.defaultTextHeight);
2820                     activeTextEl = textareaEl;
2821                 }else{
2822                     textboxEl.show();
2823                     textareaEl.hide();
2824                 }
2825             }else{
2826                 textboxEl.hide();
2827                 textareaEl.hide();
2828             }
2829             progressEl.setDisplayed(opt.progress === true);
2830             this.updateProgress(0);
2831             activeTextEl.dom.value = opt.value || "";
2832             if(opt.prompt){
2833                 dlg.setDefaultButton(activeTextEl);
2834             }else{
2835                 var bs = opt.buttons;
2836                 var db = null;
2837                 if(bs && bs.ok){
2838                     db = buttons["ok"];
2839                 }else if(bs && bs.yes){
2840                     db = buttons["yes"];
2841                 }
2842                 dlg.setDefaultButton(db);
2843             }
2844             bwidth = updateButtons(opt.buttons);
2845             this.updateText(opt.msg);
2846             if(opt.cls){
2847                 d.el.addClass(opt.cls);
2848             }
2849             d.proxyDrag = opt.proxyDrag === true;
2850             d.modal = opt.modal !== false;
2851             d.mask = opt.modal !== false ? mask : false;
2852             if(!d.isVisible()){
2853                 // force it to the end of the z-index stack so it gets a cursor in FF
2854                 document.body.appendChild(dlg.el.dom);
2855                 d.animateTarget = null;
2856                 d.show(options.animEl);
2857             }
2858             return this;
2859         },
2860
2861         /**
2862          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2863          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2864          * and closing the message box when the process is complete.
2865          * @param {String} title The title bar text
2866          * @param {String} msg The message box body text
2867          * @return {Roo.MessageBox} This message box
2868          */
2869         progress : function(title, msg){
2870             this.show({
2871                 title : title,
2872                 msg : msg,
2873                 buttons: false,
2874                 progress:true,
2875                 closable:false,
2876                 minWidth: this.minProgressWidth,
2877                 modal : true
2878             });
2879             return this;
2880         },
2881
2882         /**
2883          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2884          * If a callback function is passed it will be called after the user clicks the button, and the
2885          * id of the button that was clicked will be passed as the only parameter to the callback
2886          * (could also be the top-right close button).
2887          * @param {String} title The title bar text
2888          * @param {String} msg The message box body text
2889          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2890          * @param {Object} scope (optional) The scope of the callback function
2891          * @return {Roo.MessageBox} This message box
2892          */
2893         alert : function(title, msg, fn, scope){
2894             this.show({
2895                 title : title,
2896                 msg : msg,
2897                 buttons: this.OK,
2898                 fn: fn,
2899                 scope : scope,
2900                 modal : true
2901             });
2902             return this;
2903         },
2904
2905         /**
2906          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2907          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2908          * You are responsible for closing the message box when the process is complete.
2909          * @param {String} msg The message box body text
2910          * @param {String} title (optional) The title bar text
2911          * @return {Roo.MessageBox} This message box
2912          */
2913         wait : function(msg, title){
2914             this.show({
2915                 title : title,
2916                 msg : msg,
2917                 buttons: false,
2918                 closable:false,
2919                 progress:true,
2920                 modal:true,
2921                 width:300,
2922                 wait:true
2923             });
2924             waitTimer = Roo.TaskMgr.start({
2925                 run: function(i){
2926                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2927                 },
2928                 interval: 1000
2929             });
2930             return this;
2931         },
2932
2933         /**
2934          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2935          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2936          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2937          * @param {String} title The title bar text
2938          * @param {String} msg The message box body text
2939          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2940          * @param {Object} scope (optional) The scope of the callback function
2941          * @return {Roo.MessageBox} This message box
2942          */
2943         confirm : function(title, msg, fn, scope){
2944             this.show({
2945                 title : title,
2946                 msg : msg,
2947                 buttons: this.YESNO,
2948                 fn: fn,
2949                 scope : scope,
2950                 modal : true
2951             });
2952             return this;
2953         },
2954
2955         /**
2956          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2957          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2958          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2959          * (could also be the top-right close button) and the text that was entered will be passed as the two
2960          * parameters to the callback.
2961          * @param {String} title The title bar text
2962          * @param {String} msg The message box body text
2963          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2964          * @param {Object} scope (optional) The scope of the callback function
2965          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2966          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2967          * @return {Roo.MessageBox} This message box
2968          */
2969         prompt : function(title, msg, fn, scope, multiline){
2970             this.show({
2971                 title : title,
2972                 msg : msg,
2973                 buttons: this.OKCANCEL,
2974                 fn: fn,
2975                 minWidth:250,
2976                 scope : scope,
2977                 prompt:true,
2978                 multiline: multiline,
2979                 modal : true
2980             });
2981             return this;
2982         },
2983
2984         /**
2985          * Button config that displays a single OK button
2986          * @type Object
2987          */
2988         OK : {ok:true},
2989         /**
2990          * Button config that displays Yes and No buttons
2991          * @type Object
2992          */
2993         YESNO : {yes:true, no:true},
2994         /**
2995          * Button config that displays OK and Cancel buttons
2996          * @type Object
2997          */
2998         OKCANCEL : {ok:true, cancel:true},
2999         /**
3000          * Button config that displays Yes, No and Cancel buttons
3001          * @type Object
3002          */
3003         YESNOCANCEL : {yes:true, no:true, cancel:true},
3004
3005         /**
3006          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3007          * @type Number
3008          */
3009         defaultTextHeight : 75,
3010         /**
3011          * The maximum width in pixels of the message box (defaults to 600)
3012          * @type Number
3013          */
3014         maxWidth : 600,
3015         /**
3016          * The minimum width in pixels of the message box (defaults to 100)
3017          * @type Number
3018          */
3019         minWidth : 100,
3020         /**
3021          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3022          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3023          * @type Number
3024          */
3025         minProgressWidth : 250,
3026         /**
3027          * An object containing the default button text strings that can be overriden for localized language support.
3028          * Supported properties are: ok, cancel, yes and no.
3029          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3030          * @type Object
3031          */
3032         buttonText : {
3033             ok : "OK",
3034             cancel : "Cancel",
3035             yes : "Yes",
3036             no : "No"
3037         }
3038     };
3039 }();
3040
3041 /**
3042  * Shorthand for {@link Roo.MessageBox}
3043  */
3044 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3045 Roo.Msg = Roo.Msg || Roo.MessageBox;
3046 /*
3047  * - LGPL
3048  *
3049  * navbar
3050  * 
3051  */
3052
3053 /**
3054  * @class Roo.bootstrap.Navbar
3055  * @extends Roo.bootstrap.Component
3056  * Bootstrap Navbar class
3057
3058  * @constructor
3059  * Create a new Navbar
3060  * @param {Object} config The config object
3061  */
3062
3063
3064 Roo.bootstrap.Navbar = function(config){
3065     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3066     
3067 };
3068
3069 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3070     
3071     
3072    
3073     // private
3074     navItems : false,
3075     loadMask : false,
3076     
3077     
3078     getAutoCreate : function(){
3079         
3080         
3081         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3082         
3083     },
3084     
3085     initEvents :function ()
3086     {
3087         //Roo.log(this.el.select('.navbar-toggle',true));
3088         this.el.select('.navbar-toggle',true).on('click', function() {
3089            // Roo.log('click');
3090             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3091         }, this);
3092         
3093         var mark = {
3094             tag: "div",
3095             cls:"x-dlg-mask"
3096         }
3097         
3098         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3099         
3100         var size = this.el.getSize();
3101         this.maskEl.setSize(size.width, size.height);
3102         this.maskEl.enableDisplayMode("block");
3103         this.maskEl.hide();
3104         
3105         if(this.loadMask){
3106             this.maskEl.show();
3107         }
3108     },
3109     
3110     
3111     getChildContainer : function()
3112     {
3113         if (this.el.select('.collapse').getCount()) {
3114             return this.el.select('.collapse',true).first();
3115         }
3116         
3117         return this.el;
3118     },
3119     
3120     mask : function()
3121     {
3122         this.maskEl.show();
3123     },
3124     
3125     unmask : function()
3126     {
3127         this.maskEl.hide();
3128     } 
3129     
3130     
3131     
3132     
3133 });
3134
3135
3136
3137  
3138
3139  /*
3140  * - LGPL
3141  *
3142  * navbar
3143  * 
3144  */
3145
3146 /**
3147  * @class Roo.bootstrap.NavSimplebar
3148  * @extends Roo.bootstrap.Navbar
3149  * Bootstrap Sidebar class
3150  *
3151  * @cfg {Boolean} inverse is inverted color
3152  * 
3153  * @cfg {String} type (nav | pills | tabs)
3154  * @cfg {Boolean} arrangement stacked | justified
3155  * @cfg {String} align (left | right) alignment
3156  * 
3157  * @cfg {Boolean} main (true|false) main nav bar? default false
3158  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3159  * 
3160  * @cfg {String} tag (header|footer|nav|div) default is nav 
3161
3162  * 
3163  * 
3164  * 
3165  * @constructor
3166  * Create a new Sidebar
3167  * @param {Object} config The config object
3168  */
3169
3170
3171 Roo.bootstrap.NavSimplebar = function(config){
3172     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3173 };
3174
3175 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3176     
3177     inverse: false,
3178     
3179     type: false,
3180     arrangement: '',
3181     align : false,
3182     
3183     
3184     
3185     main : false,
3186     
3187     
3188     tag : false,
3189     
3190     
3191     getAutoCreate : function(){
3192         
3193         
3194         var cfg = {
3195             tag : this.tag || 'div',
3196             cls : 'navbar'
3197         };
3198           
3199         
3200         cfg.cn = [
3201             {
3202                 cls: 'nav',
3203                 tag : 'ul'
3204             }
3205         ];
3206         
3207          
3208         this.type = this.type || 'nav';
3209         if (['tabs','pills'].indexOf(this.type)!==-1) {
3210             cfg.cn[0].cls += ' nav-' + this.type
3211         
3212         
3213         } else {
3214             if (this.type!=='nav') {
3215                 Roo.log('nav type must be nav/tabs/pills')
3216             }
3217             cfg.cn[0].cls += ' navbar-nav'
3218         }
3219         
3220         
3221         
3222         
3223         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3224             cfg.cn[0].cls += ' nav-' + this.arrangement;
3225         }
3226         
3227         
3228         if (this.align === 'right') {
3229             cfg.cn[0].cls += ' navbar-right';
3230         }
3231         
3232         if (this.inverse) {
3233             cfg.cls += ' navbar-inverse';
3234             
3235         }
3236         
3237         
3238         return cfg;
3239     
3240         
3241     }
3242     
3243     
3244     
3245 });
3246
3247
3248
3249  
3250
3251  
3252        /*
3253  * - LGPL
3254  *
3255  * navbar
3256  * 
3257  */
3258
3259 /**
3260  * @class Roo.bootstrap.NavHeaderbar
3261  * @extends Roo.bootstrap.NavSimplebar
3262  * Bootstrap Sidebar class
3263  *
3264  * @cfg {String} brand what is brand
3265  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3266  * @cfg {String} brand_href href of the brand
3267  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3268  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3269  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3270  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3271  * 
3272  * @constructor
3273  * Create a new Sidebar
3274  * @param {Object} config The config object
3275  */
3276
3277
3278 Roo.bootstrap.NavHeaderbar = function(config){
3279     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3280       
3281 };
3282
3283 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3284     
3285     position: '',
3286     brand: '',
3287     brand_href: false,
3288     srButton : true,
3289     autohide : false,
3290     desktopCenter : false,
3291    
3292     
3293     getAutoCreate : function(){
3294         
3295         var   cfg = {
3296             tag: this.nav || 'nav',
3297             cls: 'navbar',
3298             role: 'navigation',
3299             cn: []
3300         };
3301         
3302         var cn = cfg.cn;
3303         if (this.desktopCenter) {
3304             cn.push({cls : 'container', cn : []});
3305             cn = cn[0].cn;
3306         }
3307         
3308         if(this.srButton){
3309             cn.push({
3310                 tag: 'div',
3311                 cls: 'navbar-header',
3312                 cn: [
3313                     {
3314                         tag: 'button',
3315                         type: 'button',
3316                         cls: 'navbar-toggle',
3317                         'data-toggle': 'collapse',
3318                         cn: [
3319                             {
3320                                 tag: 'span',
3321                                 cls: 'sr-only',
3322                                 html: 'Toggle navigation'
3323                             },
3324                             {
3325                                 tag: 'span',
3326                                 cls: 'icon-bar'
3327                             },
3328                             {
3329                                 tag: 'span',
3330                                 cls: 'icon-bar'
3331                             },
3332                             {
3333                                 tag: 'span',
3334                                 cls: 'icon-bar'
3335                             }
3336                         ]
3337                     }
3338                 ]
3339             });
3340         }
3341         
3342         cn.push({
3343             tag: 'div',
3344             cls: 'collapse navbar-collapse',
3345             cn : []
3346         });
3347         
3348         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3349         
3350         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3351             cfg.cls += ' navbar-' + this.position;
3352             
3353             // tag can override this..
3354             
3355             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3356         }
3357         
3358         if (this.brand !== '') {
3359             cn[0].cn.push({
3360                 tag: 'a',
3361                 href: this.brand_href ? this.brand_href : '#',
3362                 cls: 'navbar-brand',
3363                 cn: [
3364                 this.brand
3365                 ]
3366             });
3367         }
3368         
3369         if(this.main){
3370             cfg.cls += ' main-nav';
3371         }
3372         
3373         
3374         return cfg;
3375
3376         
3377     },
3378     getHeaderChildContainer : function()
3379     {
3380         if (this.el.select('.navbar-header').getCount()) {
3381             return this.el.select('.navbar-header',true).first();
3382         }
3383         
3384         return this.getChildContainer();
3385     },
3386     
3387     
3388     initEvents : function()
3389     {
3390         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3391         
3392         if (this.autohide) {
3393             
3394             var prevScroll = 0;
3395             var ft = this.el;
3396             
3397             Roo.get(document).on('scroll',function(e) {
3398                 var ns = Roo.get(document).getScroll().top;
3399                 var os = prevScroll;
3400                 prevScroll = ns;
3401                 
3402                 if(ns > os){
3403                     ft.removeClass('slideDown');
3404                     ft.addClass('slideUp');
3405                     return;
3406                 }
3407                 ft.removeClass('slideUp');
3408                 ft.addClass('slideDown');
3409                  
3410               
3411           },this);
3412         }
3413     }    
3414           
3415       
3416     
3417     
3418 });
3419
3420
3421
3422  
3423
3424  /*
3425  * - LGPL
3426  *
3427  * navbar
3428  * 
3429  */
3430
3431 /**
3432  * @class Roo.bootstrap.NavSidebar
3433  * @extends Roo.bootstrap.Navbar
3434  * Bootstrap Sidebar class
3435  * 
3436  * @constructor
3437  * Create a new Sidebar
3438  * @param {Object} config The config object
3439  */
3440
3441
3442 Roo.bootstrap.NavSidebar = function(config){
3443     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3444 };
3445
3446 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3447     
3448     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3449     
3450     getAutoCreate : function(){
3451         
3452         
3453         return  {
3454             tag: 'div',
3455             cls: 'sidebar sidebar-nav'
3456         };
3457     
3458         
3459     }
3460     
3461     
3462     
3463 });
3464
3465
3466
3467  
3468
3469  /*
3470  * - LGPL
3471  *
3472  * nav group
3473  * 
3474  */
3475
3476 /**
3477  * @class Roo.bootstrap.NavGroup
3478  * @extends Roo.bootstrap.Component
3479  * Bootstrap NavGroup class
3480  * @cfg {String} align left | right
3481  * @cfg {Boolean} inverse false | true
3482  * @cfg {String} type (nav|pills|tab) default nav
3483  * @cfg {String} navId - reference Id for navbar.
3484
3485  * 
3486  * @constructor
3487  * Create a new nav group
3488  * @param {Object} config The config object
3489  */
3490
3491 Roo.bootstrap.NavGroup = function(config){
3492     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3493     this.navItems = [];
3494    
3495     Roo.bootstrap.NavGroup.register(this);
3496      this.addEvents({
3497         /**
3498              * @event changed
3499              * Fires when the active item changes
3500              * @param {Roo.bootstrap.NavGroup} this
3501              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3502              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3503          */
3504         'changed': true
3505      });
3506     
3507 };
3508
3509 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3510     
3511     align: '',
3512     inverse: false,
3513     form: false,
3514     type: 'nav',
3515     navId : '',
3516     // private
3517     
3518     navItems : false, 
3519     
3520     getAutoCreate : function()
3521     {
3522         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3523         
3524         cfg = {
3525             tag : 'ul',
3526             cls: 'nav' 
3527         }
3528         
3529         if (['tabs','pills'].indexOf(this.type)!==-1) {
3530             cfg.cls += ' nav-' + this.type
3531         } else {
3532             if (this.type!=='nav') {
3533                 Roo.log('nav type must be nav/tabs/pills')
3534             }
3535             cfg.cls += ' navbar-nav'
3536         }
3537         
3538         if (this.parent().sidebar) {
3539             cfg = {
3540                 tag: 'ul',
3541                 cls: 'dashboard-menu sidebar-menu'
3542             }
3543             
3544             return cfg;
3545         }
3546         
3547         if (this.form === true) {
3548             cfg = {
3549                 tag: 'form',
3550                 cls: 'navbar-form'
3551             }
3552             
3553             if (this.align === 'right') {
3554                 cfg.cls += ' navbar-right';
3555             } else {
3556                 cfg.cls += ' navbar-left';
3557             }
3558         }
3559         
3560         if (this.align === 'right') {
3561             cfg.cls += ' navbar-right';
3562         }
3563         
3564         if (this.inverse) {
3565             cfg.cls += ' navbar-inverse';
3566             
3567         }
3568         
3569         
3570         return cfg;
3571     },
3572     /**
3573     * sets the active Navigation item
3574     * @param {Roo.bootstrap.NavItem} the new current navitem
3575     */
3576     setActiveItem : function(item)
3577     {
3578         var prev = false;
3579         Roo.each(this.navItems, function(v){
3580             if (v == item) {
3581                 return ;
3582             }
3583             if (v.isActive()) {
3584                 v.setActive(false, true);
3585                 prev = v;
3586                 
3587             }
3588             
3589         });
3590
3591         item.setActive(true, true);
3592         this.fireEvent('changed', this, item, prev);
3593         
3594         
3595     },
3596     /**
3597     * gets the active Navigation item
3598     * @return {Roo.bootstrap.NavItem} the current navitem
3599     */
3600     getActive : function()
3601     {
3602         
3603         var prev = false;
3604         Roo.each(this.navItems, function(v){
3605             
3606             if (v.isActive()) {
3607                 prev = v;
3608                 
3609             }
3610             
3611         });
3612         return prev;
3613     },
3614     
3615     indexOfNav : function()
3616     {
3617         
3618         var prev = false;
3619         Roo.each(this.navItems, function(v,i){
3620             
3621             if (v.isActive()) {
3622                 prev = i;
3623                 
3624             }
3625             
3626         });
3627         return prev;
3628     },
3629     /**
3630     * adds a Navigation item
3631     * @param {Roo.bootstrap.NavItem} the navitem to add
3632     */
3633     addItem : function(cfg)
3634     {
3635         var cn = new Roo.bootstrap.NavItem(cfg);
3636         this.register(cn);
3637         cn.parentId = this.id;
3638         cn.onRender(this.el, null);
3639         return cn;
3640     },
3641     /**
3642     * register a Navigation item
3643     * @param {Roo.bootstrap.NavItem} the navitem to add
3644     */
3645     register : function(item)
3646     {
3647         this.navItems.push( item);
3648         item.navId = this.navId;
3649     
3650     },
3651     
3652     /**
3653     * clear all the Navigation item
3654     */
3655    
3656     clearAll : function()
3657     {
3658         this.navItems = [];
3659         this.el.dom.innerHTML = '';
3660     },
3661     
3662     getNavItem: function(tabId)
3663     {
3664         var ret = false;
3665         Roo.each(this.navItems, function(e) {
3666             if (e.tabId == tabId) {
3667                ret =  e;
3668                return false;
3669             }
3670             return true;
3671             
3672         });
3673         return ret;
3674     },
3675     
3676     setActiveNext : function()
3677     {
3678         var i = this.indexOfNav(this.getActive());
3679         if (i > this.navItems.length) {
3680             return;
3681         }
3682         this.setActiveItem(this.navItems[i+1]);
3683     },
3684     setActivePrev : function()
3685     {
3686         var i = this.indexOfNav(this.getActive());
3687         if (i  < 1) {
3688             return;
3689         }
3690         this.setActiveItem(this.navItems[i-1]);
3691     },
3692     clearWasActive : function(except) {
3693         Roo.each(this.navItems, function(e) {
3694             if (e.tabId != except.tabId && e.was_active) {
3695                e.was_active = false;
3696                return false;
3697             }
3698             return true;
3699             
3700         });
3701     },
3702     getWasActive : function ()
3703     {
3704         var r = false;
3705         Roo.each(this.navItems, function(e) {
3706             if (e.was_active) {
3707                r = e;
3708                return false;
3709             }
3710             return true;
3711             
3712         });
3713         return r;
3714     }
3715     
3716     
3717 });
3718
3719  
3720 Roo.apply(Roo.bootstrap.NavGroup, {
3721     
3722     groups: {},
3723      /**
3724     * register a Navigation Group
3725     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3726     */
3727     register : function(navgrp)
3728     {
3729         this.groups[navgrp.navId] = navgrp;
3730         
3731     },
3732     /**
3733     * fetch a Navigation Group based on the navigation ID
3734     * @param {string} the navgroup to add
3735     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3736     */
3737     get: function(navId) {
3738         if (typeof(this.groups[navId]) == 'undefined') {
3739             return false;
3740             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3741         }
3742         return this.groups[navId] ;
3743     }
3744     
3745     
3746     
3747 });
3748
3749  /*
3750  * - LGPL
3751  *
3752  * row
3753  * 
3754  */
3755
3756 /**
3757  * @class Roo.bootstrap.NavItem
3758  * @extends Roo.bootstrap.Component
3759  * Bootstrap Navbar.NavItem class
3760  * @cfg {String} href  link to
3761  * @cfg {String} html content of button
3762  * @cfg {String} badge text inside badge
3763  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3764  * @cfg {String} glyphicon name of glyphicon
3765  * @cfg {String} icon name of font awesome icon
3766  * @cfg {Boolean} active Is item active
3767  * @cfg {Boolean} disabled Is item disabled
3768  
3769  * @cfg {Boolean} preventDefault (true | false) default false
3770  * @cfg {String} tabId the tab that this item activates.
3771  * @cfg {String} tagtype (a|span) render as a href or span?
3772  * @cfg {Boolean} animateRef (true|false) link to element default false  
3773   
3774  * @constructor
3775  * Create a new Navbar Item
3776  * @param {Object} config The config object
3777  */
3778 Roo.bootstrap.NavItem = function(config){
3779     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3780     this.addEvents({
3781         // raw events
3782         /**
3783          * @event click
3784          * The raw click event for the entire grid.
3785          * @param {Roo.EventObject} e
3786          */
3787         "click" : true,
3788          /**
3789             * @event changed
3790             * Fires when the active item active state changes
3791             * @param {Roo.bootstrap.NavItem} this
3792             * @param {boolean} state the new state
3793              
3794          */
3795         'changed': true,
3796         /**
3797             * @event scrollto
3798             * Fires when scroll to element
3799             * @param {Roo.bootstrap.NavItem} this
3800             * @param {Object} options
3801             * @param {Roo.EventObject} e
3802              
3803          */
3804         'scrollto': true
3805     });
3806    
3807 };
3808
3809 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3810     
3811     href: false,
3812     html: '',
3813     badge: '',
3814     icon: false,
3815     glyphicon: false,
3816     active: false,
3817     preventDefault : false,
3818     tabId : false,
3819     tagtype : 'a',
3820     disabled : false,
3821     animateRef : false,
3822     was_active : false,
3823     
3824     getAutoCreate : function(){
3825          
3826         var cfg = {
3827             tag: 'li',
3828             cls: 'nav-item'
3829             
3830         }
3831         if (this.active) {
3832             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3833         }
3834         if (this.disabled) {
3835             cfg.cls += ' disabled';
3836         }
3837         
3838         if (this.href || this.html || this.glyphicon || this.icon) {
3839             cfg.cn = [
3840                 {
3841                     tag: this.tagtype,
3842                     href : this.href || "#",
3843                     html: this.html || ''
3844                 }
3845             ];
3846             
3847             if (this.icon) {
3848                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3849             }
3850
3851             if(this.glyphicon) {
3852                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3853             }
3854             
3855             if (this.menu) {
3856                 
3857                 cfg.cn[0].html += " <span class='caret'></span>";
3858              
3859             }
3860             
3861             if (this.badge !== '') {
3862                  
3863                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3864             }
3865         }
3866         
3867         
3868         
3869         return cfg;
3870     },
3871     initEvents: function() 
3872     {
3873         if (typeof (this.menu) != 'undefined') {
3874             this.menu.parentType = this.xtype;
3875             this.menu.triggerEl = this.el;
3876             this.menu = this.addxtype(Roo.apply({}, this.menu));
3877         }
3878         
3879         this.el.select('a',true).on('click', this.onClick, this);
3880         
3881         if(this.tagtype == 'span'){
3882             this.el.select('span',true).on('click', this.onClick, this);
3883         }
3884        
3885         // at this point parent should be available..
3886         this.parent().register(this);
3887     },
3888     
3889     onClick : function(e)
3890     {
3891         if(
3892                 this.preventDefault || 
3893                 this.href == '#' 
3894         ){
3895             
3896             e.preventDefault();
3897         }
3898         
3899         if (this.disabled) {
3900             return;
3901         }
3902         
3903         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3904         if (tg && tg.transition) {
3905             Roo.log("waiting for the transitionend");
3906             return;
3907         }
3908         
3909         
3910         
3911         //Roo.log("fire event clicked");
3912         if(this.fireEvent('click', this, e) === false){
3913             return;
3914         };
3915         
3916         if(this.tagtype == 'span'){
3917             return;
3918         }
3919         
3920         //Roo.log(this.href);
3921         var ael = this.el.select('a',true).first();
3922         //Roo.log(ael);
3923         
3924         if(ael && this.animateRef && this.href.indexOf('#') > -1){
3925             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
3926             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
3927                 return; // ignore... - it's a 'hash' to another page.
3928             }
3929             
3930             e.preventDefault();
3931             this.scrollToElement(e);
3932         }
3933         
3934         
3935         var p =  this.parent();
3936    
3937         if (['tabs','pills'].indexOf(p.type)!==-1) {
3938             if (typeof(p.setActiveItem) !== 'undefined') {
3939                 p.setActiveItem(this);
3940             }
3941         }
3942     },
3943     
3944     isActive: function () {
3945         return this.active
3946     },
3947     setActive : function(state, fire, is_was_active)
3948     {
3949         if (this.active && !state & this.navId) {
3950             this.was_active = true;
3951             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3952             if (nv) {
3953                 nv.clearWasActive(this);
3954             }
3955             
3956         }
3957         this.active = state;
3958         
3959         if (!state ) {
3960             this.el.removeClass('active');
3961         } else if (!this.el.hasClass('active')) {
3962             this.el.addClass('active');
3963         }
3964         if (fire) {
3965             this.fireEvent('changed', this, state);
3966         }
3967         
3968         // show a panel if it's registered and related..
3969         
3970         if (!this.navId || !this.tabId || !state || is_was_active) {
3971             return;
3972         }
3973         
3974         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3975         if (!tg) {
3976             return;
3977         }
3978         var pan = tg.getPanelByName(this.tabId);
3979         if (!pan) {
3980             return;
3981         }
3982         // if we can not flip to new panel - go back to old nav highlight..
3983         if (false == tg.showPanel(pan)) {
3984             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3985             if (nv) {
3986                 var onav = nv.getWasActive();
3987                 if (onav) {
3988                     onav.setActive(true, false, true);
3989                 }
3990             }
3991             
3992         }
3993         
3994         
3995         
3996     },
3997      // this should not be here...
3998     setDisabled : function(state)
3999     {
4000         this.disabled = state;
4001         if (!state ) {
4002             this.el.removeClass('disabled');
4003         } else if (!this.el.hasClass('disabled')) {
4004             this.el.addClass('disabled');
4005         }
4006         
4007     },
4008     
4009     /**
4010      * Fetch the element to display the tooltip on.
4011      * @return {Roo.Element} defaults to this.el
4012      */
4013     tooltipEl : function()
4014     {
4015         return this.el.select('' + this.tagtype + '', true).first();
4016     },
4017     
4018     scrollToElement : function(e)
4019     {
4020         var c = document.body;
4021         
4022         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4023         
4024         if(!target){
4025             return;
4026         }
4027
4028         var o = target.calcOffsetsTo(c);
4029         
4030         var options = {
4031             target : target,
4032             value : o[1]
4033         }
4034         
4035         this.fireEvent('scrollto', this, options, e);
4036         
4037         Roo.get(c).scrollTo('top', options.value, true);
4038         
4039         return;
4040     }
4041 });
4042  
4043
4044  /*
4045  * - LGPL
4046  *
4047  * sidebar item
4048  *
4049  *  li
4050  *    <span> icon </span>
4051  *    <span> text </span>
4052  *    <span>badge </span>
4053  */
4054
4055 /**
4056  * @class Roo.bootstrap.NavSidebarItem
4057  * @extends Roo.bootstrap.NavItem
4058  * Bootstrap Navbar.NavSidebarItem class
4059  * @constructor
4060  * Create a new Navbar Button
4061  * @param {Object} config The config object
4062  */
4063 Roo.bootstrap.NavSidebarItem = function(config){
4064     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4065     this.addEvents({
4066         // raw events
4067         /**
4068          * @event click
4069          * The raw click event for the entire grid.
4070          * @param {Roo.EventObject} e
4071          */
4072         "click" : true,
4073          /**
4074             * @event changed
4075             * Fires when the active item active state changes
4076             * @param {Roo.bootstrap.NavSidebarItem} this
4077             * @param {boolean} state the new state
4078              
4079          */
4080         'changed': true
4081     });
4082    
4083 };
4084
4085 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4086     
4087     
4088     getAutoCreate : function(){
4089         
4090         
4091         var a = {
4092                 tag: 'a',
4093                 href : this.href || '#',
4094                 cls: '',
4095                 html : '',
4096                 cn : []
4097         };
4098         var cfg = {
4099             tag: 'li',
4100             cls: '',
4101             cn: [ a ]
4102         }
4103         var span = {
4104             tag: 'span',
4105             html : this.html || ''
4106         }
4107         
4108         
4109         if (this.active) {
4110             cfg.cls += ' active';
4111         }
4112         
4113         // left icon..
4114         if (this.glyphicon || this.icon) {
4115             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4116             a.cn.push({ tag : 'i', cls : c }) ;
4117         }
4118         // html..
4119         a.cn.push(span);
4120         // then badge..
4121         if (this.badge !== '') {
4122             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4123         }
4124         // fi
4125         if (this.menu) {
4126             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4127             a.cls += 'dropdown-toggle treeview' ;
4128             
4129         }
4130         
4131         
4132         
4133         return cfg;
4134          
4135            
4136     }
4137    
4138      
4139  
4140 });
4141  
4142
4143  /*
4144  * - LGPL
4145  *
4146  * row
4147  * 
4148  */
4149
4150 /**
4151  * @class Roo.bootstrap.Row
4152  * @extends Roo.bootstrap.Component
4153  * Bootstrap Row class (contains columns...)
4154  * 
4155  * @constructor
4156  * Create a new Row
4157  * @param {Object} config The config object
4158  */
4159
4160 Roo.bootstrap.Row = function(config){
4161     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4162 };
4163
4164 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4165     
4166     getAutoCreate : function(){
4167        return {
4168             cls: 'row clearfix'
4169        };
4170     }
4171     
4172     
4173 });
4174
4175  
4176
4177  /*
4178  * - LGPL
4179  *
4180  * element
4181  * 
4182  */
4183
4184 /**
4185  * @class Roo.bootstrap.Element
4186  * @extends Roo.bootstrap.Component
4187  * Bootstrap Element class
4188  * @cfg {String} html contents of the element
4189  * @cfg {String} tag tag of the element
4190  * @cfg {String} cls class of the element
4191  * @cfg {Boolean} preventDefault (true|false) default false
4192  * @cfg {Boolean} clickable (true|false) default false
4193  * 
4194  * @constructor
4195  * Create a new Element
4196  * @param {Object} config The config object
4197  */
4198
4199 Roo.bootstrap.Element = function(config){
4200     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4201     
4202     this.addEvents({
4203         // raw events
4204         /**
4205          * @event click
4206          * When a element is chick
4207          * @param {Roo.bootstrap.Element} this
4208          * @param {Roo.EventObject} e
4209          */
4210         "click" : true
4211     });
4212 };
4213
4214 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4215     
4216     tag: 'div',
4217     cls: '',
4218     html: '',
4219     preventDefault: false, 
4220     clickable: false,
4221     
4222     getAutoCreate : function(){
4223         
4224         var cfg = {
4225             tag: this.tag,
4226             cls: this.cls,
4227             html: this.html
4228         }
4229         
4230         return cfg;
4231     },
4232     
4233     initEvents: function() 
4234     {
4235         Roo.bootstrap.Element.superclass.initEvents.call(this);
4236         
4237         if(this.clickable){
4238             this.el.on('click', this.onClick, this);
4239         }
4240         
4241     },
4242     
4243     onClick : function(e)
4244     {
4245         if(this.preventDefault){
4246             e.preventDefault();
4247         }
4248         
4249         this.fireEvent('click', this, e);
4250     },
4251     
4252     getValue : function()
4253     {
4254         return this.el.dom.innerHTML;
4255     },
4256     
4257     setValue : function(value)
4258     {
4259         this.el.dom.innerHTML = value;
4260     }
4261    
4262 });
4263
4264  
4265
4266  /*
4267  * - LGPL
4268  *
4269  * pagination
4270  * 
4271  */
4272
4273 /**
4274  * @class Roo.bootstrap.Pagination
4275  * @extends Roo.bootstrap.Component
4276  * Bootstrap Pagination class
4277  * @cfg {String} size xs | sm | md | lg
4278  * @cfg {Boolean} inverse false | true
4279  * 
4280  * @constructor
4281  * Create a new Pagination
4282  * @param {Object} config The config object
4283  */
4284
4285 Roo.bootstrap.Pagination = function(config){
4286     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4287 };
4288
4289 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4290     
4291     cls: false,
4292     size: false,
4293     inverse: false,
4294     
4295     getAutoCreate : function(){
4296         var cfg = {
4297             tag: 'ul',
4298                 cls: 'pagination'
4299         };
4300         if (this.inverse) {
4301             cfg.cls += ' inverse';
4302         }
4303         if (this.html) {
4304             cfg.html=this.html;
4305         }
4306         if (this.cls) {
4307             cfg.cls += " " + this.cls;
4308         }
4309         return cfg;
4310     }
4311    
4312 });
4313
4314  
4315
4316  /*
4317  * - LGPL
4318  *
4319  * Pagination item
4320  * 
4321  */
4322
4323
4324 /**
4325  * @class Roo.bootstrap.PaginationItem
4326  * @extends Roo.bootstrap.Component
4327  * Bootstrap PaginationItem class
4328  * @cfg {String} html text
4329  * @cfg {String} href the link
4330  * @cfg {Boolean} preventDefault (true | false) default true
4331  * @cfg {Boolean} active (true | false) default false
4332  * @cfg {Boolean} disabled default false
4333  * 
4334  * 
4335  * @constructor
4336  * Create a new PaginationItem
4337  * @param {Object} config The config object
4338  */
4339
4340
4341 Roo.bootstrap.PaginationItem = function(config){
4342     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4343     this.addEvents({
4344         // raw events
4345         /**
4346          * @event click
4347          * The raw click event for the entire grid.
4348          * @param {Roo.EventObject} e
4349          */
4350         "click" : true
4351     });
4352 };
4353
4354 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4355     
4356     href : false,
4357     html : false,
4358     preventDefault: true,
4359     active : false,
4360     cls : false,
4361     disabled: false,
4362     
4363     getAutoCreate : function(){
4364         var cfg= {
4365             tag: 'li',
4366             cn: [
4367                 {
4368                     tag : 'a',
4369                     href : this.href ? this.href : '#',
4370                     html : this.html ? this.html : ''
4371                 }
4372             ]
4373         };
4374         
4375         if(this.cls){
4376             cfg.cls = this.cls;
4377         }
4378         
4379         if(this.disabled){
4380             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4381         }
4382         
4383         if(this.active){
4384             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4385         }
4386         
4387         return cfg;
4388     },
4389     
4390     initEvents: function() {
4391         
4392         this.el.on('click', this.onClick, this);
4393         
4394     },
4395     onClick : function(e)
4396     {
4397         Roo.log('PaginationItem on click ');
4398         if(this.preventDefault){
4399             e.preventDefault();
4400         }
4401         
4402         if(this.disabled){
4403             return;
4404         }
4405         
4406         this.fireEvent('click', this, e);
4407     }
4408    
4409 });
4410
4411  
4412
4413  /*
4414  * - LGPL
4415  *
4416  * slider
4417  * 
4418  */
4419
4420
4421 /**
4422  * @class Roo.bootstrap.Slider
4423  * @extends Roo.bootstrap.Component
4424  * Bootstrap Slider class
4425  *    
4426  * @constructor
4427  * Create a new Slider
4428  * @param {Object} config The config object
4429  */
4430
4431 Roo.bootstrap.Slider = function(config){
4432     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4433 };
4434
4435 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4436     
4437     getAutoCreate : function(){
4438         
4439         var cfg = {
4440             tag: 'div',
4441             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4442             cn: [
4443                 {
4444                     tag: 'a',
4445                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4446                 }
4447             ]
4448         }
4449         
4450         return cfg;
4451     }
4452    
4453 });
4454
4455  /*
4456  * Based on:
4457  * Ext JS Library 1.1.1
4458  * Copyright(c) 2006-2007, Ext JS, LLC.
4459  *
4460  * Originally Released Under LGPL - original licence link has changed is not relivant.
4461  *
4462  * Fork - LGPL
4463  * <script type="text/javascript">
4464  */
4465  
4466
4467 /**
4468  * @class Roo.grid.ColumnModel
4469  * @extends Roo.util.Observable
4470  * This is the default implementation of a ColumnModel used by the Grid. It defines
4471  * the columns in the grid.
4472  * <br>Usage:<br>
4473  <pre><code>
4474  var colModel = new Roo.grid.ColumnModel([
4475         {header: "Ticker", width: 60, sortable: true, locked: true},
4476         {header: "Company Name", width: 150, sortable: true},
4477         {header: "Market Cap.", width: 100, sortable: true},
4478         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4479         {header: "Employees", width: 100, sortable: true, resizable: false}
4480  ]);
4481  </code></pre>
4482  * <p>
4483  
4484  * The config options listed for this class are options which may appear in each
4485  * individual column definition.
4486  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4487  * @constructor
4488  * @param {Object} config An Array of column config objects. See this class's
4489  * config objects for details.
4490 */
4491 Roo.grid.ColumnModel = function(config){
4492         /**
4493      * The config passed into the constructor
4494      */
4495     this.config = config;
4496     this.lookup = {};
4497
4498     // if no id, create one
4499     // if the column does not have a dataIndex mapping,
4500     // map it to the order it is in the config
4501     for(var i = 0, len = config.length; i < len; i++){
4502         var c = config[i];
4503         if(typeof c.dataIndex == "undefined"){
4504             c.dataIndex = i;
4505         }
4506         if(typeof c.renderer == "string"){
4507             c.renderer = Roo.util.Format[c.renderer];
4508         }
4509         if(typeof c.id == "undefined"){
4510             c.id = Roo.id();
4511         }
4512         if(c.editor && c.editor.xtype){
4513             c.editor  = Roo.factory(c.editor, Roo.grid);
4514         }
4515         if(c.editor && c.editor.isFormField){
4516             c.editor = new Roo.grid.GridEditor(c.editor);
4517         }
4518         this.lookup[c.id] = c;
4519     }
4520
4521     /**
4522      * The width of columns which have no width specified (defaults to 100)
4523      * @type Number
4524      */
4525     this.defaultWidth = 100;
4526
4527     /**
4528      * Default sortable of columns which have no sortable specified (defaults to false)
4529      * @type Boolean
4530      */
4531     this.defaultSortable = false;
4532
4533     this.addEvents({
4534         /**
4535              * @event widthchange
4536              * Fires when the width of a column changes.
4537              * @param {ColumnModel} this
4538              * @param {Number} columnIndex The column index
4539              * @param {Number} newWidth The new width
4540              */
4541             "widthchange": true,
4542         /**
4543              * @event headerchange
4544              * Fires when the text of a header changes.
4545              * @param {ColumnModel} this
4546              * @param {Number} columnIndex The column index
4547              * @param {Number} newText The new header text
4548              */
4549             "headerchange": true,
4550         /**
4551              * @event hiddenchange
4552              * Fires when a column is hidden or "unhidden".
4553              * @param {ColumnModel} this
4554              * @param {Number} columnIndex The column index
4555              * @param {Boolean} hidden true if hidden, false otherwise
4556              */
4557             "hiddenchange": true,
4558             /**
4559          * @event columnmoved
4560          * Fires when a column is moved.
4561          * @param {ColumnModel} this
4562          * @param {Number} oldIndex
4563          * @param {Number} newIndex
4564          */
4565         "columnmoved" : true,
4566         /**
4567          * @event columlockchange
4568          * Fires when a column's locked state is changed
4569          * @param {ColumnModel} this
4570          * @param {Number} colIndex
4571          * @param {Boolean} locked true if locked
4572          */
4573         "columnlockchange" : true
4574     });
4575     Roo.grid.ColumnModel.superclass.constructor.call(this);
4576 };
4577 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4578     /**
4579      * @cfg {String} header The header text to display in the Grid view.
4580      */
4581     /**
4582      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4583      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4584      * specified, the column's index is used as an index into the Record's data Array.
4585      */
4586     /**
4587      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4588      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4589      */
4590     /**
4591      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4592      * Defaults to the value of the {@link #defaultSortable} property.
4593      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4594      */
4595     /**
4596      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4597      */
4598     /**
4599      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4600      */
4601     /**
4602      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4603      */
4604     /**
4605      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4606      */
4607     /**
4608      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4609      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4610      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4611      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4612      */
4613        /**
4614      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4615      */
4616     /**
4617      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4618      */
4619     /**
4620      * @cfg {String} cursor (Optional)
4621      */
4622     /**
4623      * @cfg {String} tooltip (Optional)
4624      */
4625     /**
4626      * Returns the id of the column at the specified index.
4627      * @param {Number} index The column index
4628      * @return {String} the id
4629      */
4630     getColumnId : function(index){
4631         return this.config[index].id;
4632     },
4633
4634     /**
4635      * Returns the column for a specified id.
4636      * @param {String} id The column id
4637      * @return {Object} the column
4638      */
4639     getColumnById : function(id){
4640         return this.lookup[id];
4641     },
4642
4643     
4644     /**
4645      * Returns the column for a specified dataIndex.
4646      * @param {String} dataIndex The column dataIndex
4647      * @return {Object|Boolean} the column or false if not found
4648      */
4649     getColumnByDataIndex: function(dataIndex){
4650         var index = this.findColumnIndex(dataIndex);
4651         return index > -1 ? this.config[index] : false;
4652     },
4653     
4654     /**
4655      * Returns the index for a specified column id.
4656      * @param {String} id The column id
4657      * @return {Number} the index, or -1 if not found
4658      */
4659     getIndexById : function(id){
4660         for(var i = 0, len = this.config.length; i < len; i++){
4661             if(this.config[i].id == id){
4662                 return i;
4663             }
4664         }
4665         return -1;
4666     },
4667     
4668     /**
4669      * Returns the index for a specified column dataIndex.
4670      * @param {String} dataIndex The column dataIndex
4671      * @return {Number} the index, or -1 if not found
4672      */
4673     
4674     findColumnIndex : function(dataIndex){
4675         for(var i = 0, len = this.config.length; i < len; i++){
4676             if(this.config[i].dataIndex == dataIndex){
4677                 return i;
4678             }
4679         }
4680         return -1;
4681     },
4682     
4683     
4684     moveColumn : function(oldIndex, newIndex){
4685         var c = this.config[oldIndex];
4686         this.config.splice(oldIndex, 1);
4687         this.config.splice(newIndex, 0, c);
4688         this.dataMap = null;
4689         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4690     },
4691
4692     isLocked : function(colIndex){
4693         return this.config[colIndex].locked === true;
4694     },
4695
4696     setLocked : function(colIndex, value, suppressEvent){
4697         if(this.isLocked(colIndex) == value){
4698             return;
4699         }
4700         this.config[colIndex].locked = value;
4701         if(!suppressEvent){
4702             this.fireEvent("columnlockchange", this, colIndex, value);
4703         }
4704     },
4705
4706     getTotalLockedWidth : function(){
4707         var totalWidth = 0;
4708         for(var i = 0; i < this.config.length; i++){
4709             if(this.isLocked(i) && !this.isHidden(i)){
4710                 this.totalWidth += this.getColumnWidth(i);
4711             }
4712         }
4713         return totalWidth;
4714     },
4715
4716     getLockedCount : function(){
4717         for(var i = 0, len = this.config.length; i < len; i++){
4718             if(!this.isLocked(i)){
4719                 return i;
4720             }
4721         }
4722     },
4723
4724     /**
4725      * Returns the number of columns.
4726      * @return {Number}
4727      */
4728     getColumnCount : function(visibleOnly){
4729         if(visibleOnly === true){
4730             var c = 0;
4731             for(var i = 0, len = this.config.length; i < len; i++){
4732                 if(!this.isHidden(i)){
4733                     c++;
4734                 }
4735             }
4736             return c;
4737         }
4738         return this.config.length;
4739     },
4740
4741     /**
4742      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4743      * @param {Function} fn
4744      * @param {Object} scope (optional)
4745      * @return {Array} result
4746      */
4747     getColumnsBy : function(fn, scope){
4748         var r = [];
4749         for(var i = 0, len = this.config.length; i < len; i++){
4750             var c = this.config[i];
4751             if(fn.call(scope||this, c, i) === true){
4752                 r[r.length] = c;
4753             }
4754         }
4755         return r;
4756     },
4757
4758     /**
4759      * Returns true if the specified column is sortable.
4760      * @param {Number} col The column index
4761      * @return {Boolean}
4762      */
4763     isSortable : function(col){
4764         if(typeof this.config[col].sortable == "undefined"){
4765             return this.defaultSortable;
4766         }
4767         return this.config[col].sortable;
4768     },
4769
4770     /**
4771      * Returns the rendering (formatting) function defined for the column.
4772      * @param {Number} col The column index.
4773      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4774      */
4775     getRenderer : function(col){
4776         if(!this.config[col].renderer){
4777             return Roo.grid.ColumnModel.defaultRenderer;
4778         }
4779         return this.config[col].renderer;
4780     },
4781
4782     /**
4783      * Sets the rendering (formatting) function for a column.
4784      * @param {Number} col The column index
4785      * @param {Function} fn The function to use to process the cell's raw data
4786      * to return HTML markup for the grid view. The render function is called with
4787      * the following parameters:<ul>
4788      * <li>Data value.</li>
4789      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4790      * <li>css A CSS style string to apply to the table cell.</li>
4791      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4792      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4793      * <li>Row index</li>
4794      * <li>Column index</li>
4795      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4796      */
4797     setRenderer : function(col, fn){
4798         this.config[col].renderer = fn;
4799     },
4800
4801     /**
4802      * Returns the width for the specified column.
4803      * @param {Number} col The column index
4804      * @return {Number}
4805      */
4806     getColumnWidth : function(col){
4807         return this.config[col].width * 1 || this.defaultWidth;
4808     },
4809
4810     /**
4811      * Sets the width for a column.
4812      * @param {Number} col The column index
4813      * @param {Number} width The new width
4814      */
4815     setColumnWidth : function(col, width, suppressEvent){
4816         this.config[col].width = width;
4817         this.totalWidth = null;
4818         if(!suppressEvent){
4819              this.fireEvent("widthchange", this, col, width);
4820         }
4821     },
4822
4823     /**
4824      * Returns the total width of all columns.
4825      * @param {Boolean} includeHidden True to include hidden column widths
4826      * @return {Number}
4827      */
4828     getTotalWidth : function(includeHidden){
4829         if(!this.totalWidth){
4830             this.totalWidth = 0;
4831             for(var i = 0, len = this.config.length; i < len; i++){
4832                 if(includeHidden || !this.isHidden(i)){
4833                     this.totalWidth += this.getColumnWidth(i);
4834                 }
4835             }
4836         }
4837         return this.totalWidth;
4838     },
4839
4840     /**
4841      * Returns the header for the specified column.
4842      * @param {Number} col The column index
4843      * @return {String}
4844      */
4845     getColumnHeader : function(col){
4846         return this.config[col].header;
4847     },
4848
4849     /**
4850      * Sets the header for a column.
4851      * @param {Number} col The column index
4852      * @param {String} header The new header
4853      */
4854     setColumnHeader : function(col, header){
4855         this.config[col].header = header;
4856         this.fireEvent("headerchange", this, col, header);
4857     },
4858
4859     /**
4860      * Returns the tooltip for the specified column.
4861      * @param {Number} col The column index
4862      * @return {String}
4863      */
4864     getColumnTooltip : function(col){
4865             return this.config[col].tooltip;
4866     },
4867     /**
4868      * Sets the tooltip for a column.
4869      * @param {Number} col The column index
4870      * @param {String} tooltip The new tooltip
4871      */
4872     setColumnTooltip : function(col, tooltip){
4873             this.config[col].tooltip = tooltip;
4874     },
4875
4876     /**
4877      * Returns the dataIndex for the specified column.
4878      * @param {Number} col The column index
4879      * @return {Number}
4880      */
4881     getDataIndex : function(col){
4882         return this.config[col].dataIndex;
4883     },
4884
4885     /**
4886      * Sets the dataIndex for a column.
4887      * @param {Number} col The column index
4888      * @param {Number} dataIndex The new dataIndex
4889      */
4890     setDataIndex : function(col, dataIndex){
4891         this.config[col].dataIndex = dataIndex;
4892     },
4893
4894     
4895     
4896     /**
4897      * Returns true if the cell is editable.
4898      * @param {Number} colIndex The column index
4899      * @param {Number} rowIndex The row index
4900      * @return {Boolean}
4901      */
4902     isCellEditable : function(colIndex, rowIndex){
4903         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4904     },
4905
4906     /**
4907      * Returns the editor defined for the cell/column.
4908      * return false or null to disable editing.
4909      * @param {Number} colIndex The column index
4910      * @param {Number} rowIndex The row index
4911      * @return {Object}
4912      */
4913     getCellEditor : function(colIndex, rowIndex){
4914         return this.config[colIndex].editor;
4915     },
4916
4917     /**
4918      * Sets if a column is editable.
4919      * @param {Number} col The column index
4920      * @param {Boolean} editable True if the column is editable
4921      */
4922     setEditable : function(col, editable){
4923         this.config[col].editable = editable;
4924     },
4925
4926
4927     /**
4928      * Returns true if the column is hidden.
4929      * @param {Number} colIndex The column index
4930      * @return {Boolean}
4931      */
4932     isHidden : function(colIndex){
4933         return this.config[colIndex].hidden;
4934     },
4935
4936
4937     /**
4938      * Returns true if the column width cannot be changed
4939      */
4940     isFixed : function(colIndex){
4941         return this.config[colIndex].fixed;
4942     },
4943
4944     /**
4945      * Returns true if the column can be resized
4946      * @return {Boolean}
4947      */
4948     isResizable : function(colIndex){
4949         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4950     },
4951     /**
4952      * Sets if a column is hidden.
4953      * @param {Number} colIndex The column index
4954      * @param {Boolean} hidden True if the column is hidden
4955      */
4956     setHidden : function(colIndex, hidden){
4957         this.config[colIndex].hidden = hidden;
4958         this.totalWidth = null;
4959         this.fireEvent("hiddenchange", this, colIndex, hidden);
4960     },
4961
4962     /**
4963      * Sets the editor for a column.
4964      * @param {Number} col The column index
4965      * @param {Object} editor The editor object
4966      */
4967     setEditor : function(col, editor){
4968         this.config[col].editor = editor;
4969     }
4970 });
4971
4972 Roo.grid.ColumnModel.defaultRenderer = function(value){
4973         if(typeof value == "string" && value.length < 1){
4974             return "&#160;";
4975         }
4976         return value;
4977 };
4978
4979 // Alias for backwards compatibility
4980 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4981 /*
4982  * Based on:
4983  * Ext JS Library 1.1.1
4984  * Copyright(c) 2006-2007, Ext JS, LLC.
4985  *
4986  * Originally Released Under LGPL - original licence link has changed is not relivant.
4987  *
4988  * Fork - LGPL
4989  * <script type="text/javascript">
4990  */
4991  
4992 /**
4993  * @class Roo.LoadMask
4994  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4995  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4996  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4997  * element's UpdateManager load indicator and will be destroyed after the initial load.
4998  * @constructor
4999  * Create a new LoadMask
5000  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5001  * @param {Object} config The config object
5002  */
5003 Roo.LoadMask = function(el, config){
5004     this.el = Roo.get(el);
5005     Roo.apply(this, config);
5006     if(this.store){
5007         this.store.on('beforeload', this.onBeforeLoad, this);
5008         this.store.on('load', this.onLoad, this);
5009         this.store.on('loadexception', this.onLoadException, this);
5010         this.removeMask = false;
5011     }else{
5012         var um = this.el.getUpdateManager();
5013         um.showLoadIndicator = false; // disable the default indicator
5014         um.on('beforeupdate', this.onBeforeLoad, this);
5015         um.on('update', this.onLoad, this);
5016         um.on('failure', this.onLoad, this);
5017         this.removeMask = true;
5018     }
5019 };
5020
5021 Roo.LoadMask.prototype = {
5022     /**
5023      * @cfg {Boolean} removeMask
5024      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5025      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5026      */
5027     /**
5028      * @cfg {String} msg
5029      * The text to display in a centered loading message box (defaults to 'Loading...')
5030      */
5031     msg : 'Loading...',
5032     /**
5033      * @cfg {String} msgCls
5034      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5035      */
5036     msgCls : 'x-mask-loading',
5037
5038     /**
5039      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5040      * @type Boolean
5041      */
5042     disabled: false,
5043
5044     /**
5045      * Disables the mask to prevent it from being displayed
5046      */
5047     disable : function(){
5048        this.disabled = true;
5049     },
5050
5051     /**
5052      * Enables the mask so that it can be displayed
5053      */
5054     enable : function(){
5055         this.disabled = false;
5056     },
5057     
5058     onLoadException : function()
5059     {
5060         Roo.log(arguments);
5061         
5062         if (typeof(arguments[3]) != 'undefined') {
5063             Roo.MessageBox.alert("Error loading",arguments[3]);
5064         } 
5065         /*
5066         try {
5067             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5068                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5069             }   
5070         } catch(e) {
5071             
5072         }
5073         */
5074     
5075         
5076         
5077         this.el.unmask(this.removeMask);
5078     },
5079     // private
5080     onLoad : function()
5081     {
5082         this.el.unmask(this.removeMask);
5083     },
5084
5085     // private
5086     onBeforeLoad : function(){
5087         if(!this.disabled){
5088             this.el.mask(this.msg, this.msgCls);
5089         }
5090     },
5091
5092     // private
5093     destroy : function(){
5094         if(this.store){
5095             this.store.un('beforeload', this.onBeforeLoad, this);
5096             this.store.un('load', this.onLoad, this);
5097             this.store.un('loadexception', this.onLoadException, this);
5098         }else{
5099             var um = this.el.getUpdateManager();
5100             um.un('beforeupdate', this.onBeforeLoad, this);
5101             um.un('update', this.onLoad, this);
5102             um.un('failure', this.onLoad, this);
5103         }
5104     }
5105 };/*
5106  * - LGPL
5107  *
5108  * table
5109  * 
5110  */
5111
5112 /**
5113  * @class Roo.bootstrap.Table
5114  * @extends Roo.bootstrap.Component
5115  * Bootstrap Table class
5116  * @cfg {String} cls table class
5117  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5118  * @cfg {String} bgcolor Specifies the background color for a table
5119  * @cfg {Number} border Specifies whether the table cells should have borders or not
5120  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5121  * @cfg {Number} cellspacing Specifies the space between cells
5122  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5123  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5124  * @cfg {String} sortable Specifies that the table should be sortable
5125  * @cfg {String} summary Specifies a summary of the content of a table
5126  * @cfg {Number} width Specifies the width of a table
5127  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5128  * 
5129  * @cfg {boolean} striped Should the rows be alternative striped
5130  * @cfg {boolean} bordered Add borders to the table
5131  * @cfg {boolean} hover Add hover highlighting
5132  * @cfg {boolean} condensed Format condensed
5133  * @cfg {boolean} responsive Format condensed
5134  * @cfg {Boolean} loadMask (true|false) default false
5135  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5136  * @cfg {Boolean} thead (true|false) generate thead, default true
5137  * @cfg {Boolean} RowSelection (true|false) default false
5138  * @cfg {Boolean} CellSelection (true|false) default false
5139  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5140  
5141  * 
5142  * @constructor
5143  * Create a new Table
5144  * @param {Object} config The config object
5145  */
5146
5147 Roo.bootstrap.Table = function(config){
5148     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5149     
5150     if (this.sm) {
5151         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5152         this.sm = this.selModel;
5153         this.sm.xmodule = this.xmodule || false;
5154     }
5155     if (this.cm && typeof(this.cm.config) == 'undefined') {
5156         this.colModel = new Roo.grid.ColumnModel(this.cm);
5157         this.cm = this.colModel;
5158         this.cm.xmodule = this.xmodule || false;
5159     }
5160     if (this.store) {
5161         this.store= Roo.factory(this.store, Roo.data);
5162         this.ds = this.store;
5163         this.ds.xmodule = this.xmodule || false;
5164          
5165     }
5166     if (this.footer && this.store) {
5167         this.footer.dataSource = this.ds;
5168         this.footer = Roo.factory(this.footer);
5169     }
5170     
5171     /** @private */
5172     this.addEvents({
5173         /**
5174          * @event cellclick
5175          * Fires when a cell is clicked
5176          * @param {Roo.bootstrap.Table} this
5177          * @param {Roo.Element} el
5178          * @param {Number} rowIndex
5179          * @param {Number} columnIndex
5180          * @param {Roo.EventObject} e
5181          */
5182         "cellclick" : true,
5183         /**
5184          * @event celldblclick
5185          * Fires when a cell is double clicked
5186          * @param {Roo.bootstrap.Table} this
5187          * @param {Roo.Element} el
5188          * @param {Number} rowIndex
5189          * @param {Number} columnIndex
5190          * @param {Roo.EventObject} e
5191          */
5192         "celldblclick" : true,
5193         /**
5194          * @event rowclick
5195          * Fires when a row is clicked
5196          * @param {Roo.bootstrap.Table} this
5197          * @param {Roo.Element} el
5198          * @param {Number} rowIndex
5199          * @param {Roo.EventObject} e
5200          */
5201         "rowclick" : true,
5202         /**
5203          * @event rowdblclick
5204          * Fires when a row is double clicked
5205          * @param {Roo.bootstrap.Table} this
5206          * @param {Roo.Element} el
5207          * @param {Number} rowIndex
5208          * @param {Roo.EventObject} e
5209          */
5210         "rowdblclick" : true,
5211         /**
5212          * @event mouseover
5213          * Fires when a mouseover occur
5214          * @param {Roo.bootstrap.Table} this
5215          * @param {Roo.Element} el
5216          * @param {Number} rowIndex
5217          * @param {Number} columnIndex
5218          * @param {Roo.EventObject} e
5219          */
5220         "mouseover" : true,
5221         /**
5222          * @event mouseout
5223          * Fires when a mouseout occur
5224          * @param {Roo.bootstrap.Table} this
5225          * @param {Roo.Element} el
5226          * @param {Number} rowIndex
5227          * @param {Number} columnIndex
5228          * @param {Roo.EventObject} e
5229          */
5230         "mouseout" : true,
5231         /**
5232          * @event rowclass
5233          * Fires when a row is rendered, so you can change add a style to it.
5234          * @param {Roo.bootstrap.Table} this
5235          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5236          */
5237         'rowclass' : true,
5238           /**
5239          * @event rowsrendered
5240          * Fires when all the  rows have been rendered
5241          * @param {Roo.bootstrap.Table} this
5242          */
5243         'rowsrendered' : true
5244         
5245     });
5246 };
5247
5248 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5249     
5250     cls: false,
5251     align: false,
5252     bgcolor: false,
5253     border: false,
5254     cellpadding: false,
5255     cellspacing: false,
5256     frame: false,
5257     rules: false,
5258     sortable: false,
5259     summary: false,
5260     width: false,
5261     striped : false,
5262     bordered: false,
5263     hover:  false,
5264     condensed : false,
5265     responsive : false,
5266     sm : false,
5267     cm : false,
5268     store : false,
5269     loadMask : false,
5270     tfoot : true,
5271     thead : true,
5272     RowSelection : false,
5273     CellSelection : false,
5274     layout : false,
5275     
5276     // Roo.Element - the tbody
5277     mainBody: false, 
5278     
5279     getAutoCreate : function(){
5280         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5281         
5282         cfg = {
5283             tag: 'table',
5284             cls : 'table',
5285             cn : []
5286         }
5287             
5288         if (this.striped) {
5289             cfg.cls += ' table-striped';
5290         }
5291         
5292         if (this.hover) {
5293             cfg.cls += ' table-hover';
5294         }
5295         if (this.bordered) {
5296             cfg.cls += ' table-bordered';
5297         }
5298         if (this.condensed) {
5299             cfg.cls += ' table-condensed';
5300         }
5301         if (this.responsive) {
5302             cfg.cls += ' table-responsive';
5303         }
5304         
5305         if (this.cls) {
5306             cfg.cls+=  ' ' +this.cls;
5307         }
5308         
5309         // this lot should be simplifed...
5310         
5311         if (this.align) {
5312             cfg.align=this.align;
5313         }
5314         if (this.bgcolor) {
5315             cfg.bgcolor=this.bgcolor;
5316         }
5317         if (this.border) {
5318             cfg.border=this.border;
5319         }
5320         if (this.cellpadding) {
5321             cfg.cellpadding=this.cellpadding;
5322         }
5323         if (this.cellspacing) {
5324             cfg.cellspacing=this.cellspacing;
5325         }
5326         if (this.frame) {
5327             cfg.frame=this.frame;
5328         }
5329         if (this.rules) {
5330             cfg.rules=this.rules;
5331         }
5332         if (this.sortable) {
5333             cfg.sortable=this.sortable;
5334         }
5335         if (this.summary) {
5336             cfg.summary=this.summary;
5337         }
5338         if (this.width) {
5339             cfg.width=this.width;
5340         }
5341         if (this.layout) {
5342             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5343         }
5344         
5345         if(this.store || this.cm){
5346             if(this.thead){
5347                 cfg.cn.push(this.renderHeader());
5348             }
5349             
5350             cfg.cn.push(this.renderBody());
5351             
5352             if(this.tfoot){
5353                 cfg.cn.push(this.renderFooter());
5354             }
5355             
5356             cfg.cls+=  ' TableGrid';
5357         }
5358         
5359         return { cn : [ cfg ] };
5360     },
5361     
5362     initEvents : function()
5363     {   
5364         if(!this.store || !this.cm){
5365             return;
5366         }
5367         
5368         //Roo.log('initEvents with ds!!!!');
5369         
5370         this.mainBody = this.el.select('tbody', true).first();
5371         
5372         
5373         var _this = this;
5374         
5375         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5376             e.on('click', _this.sort, _this);
5377         });
5378         
5379         this.el.on("click", this.onClick, this);
5380         this.el.on("dblclick", this.onDblClick, this);
5381         
5382         // why is this done????? = it breaks dialogs??
5383         //this.parent().el.setStyle('position', 'relative');
5384         
5385         
5386         if (this.footer) {
5387             this.footer.parentId = this.id;
5388             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5389         }
5390         
5391         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5392         
5393         this.store.on('load', this.onLoad, this);
5394         this.store.on('beforeload', this.onBeforeLoad, this);
5395         this.store.on('update', this.onUpdate, this);
5396         this.store.on('add', this.onAdd, this);
5397         
5398     },
5399     
5400     onMouseover : function(e, el)
5401     {
5402         var cell = Roo.get(el);
5403         
5404         if(!cell){
5405             return;
5406         }
5407         
5408         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5409             cell = cell.findParent('td', false, true);
5410         }
5411         
5412         var row = cell.findParent('tr', false, true);
5413         var cellIndex = cell.dom.cellIndex;
5414         var rowIndex = row.dom.rowIndex - 1; // start from 0
5415         
5416         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5417         
5418     },
5419     
5420     onMouseout : function(e, el)
5421     {
5422         var cell = Roo.get(el);
5423         
5424         if(!cell){
5425             return;
5426         }
5427         
5428         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5429             cell = cell.findParent('td', false, true);
5430         }
5431         
5432         var row = cell.findParent('tr', false, true);
5433         var cellIndex = cell.dom.cellIndex;
5434         var rowIndex = row.dom.rowIndex - 1; // start from 0
5435         
5436         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5437         
5438     },
5439     
5440     onClick : function(e, el)
5441     {
5442         var cell = Roo.get(el);
5443         
5444         if(!cell || (!this.CellSelection && !this.RowSelection)){
5445             return;
5446         }
5447         
5448         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5449             cell = cell.findParent('td', false, true);
5450         }
5451         
5452         if(!cell || typeof(cell) == 'undefined'){
5453             return;
5454         }
5455         
5456         var row = cell.findParent('tr', false, true);
5457         
5458         if(!row || typeof(row) == 'undefined'){
5459             return;
5460         }
5461         
5462         var cellIndex = cell.dom.cellIndex;
5463         var rowIndex = this.getRowIndex(row);
5464         
5465         if(this.CellSelection){
5466             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5467         }
5468         
5469         if(this.RowSelection){
5470             this.fireEvent('rowclick', this, row, rowIndex, e);
5471         }
5472         
5473         
5474     },
5475     
5476     onDblClick : function(e,el)
5477     {
5478         var cell = Roo.get(el);
5479         
5480         if(!cell || (!this.CellSelection && !this.RowSelection)){
5481             return;
5482         }
5483         
5484         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5485             cell = cell.findParent('td', false, true);
5486         }
5487         
5488         if(!cell || typeof(cell) == 'undefined'){
5489             return;
5490         }
5491         
5492         var row = cell.findParent('tr', false, true);
5493         
5494         if(!row || typeof(row) == 'undefined'){
5495             return;
5496         }
5497         
5498         var cellIndex = cell.dom.cellIndex;
5499         var rowIndex = this.getRowIndex(row);
5500         
5501         if(this.CellSelection){
5502             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5503         }
5504         
5505         if(this.RowSelection){
5506             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5507         }
5508     },
5509     
5510     sort : function(e,el)
5511     {
5512         var col = Roo.get(el);
5513         
5514         if(!col.hasClass('sortable')){
5515             return;
5516         }
5517         
5518         var sort = col.attr('sort');
5519         var dir = 'ASC';
5520         
5521         if(col.hasClass('glyphicon-arrow-up')){
5522             dir = 'DESC';
5523         }
5524         
5525         this.store.sortInfo = {field : sort, direction : dir};
5526         
5527         if (this.footer) {
5528             Roo.log("calling footer first");
5529             this.footer.onClick('first');
5530         } else {
5531         
5532             this.store.load({ params : { start : 0 } });
5533         }
5534     },
5535     
5536     renderHeader : function()
5537     {
5538         var header = {
5539             tag: 'thead',
5540             cn : []
5541         };
5542         
5543         var cm = this.cm;
5544         
5545         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5546             
5547             var config = cm.config[i];
5548                     
5549             var c = {
5550                 tag: 'th',
5551                 style : '',
5552                 html: cm.getColumnHeader(i)
5553             };
5554             
5555             if(typeof(config.tooltip) != 'undefined'){
5556                 c.tooltip = config.tooltip;
5557             }
5558             
5559             if(typeof(config.colspan) != 'undefined'){
5560                 c.colspan = config.colspan;
5561             }
5562             
5563             if(typeof(config.hidden) != 'undefined' && config.hidden){
5564                 c.style += ' display:none;';
5565             }
5566             
5567             if(typeof(config.dataIndex) != 'undefined'){
5568                 c.sort = config.dataIndex;
5569             }
5570             
5571             if(typeof(config.sortable) != 'undefined' && config.sortable){
5572                 c.cls = 'sortable';
5573             }
5574             
5575             if(typeof(config.align) != 'undefined' && config.align.length){
5576                 c.style += ' text-align:' + config.align + ';';
5577             }
5578             
5579             if(typeof(config.width) != 'undefined'){
5580                 c.style += ' width:' + config.width + 'px;';
5581             }
5582             
5583             if(typeof(config.cls) != 'undefined'){
5584                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5585             }
5586             
5587             header.cn.push(c)
5588         }
5589         
5590         return header;
5591     },
5592     
5593     renderBody : function()
5594     {
5595         var body = {
5596             tag: 'tbody',
5597             cn : [
5598                 {
5599                     tag: 'tr',
5600                     cn : [
5601                         {
5602                             tag : 'td',
5603                             colspan :  this.cm.getColumnCount()
5604                         }
5605                     ]
5606                 }
5607             ]
5608         };
5609         
5610         return body;
5611     },
5612     
5613     renderFooter : function()
5614     {
5615         var footer = {
5616             tag: 'tfoot',
5617             cn : [
5618                 {
5619                     tag: 'tr',
5620                     cn : [
5621                         {
5622                             tag : 'td',
5623                             colspan :  this.cm.getColumnCount()
5624                         }
5625                     ]
5626                 }
5627             ]
5628         };
5629         
5630         return footer;
5631     },
5632     
5633     
5634     
5635     onLoad : function()
5636     {
5637         Roo.log('ds onload');
5638         this.clear();
5639         
5640         var _this = this;
5641         var cm = this.cm;
5642         var ds = this.store;
5643         
5644         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5645             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5646             
5647             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5648                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5649             }
5650             
5651             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5652                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5653             }
5654         });
5655         
5656         var tbody =  this.mainBody;
5657               
5658         if(ds.getCount() > 0){
5659             ds.data.each(function(d,rowIndex){
5660                 var row =  this.renderRow(cm, ds, rowIndex);
5661                 
5662                 tbody.createChild(row);
5663                 
5664                 var _this = this;
5665                 
5666                 if(row.cellObjects.length){
5667                     Roo.each(row.cellObjects, function(r){
5668                         _this.renderCellObject(r);
5669                     })
5670                 }
5671                 
5672             }, this);
5673         }
5674         
5675         Roo.each(this.el.select('tbody td', true).elements, function(e){
5676             e.on('mouseover', _this.onMouseover, _this);
5677         });
5678         
5679         Roo.each(this.el.select('tbody td', true).elements, function(e){
5680             e.on('mouseout', _this.onMouseout, _this);
5681         });
5682         this.fireEvent('rowsrendered', this);
5683         //if(this.loadMask){
5684         //    this.maskEl.hide();
5685         //}
5686     },
5687     
5688     
5689     onUpdate : function(ds,record)
5690     {
5691         this.refreshRow(record);
5692     },
5693     
5694     onRemove : function(ds, record, index, isUpdate){
5695         if(isUpdate !== true){
5696             this.fireEvent("beforerowremoved", this, index, record);
5697         }
5698         var bt = this.mainBody.dom;
5699         
5700         var rows = this.el.select('tbody > tr', true).elements;
5701         
5702         if(typeof(rows[index]) != 'undefined'){
5703             bt.removeChild(rows[index].dom);
5704         }
5705         
5706 //        if(bt.rows[index]){
5707 //            bt.removeChild(bt.rows[index]);
5708 //        }
5709         
5710         if(isUpdate !== true){
5711             //this.stripeRows(index);
5712             //this.syncRowHeights(index, index);
5713             //this.layout();
5714             this.fireEvent("rowremoved", this, index, record);
5715         }
5716     },
5717     
5718     onAdd : function(ds, records, rowIndex)
5719     {
5720         //Roo.log('on Add called');
5721         // - note this does not handle multiple adding very well..
5722         var bt = this.mainBody.dom;
5723         for (var i =0 ; i < records.length;i++) {
5724             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5725             //Roo.log(records[i]);
5726             //Roo.log(this.store.getAt(rowIndex+i));
5727             this.insertRow(this.store, rowIndex + i, false);
5728             return;
5729         }
5730         
5731     },
5732     
5733     
5734     refreshRow : function(record){
5735         var ds = this.store, index;
5736         if(typeof record == 'number'){
5737             index = record;
5738             record = ds.getAt(index);
5739         }else{
5740             index = ds.indexOf(record);
5741         }
5742         this.insertRow(ds, index, true);
5743         this.onRemove(ds, record, index+1, true);
5744         //this.syncRowHeights(index, index);
5745         //this.layout();
5746         this.fireEvent("rowupdated", this, index, record);
5747     },
5748     
5749     insertRow : function(dm, rowIndex, isUpdate){
5750         
5751         if(!isUpdate){
5752             this.fireEvent("beforerowsinserted", this, rowIndex);
5753         }
5754             //var s = this.getScrollState();
5755         var row = this.renderRow(this.cm, this.store, rowIndex);
5756         // insert before rowIndex..
5757         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5758         
5759         var _this = this;
5760                 
5761         if(row.cellObjects.length){
5762             Roo.each(row.cellObjects, function(r){
5763                 _this.renderCellObject(r);
5764             })
5765         }
5766             
5767         if(!isUpdate){
5768             this.fireEvent("rowsinserted", this, rowIndex);
5769             //this.syncRowHeights(firstRow, lastRow);
5770             //this.stripeRows(firstRow);
5771             //this.layout();
5772         }
5773         
5774     },
5775     
5776     
5777     getRowDom : function(rowIndex)
5778     {
5779         var rows = this.el.select('tbody > tr', true).elements;
5780         
5781         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
5782         
5783     },
5784     // returns the object tree for a tr..
5785   
5786     
5787     renderRow : function(cm, ds, rowIndex) 
5788     {
5789         
5790         var d = ds.getAt(rowIndex);
5791         
5792         var row = {
5793             tag : 'tr',
5794             cn : []
5795         };
5796             
5797         var cellObjects = [];
5798         
5799         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5800             var config = cm.config[i];
5801             
5802             var renderer = cm.getRenderer(i);
5803             var value = '';
5804             var id = false;
5805             
5806             if(typeof(renderer) !== 'undefined'){
5807                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5808             }
5809             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5810             // and are rendered into the cells after the row is rendered - using the id for the element.
5811             
5812             if(typeof(value) === 'object'){
5813                 id = Roo.id();
5814                 cellObjects.push({
5815                     container : id,
5816                     cfg : value 
5817                 })
5818             }
5819             
5820             var rowcfg = {
5821                 record: d,
5822                 rowIndex : rowIndex,
5823                 colIndex : i,
5824                 rowClass : ''
5825             }
5826
5827             this.fireEvent('rowclass', this, rowcfg);
5828             
5829             var td = {
5830                 tag: 'td',
5831                 cls : rowcfg.rowClass,
5832                 style: '',
5833                 html: (typeof(value) === 'object') ? '' : value
5834             };
5835             
5836             if (id) {
5837                 td.id = id;
5838             }
5839             
5840             if(typeof(config.colspan) != 'undefined'){
5841                 td.colspan = config.colspan;
5842             }
5843             
5844             if(typeof(config.hidden) != 'undefined' && config.hidden){
5845                 td.style += ' display:none;';
5846             }
5847             
5848             if(typeof(config.align) != 'undefined' && config.align.length){
5849                 td.style += ' text-align:' + config.align + ';';
5850             }
5851             
5852             if(typeof(config.width) != 'undefined'){
5853                 td.style += ' width:' +  config.width + 'px;';
5854             }
5855             
5856             if(typeof(config.cursor) != 'undefined'){
5857                 td.style += ' cursor:' +  config.cursor + ';';
5858             }
5859             
5860             if(typeof(config.cls) != 'undefined'){
5861                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
5862             }
5863              
5864             row.cn.push(td);
5865            
5866         }
5867         
5868         row.cellObjects = cellObjects;
5869         
5870         return row;
5871           
5872     },
5873     
5874     
5875     
5876     onBeforeLoad : function()
5877     {
5878         //Roo.log('ds onBeforeLoad');
5879         
5880         //this.clear();
5881         
5882         //if(this.loadMask){
5883         //    this.maskEl.show();
5884         //}
5885     },
5886      /**
5887      * Remove all rows
5888      */
5889     clear : function()
5890     {
5891         this.el.select('tbody', true).first().dom.innerHTML = '';
5892     },
5893     /**
5894      * Show or hide a row.
5895      * @param {Number} rowIndex to show or hide
5896      * @param {Boolean} state hide
5897      */
5898     setRowVisibility : function(rowIndex, state)
5899     {
5900         var bt = this.mainBody.dom;
5901         
5902         var rows = this.el.select('tbody > tr', true).elements;
5903         
5904         if(typeof(rows[rowIndex]) == 'undefined'){
5905             return;
5906         }
5907         rows[rowIndex].dom.style.display = state ? '' : 'none';
5908     },
5909     
5910     
5911     getSelectionModel : function(){
5912         if(!this.selModel){
5913             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5914         }
5915         return this.selModel;
5916     },
5917     /*
5918      * Render the Roo.bootstrap object from renderder
5919      */
5920     renderCellObject : function(r)
5921     {
5922         var _this = this;
5923         
5924         var t = r.cfg.render(r.container);
5925         
5926         if(r.cfg.cn){
5927             Roo.each(r.cfg.cn, function(c){
5928                 var child = {
5929                     container: t.getChildContainer(),
5930                     cfg: c
5931                 }
5932                 _this.renderCellObject(child);
5933             })
5934         }
5935     },
5936     
5937     getRowIndex : function(row)
5938     {
5939         var rowIndex = -1;
5940         
5941         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
5942             if(el != row){
5943                 return;
5944             }
5945             
5946             rowIndex = index;
5947         });
5948         
5949         return rowIndex;
5950     }
5951    
5952 });
5953
5954  
5955
5956  /*
5957  * - LGPL
5958  *
5959  * table cell
5960  * 
5961  */
5962
5963 /**
5964  * @class Roo.bootstrap.TableCell
5965  * @extends Roo.bootstrap.Component
5966  * Bootstrap TableCell class
5967  * @cfg {String} html cell contain text
5968  * @cfg {String} cls cell class
5969  * @cfg {String} tag cell tag (td|th) default td
5970  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5971  * @cfg {String} align Aligns the content in a cell
5972  * @cfg {String} axis Categorizes cells
5973  * @cfg {String} bgcolor Specifies the background color of a cell
5974  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5975  * @cfg {Number} colspan Specifies the number of columns a cell should span
5976  * @cfg {String} headers Specifies one or more header cells a cell is related to
5977  * @cfg {Number} height Sets the height of a cell
5978  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5979  * @cfg {Number} rowspan Sets the number of rows a cell should span
5980  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5981  * @cfg {String} valign Vertical aligns the content in a cell
5982  * @cfg {Number} width Specifies the width of a cell
5983  * 
5984  * @constructor
5985  * Create a new TableCell
5986  * @param {Object} config The config object
5987  */
5988
5989 Roo.bootstrap.TableCell = function(config){
5990     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5991 };
5992
5993 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5994     
5995     html: false,
5996     cls: false,
5997     tag: false,
5998     abbr: false,
5999     align: false,
6000     axis: false,
6001     bgcolor: false,
6002     charoff: false,
6003     colspan: false,
6004     headers: false,
6005     height: false,
6006     nowrap: false,
6007     rowspan: false,
6008     scope: false,
6009     valign: false,
6010     width: false,
6011     
6012     
6013     getAutoCreate : function(){
6014         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6015         
6016         cfg = {
6017             tag: 'td'
6018         }
6019         
6020         if(this.tag){
6021             cfg.tag = this.tag;
6022         }
6023         
6024         if (this.html) {
6025             cfg.html=this.html
6026         }
6027         if (this.cls) {
6028             cfg.cls=this.cls
6029         }
6030         if (this.abbr) {
6031             cfg.abbr=this.abbr
6032         }
6033         if (this.align) {
6034             cfg.align=this.align
6035         }
6036         if (this.axis) {
6037             cfg.axis=this.axis
6038         }
6039         if (this.bgcolor) {
6040             cfg.bgcolor=this.bgcolor
6041         }
6042         if (this.charoff) {
6043             cfg.charoff=this.charoff
6044         }
6045         if (this.colspan) {
6046             cfg.colspan=this.colspan
6047         }
6048         if (this.headers) {
6049             cfg.headers=this.headers
6050         }
6051         if (this.height) {
6052             cfg.height=this.height
6053         }
6054         if (this.nowrap) {
6055             cfg.nowrap=this.nowrap
6056         }
6057         if (this.rowspan) {
6058             cfg.rowspan=this.rowspan
6059         }
6060         if (this.scope) {
6061             cfg.scope=this.scope
6062         }
6063         if (this.valign) {
6064             cfg.valign=this.valign
6065         }
6066         if (this.width) {
6067             cfg.width=this.width
6068         }
6069         
6070         
6071         return cfg;
6072     }
6073    
6074 });
6075
6076  
6077
6078  /*
6079  * - LGPL
6080  *
6081  * table row
6082  * 
6083  */
6084
6085 /**
6086  * @class Roo.bootstrap.TableRow
6087  * @extends Roo.bootstrap.Component
6088  * Bootstrap TableRow class
6089  * @cfg {String} cls row class
6090  * @cfg {String} align Aligns the content in a table row
6091  * @cfg {String} bgcolor Specifies a background color for a table row
6092  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6093  * @cfg {String} valign Vertical aligns the content in a table row
6094  * 
6095  * @constructor
6096  * Create a new TableRow
6097  * @param {Object} config The config object
6098  */
6099
6100 Roo.bootstrap.TableRow = function(config){
6101     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6102 };
6103
6104 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6105     
6106     cls: false,
6107     align: false,
6108     bgcolor: false,
6109     charoff: false,
6110     valign: false,
6111     
6112     getAutoCreate : function(){
6113         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6114         
6115         cfg = {
6116             tag: 'tr'
6117         }
6118             
6119         if(this.cls){
6120             cfg.cls = this.cls;
6121         }
6122         if(this.align){
6123             cfg.align = this.align;
6124         }
6125         if(this.bgcolor){
6126             cfg.bgcolor = this.bgcolor;
6127         }
6128         if(this.charoff){
6129             cfg.charoff = this.charoff;
6130         }
6131         if(this.valign){
6132             cfg.valign = this.valign;
6133         }
6134         
6135         return cfg;
6136     }
6137    
6138 });
6139
6140  
6141
6142  /*
6143  * - LGPL
6144  *
6145  * table body
6146  * 
6147  */
6148
6149 /**
6150  * @class Roo.bootstrap.TableBody
6151  * @extends Roo.bootstrap.Component
6152  * Bootstrap TableBody class
6153  * @cfg {String} cls element class
6154  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6155  * @cfg {String} align Aligns the content inside the element
6156  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6157  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6158  * 
6159  * @constructor
6160  * Create a new TableBody
6161  * @param {Object} config The config object
6162  */
6163
6164 Roo.bootstrap.TableBody = function(config){
6165     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6166 };
6167
6168 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6169     
6170     cls: false,
6171     tag: false,
6172     align: false,
6173     charoff: false,
6174     valign: false,
6175     
6176     getAutoCreate : function(){
6177         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6178         
6179         cfg = {
6180             tag: 'tbody'
6181         }
6182             
6183         if (this.cls) {
6184             cfg.cls=this.cls
6185         }
6186         if(this.tag){
6187             cfg.tag = this.tag;
6188         }
6189         
6190         if(this.align){
6191             cfg.align = this.align;
6192         }
6193         if(this.charoff){
6194             cfg.charoff = this.charoff;
6195         }
6196         if(this.valign){
6197             cfg.valign = this.valign;
6198         }
6199         
6200         return cfg;
6201     }
6202     
6203     
6204 //    initEvents : function()
6205 //    {
6206 //        
6207 //        if(!this.store){
6208 //            return;
6209 //        }
6210 //        
6211 //        this.store = Roo.factory(this.store, Roo.data);
6212 //        this.store.on('load', this.onLoad, this);
6213 //        
6214 //        this.store.load();
6215 //        
6216 //    },
6217 //    
6218 //    onLoad: function () 
6219 //    {   
6220 //        this.fireEvent('load', this);
6221 //    }
6222 //    
6223 //   
6224 });
6225
6226  
6227
6228  /*
6229  * Based on:
6230  * Ext JS Library 1.1.1
6231  * Copyright(c) 2006-2007, Ext JS, LLC.
6232  *
6233  * Originally Released Under LGPL - original licence link has changed is not relivant.
6234  *
6235  * Fork - LGPL
6236  * <script type="text/javascript">
6237  */
6238
6239 // as we use this in bootstrap.
6240 Roo.namespace('Roo.form');
6241  /**
6242  * @class Roo.form.Action
6243  * Internal Class used to handle form actions
6244  * @constructor
6245  * @param {Roo.form.BasicForm} el The form element or its id
6246  * @param {Object} config Configuration options
6247  */
6248
6249  
6250  
6251 // define the action interface
6252 Roo.form.Action = function(form, options){
6253     this.form = form;
6254     this.options = options || {};
6255 };
6256 /**
6257  * Client Validation Failed
6258  * @const 
6259  */
6260 Roo.form.Action.CLIENT_INVALID = 'client';
6261 /**
6262  * Server Validation Failed
6263  * @const 
6264  */
6265 Roo.form.Action.SERVER_INVALID = 'server';
6266  /**
6267  * Connect to Server Failed
6268  * @const 
6269  */
6270 Roo.form.Action.CONNECT_FAILURE = 'connect';
6271 /**
6272  * Reading Data from Server Failed
6273  * @const 
6274  */
6275 Roo.form.Action.LOAD_FAILURE = 'load';
6276
6277 Roo.form.Action.prototype = {
6278     type : 'default',
6279     failureType : undefined,
6280     response : undefined,
6281     result : undefined,
6282
6283     // interface method
6284     run : function(options){
6285
6286     },
6287
6288     // interface method
6289     success : function(response){
6290
6291     },
6292
6293     // interface method
6294     handleResponse : function(response){
6295
6296     },
6297
6298     // default connection failure
6299     failure : function(response){
6300         
6301         this.response = response;
6302         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6303         this.form.afterAction(this, false);
6304     },
6305
6306     processResponse : function(response){
6307         this.response = response;
6308         if(!response.responseText){
6309             return true;
6310         }
6311         this.result = this.handleResponse(response);
6312         return this.result;
6313     },
6314
6315     // utility functions used internally
6316     getUrl : function(appendParams){
6317         var url = this.options.url || this.form.url || this.form.el.dom.action;
6318         if(appendParams){
6319             var p = this.getParams();
6320             if(p){
6321                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6322             }
6323         }
6324         return url;
6325     },
6326
6327     getMethod : function(){
6328         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6329     },
6330
6331     getParams : function(){
6332         var bp = this.form.baseParams;
6333         var p = this.options.params;
6334         if(p){
6335             if(typeof p == "object"){
6336                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6337             }else if(typeof p == 'string' && bp){
6338                 p += '&' + Roo.urlEncode(bp);
6339             }
6340         }else if(bp){
6341             p = Roo.urlEncode(bp);
6342         }
6343         return p;
6344     },
6345
6346     createCallback : function(){
6347         return {
6348             success: this.success,
6349             failure: this.failure,
6350             scope: this,
6351             timeout: (this.form.timeout*1000),
6352             upload: this.form.fileUpload ? this.success : undefined
6353         };
6354     }
6355 };
6356
6357 Roo.form.Action.Submit = function(form, options){
6358     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6359 };
6360
6361 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6362     type : 'submit',
6363
6364     haveProgress : false,
6365     uploadComplete : false,
6366     
6367     // uploadProgress indicator.
6368     uploadProgress : function()
6369     {
6370         if (!this.form.progressUrl) {
6371             return;
6372         }
6373         
6374         if (!this.haveProgress) {
6375             Roo.MessageBox.progress("Uploading", "Uploading");
6376         }
6377         if (this.uploadComplete) {
6378            Roo.MessageBox.hide();
6379            return;
6380         }
6381         
6382         this.haveProgress = true;
6383    
6384         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6385         
6386         var c = new Roo.data.Connection();
6387         c.request({
6388             url : this.form.progressUrl,
6389             params: {
6390                 id : uid
6391             },
6392             method: 'GET',
6393             success : function(req){
6394                //console.log(data);
6395                 var rdata = false;
6396                 var edata;
6397                 try  {
6398                    rdata = Roo.decode(req.responseText)
6399                 } catch (e) {
6400                     Roo.log("Invalid data from server..");
6401                     Roo.log(edata);
6402                     return;
6403                 }
6404                 if (!rdata || !rdata.success) {
6405                     Roo.log(rdata);
6406                     Roo.MessageBox.alert(Roo.encode(rdata));
6407                     return;
6408                 }
6409                 var data = rdata.data;
6410                 
6411                 if (this.uploadComplete) {
6412                    Roo.MessageBox.hide();
6413                    return;
6414                 }
6415                    
6416                 if (data){
6417                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6418                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6419                     );
6420                 }
6421                 this.uploadProgress.defer(2000,this);
6422             },
6423        
6424             failure: function(data) {
6425                 Roo.log('progress url failed ');
6426                 Roo.log(data);
6427             },
6428             scope : this
6429         });
6430            
6431     },
6432     
6433     
6434     run : function()
6435     {
6436         // run get Values on the form, so it syncs any secondary forms.
6437         this.form.getValues();
6438         
6439         var o = this.options;
6440         var method = this.getMethod();
6441         var isPost = method == 'POST';
6442         if(o.clientValidation === false || this.form.isValid()){
6443             
6444             if (this.form.progressUrl) {
6445                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6446                     (new Date() * 1) + '' + Math.random());
6447                     
6448             } 
6449             
6450             
6451             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6452                 form:this.form.el.dom,
6453                 url:this.getUrl(!isPost),
6454                 method: method,
6455                 params:isPost ? this.getParams() : null,
6456                 isUpload: this.form.fileUpload
6457             }));
6458             
6459             this.uploadProgress();
6460
6461         }else if (o.clientValidation !== false){ // client validation failed
6462             this.failureType = Roo.form.Action.CLIENT_INVALID;
6463             this.form.afterAction(this, false);
6464         }
6465     },
6466
6467     success : function(response)
6468     {
6469         this.uploadComplete= true;
6470         if (this.haveProgress) {
6471             Roo.MessageBox.hide();
6472         }
6473         
6474         
6475         var result = this.processResponse(response);
6476         if(result === true || result.success){
6477             this.form.afterAction(this, true);
6478             return;
6479         }
6480         if(result.errors){
6481             this.form.markInvalid(result.errors);
6482             this.failureType = Roo.form.Action.SERVER_INVALID;
6483         }
6484         this.form.afterAction(this, false);
6485     },
6486     failure : function(response)
6487     {
6488         this.uploadComplete= true;
6489         if (this.haveProgress) {
6490             Roo.MessageBox.hide();
6491         }
6492         
6493         this.response = response;
6494         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6495         this.form.afterAction(this, false);
6496     },
6497     
6498     handleResponse : function(response){
6499         if(this.form.errorReader){
6500             var rs = this.form.errorReader.read(response);
6501             var errors = [];
6502             if(rs.records){
6503                 for(var i = 0, len = rs.records.length; i < len; i++) {
6504                     var r = rs.records[i];
6505                     errors[i] = r.data;
6506                 }
6507             }
6508             if(errors.length < 1){
6509                 errors = null;
6510             }
6511             return {
6512                 success : rs.success,
6513                 errors : errors
6514             };
6515         }
6516         var ret = false;
6517         try {
6518             ret = Roo.decode(response.responseText);
6519         } catch (e) {
6520             ret = {
6521                 success: false,
6522                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6523                 errors : []
6524             };
6525         }
6526         return ret;
6527         
6528     }
6529 });
6530
6531
6532 Roo.form.Action.Load = function(form, options){
6533     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6534     this.reader = this.form.reader;
6535 };
6536
6537 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6538     type : 'load',
6539
6540     run : function(){
6541         
6542         Roo.Ajax.request(Roo.apply(
6543                 this.createCallback(), {
6544                     method:this.getMethod(),
6545                     url:this.getUrl(false),
6546                     params:this.getParams()
6547         }));
6548     },
6549
6550     success : function(response){
6551         
6552         var result = this.processResponse(response);
6553         if(result === true || !result.success || !result.data){
6554             this.failureType = Roo.form.Action.LOAD_FAILURE;
6555             this.form.afterAction(this, false);
6556             return;
6557         }
6558         this.form.clearInvalid();
6559         this.form.setValues(result.data);
6560         this.form.afterAction(this, true);
6561     },
6562
6563     handleResponse : function(response){
6564         if(this.form.reader){
6565             var rs = this.form.reader.read(response);
6566             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6567             return {
6568                 success : rs.success,
6569                 data : data
6570             };
6571         }
6572         return Roo.decode(response.responseText);
6573     }
6574 });
6575
6576 Roo.form.Action.ACTION_TYPES = {
6577     'load' : Roo.form.Action.Load,
6578     'submit' : Roo.form.Action.Submit
6579 };/*
6580  * - LGPL
6581  *
6582  * form
6583  * 
6584  */
6585
6586 /**
6587  * @class Roo.bootstrap.Form
6588  * @extends Roo.bootstrap.Component
6589  * Bootstrap Form class
6590  * @cfg {String} method  GET | POST (default POST)
6591  * @cfg {String} labelAlign top | left (default top)
6592  * @cfg {String} align left  | right - for navbars
6593  * @cfg {Boolean} loadMask load mask when submit (default true)
6594
6595  * 
6596  * @constructor
6597  * Create a new Form
6598  * @param {Object} config The config object
6599  */
6600
6601
6602 Roo.bootstrap.Form = function(config){
6603     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6604     this.addEvents({
6605         /**
6606          * @event clientvalidation
6607          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6608          * @param {Form} this
6609          * @param {Boolean} valid true if the form has passed client-side validation
6610          */
6611         clientvalidation: true,
6612         /**
6613          * @event beforeaction
6614          * Fires before any action is performed. Return false to cancel the action.
6615          * @param {Form} this
6616          * @param {Action} action The action to be performed
6617          */
6618         beforeaction: true,
6619         /**
6620          * @event actionfailed
6621          * Fires when an action fails.
6622          * @param {Form} this
6623          * @param {Action} action The action that failed
6624          */
6625         actionfailed : true,
6626         /**
6627          * @event actioncomplete
6628          * Fires when an action is completed.
6629          * @param {Form} this
6630          * @param {Action} action The action that completed
6631          */
6632         actioncomplete : true
6633     });
6634     
6635 };
6636
6637 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6638       
6639      /**
6640      * @cfg {String} method
6641      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6642      */
6643     method : 'POST',
6644     /**
6645      * @cfg {String} url
6646      * The URL to use for form actions if one isn't supplied in the action options.
6647      */
6648     /**
6649      * @cfg {Boolean} fileUpload
6650      * Set to true if this form is a file upload.
6651      */
6652      
6653     /**
6654      * @cfg {Object} baseParams
6655      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6656      */
6657       
6658     /**
6659      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6660      */
6661     timeout: 30,
6662     /**
6663      * @cfg {Sting} align (left|right) for navbar forms
6664      */
6665     align : 'left',
6666
6667     // private
6668     activeAction : null,
6669  
6670     /**
6671      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6672      * element by passing it or its id or mask the form itself by passing in true.
6673      * @type Mixed
6674      */
6675     waitMsgTarget : false,
6676     
6677     loadMask : true,
6678     
6679     getAutoCreate : function(){
6680         
6681         var cfg = {
6682             tag: 'form',
6683             method : this.method || 'POST',
6684             id : this.id || Roo.id(),
6685             cls : ''
6686         }
6687         if (this.parent().xtype.match(/^Nav/)) {
6688             cfg.cls = 'navbar-form navbar-' + this.align;
6689             
6690         }
6691         
6692         if (this.labelAlign == 'left' ) {
6693             cfg.cls += ' form-horizontal';
6694         }
6695         
6696         
6697         return cfg;
6698     },
6699     initEvents : function()
6700     {
6701         this.el.on('submit', this.onSubmit, this);
6702         // this was added as random key presses on the form where triggering form submit.
6703         this.el.on('keypress', function(e) {
6704             if (e.getCharCode() != 13) {
6705                 return true;
6706             }
6707             // we might need to allow it for textareas.. and some other items.
6708             // check e.getTarget().
6709             
6710             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6711                 return true;
6712             }
6713         
6714             Roo.log("keypress blocked");
6715             
6716             e.preventDefault();
6717             return false;
6718         });
6719         
6720     },
6721     // private
6722     onSubmit : function(e){
6723         e.stopEvent();
6724     },
6725     
6726      /**
6727      * Returns true if client-side validation on the form is successful.
6728      * @return Boolean
6729      */
6730     isValid : function(){
6731         var items = this.getItems();
6732         var valid = true;
6733         items.each(function(f){
6734            if(!f.validate()){
6735                valid = false;
6736                
6737            }
6738         });
6739         return valid;
6740     },
6741     /**
6742      * Returns true if any fields in this form have changed since their original load.
6743      * @return Boolean
6744      */
6745     isDirty : function(){
6746         var dirty = false;
6747         var items = this.getItems();
6748         items.each(function(f){
6749            if(f.isDirty()){
6750                dirty = true;
6751                return false;
6752            }
6753            return true;
6754         });
6755         return dirty;
6756     },
6757      /**
6758      * Performs a predefined action (submit or load) or custom actions you define on this form.
6759      * @param {String} actionName The name of the action type
6760      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6761      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6762      * accept other config options):
6763      * <pre>
6764 Property          Type             Description
6765 ----------------  ---------------  ----------------------------------------------------------------------------------
6766 url               String           The url for the action (defaults to the form's url)
6767 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6768 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6769 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6770                                    validate the form on the client (defaults to false)
6771      * </pre>
6772      * @return {BasicForm} this
6773      */
6774     doAction : function(action, options){
6775         if(typeof action == 'string'){
6776             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6777         }
6778         if(this.fireEvent('beforeaction', this, action) !== false){
6779             this.beforeAction(action);
6780             action.run.defer(100, action);
6781         }
6782         return this;
6783     },
6784     
6785     // private
6786     beforeAction : function(action){
6787         var o = action.options;
6788         
6789         if(this.loadMask){
6790             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6791         }
6792         // not really supported yet.. ??
6793         
6794         //if(this.waitMsgTarget === true){
6795         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6796         //}else if(this.waitMsgTarget){
6797         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6798         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6799         //}else {
6800         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6801        // }
6802          
6803     },
6804
6805     // private
6806     afterAction : function(action, success){
6807         this.activeAction = null;
6808         var o = action.options;
6809         
6810         //if(this.waitMsgTarget === true){
6811             this.el.unmask();
6812         //}else if(this.waitMsgTarget){
6813         //    this.waitMsgTarget.unmask();
6814         //}else{
6815         //    Roo.MessageBox.updateProgress(1);
6816         //    Roo.MessageBox.hide();
6817        // }
6818         // 
6819         if(success){
6820             if(o.reset){
6821                 this.reset();
6822             }
6823             Roo.callback(o.success, o.scope, [this, action]);
6824             this.fireEvent('actioncomplete', this, action);
6825             
6826         }else{
6827             
6828             // failure condition..
6829             // we have a scenario where updates need confirming.
6830             // eg. if a locking scenario exists..
6831             // we look for { errors : { needs_confirm : true }} in the response.
6832             if (
6833                 (typeof(action.result) != 'undefined')  &&
6834                 (typeof(action.result.errors) != 'undefined')  &&
6835                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6836            ){
6837                 var _t = this;
6838                 Roo.log("not supported yet");
6839                  /*
6840                 
6841                 Roo.MessageBox.confirm(
6842                     "Change requires confirmation",
6843                     action.result.errorMsg,
6844                     function(r) {
6845                         if (r != 'yes') {
6846                             return;
6847                         }
6848                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6849                     }
6850                     
6851                 );
6852                 */
6853                 
6854                 
6855                 return;
6856             }
6857             
6858             Roo.callback(o.failure, o.scope, [this, action]);
6859             // show an error message if no failed handler is set..
6860             if (!this.hasListener('actionfailed')) {
6861                 Roo.log("need to add dialog support");
6862                 /*
6863                 Roo.MessageBox.alert("Error",
6864                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6865                         action.result.errorMsg :
6866                         "Saving Failed, please check your entries or try again"
6867                 );
6868                 */
6869             }
6870             
6871             this.fireEvent('actionfailed', this, action);
6872         }
6873         
6874     },
6875     /**
6876      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6877      * @param {String} id The value to search for
6878      * @return Field
6879      */
6880     findField : function(id){
6881         var items = this.getItems();
6882         var field = items.get(id);
6883         if(!field){
6884              items.each(function(f){
6885                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6886                     field = f;
6887                     return false;
6888                 }
6889                 return true;
6890             });
6891         }
6892         return field || null;
6893     },
6894      /**
6895      * Mark fields in this form invalid in bulk.
6896      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6897      * @return {BasicForm} this
6898      */
6899     markInvalid : function(errors){
6900         if(errors instanceof Array){
6901             for(var i = 0, len = errors.length; i < len; i++){
6902                 var fieldError = errors[i];
6903                 var f = this.findField(fieldError.id);
6904                 if(f){
6905                     f.markInvalid(fieldError.msg);
6906                 }
6907             }
6908         }else{
6909             var field, id;
6910             for(id in errors){
6911                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6912                     field.markInvalid(errors[id]);
6913                 }
6914             }
6915         }
6916         //Roo.each(this.childForms || [], function (f) {
6917         //    f.markInvalid(errors);
6918         //});
6919         
6920         return this;
6921     },
6922
6923     /**
6924      * Set values for fields in this form in bulk.
6925      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6926      * @return {BasicForm} this
6927      */
6928     setValues : function(values){
6929         if(values instanceof Array){ // array of objects
6930             for(var i = 0, len = values.length; i < len; i++){
6931                 var v = values[i];
6932                 var f = this.findField(v.id);
6933                 if(f){
6934                     f.setValue(v.value);
6935                     if(this.trackResetOnLoad){
6936                         f.originalValue = f.getValue();
6937                     }
6938                 }
6939             }
6940         }else{ // object hash
6941             var field, id;
6942             for(id in values){
6943                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6944                     
6945                     if (field.setFromData && 
6946                         field.valueField && 
6947                         field.displayField &&
6948                         // combos' with local stores can 
6949                         // be queried via setValue()
6950                         // to set their value..
6951                         (field.store && !field.store.isLocal)
6952                         ) {
6953                         // it's a combo
6954                         var sd = { };
6955                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6956                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6957                         field.setFromData(sd);
6958                         
6959                     } else {
6960                         field.setValue(values[id]);
6961                     }
6962                     
6963                     
6964                     if(this.trackResetOnLoad){
6965                         field.originalValue = field.getValue();
6966                     }
6967                 }
6968             }
6969         }
6970          
6971         //Roo.each(this.childForms || [], function (f) {
6972         //    f.setValues(values);
6973         //});
6974                 
6975         return this;
6976     },
6977
6978     /**
6979      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6980      * they are returned as an array.
6981      * @param {Boolean} asString
6982      * @return {Object}
6983      */
6984     getValues : function(asString){
6985         //if (this.childForms) {
6986             // copy values from the child forms
6987         //    Roo.each(this.childForms, function (f) {
6988         //        this.setValues(f.getValues());
6989         //    }, this);
6990         //}
6991         
6992         
6993         
6994         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6995         if(asString === true){
6996             return fs;
6997         }
6998         return Roo.urlDecode(fs);
6999     },
7000     
7001     /**
7002      * Returns the fields in this form as an object with key/value pairs. 
7003      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7004      * @return {Object}
7005      */
7006     getFieldValues : function(with_hidden)
7007     {
7008         var items = this.getItems();
7009         var ret = {};
7010         items.each(function(f){
7011             if (!f.getName()) {
7012                 return;
7013             }
7014             var v = f.getValue();
7015             if (f.inputType =='radio') {
7016                 if (typeof(ret[f.getName()]) == 'undefined') {
7017                     ret[f.getName()] = ''; // empty..
7018                 }
7019                 
7020                 if (!f.el.dom.checked) {
7021                     return;
7022                     
7023                 }
7024                 v = f.el.dom.value;
7025                 
7026             }
7027             
7028             // not sure if this supported any more..
7029             if ((typeof(v) == 'object') && f.getRawValue) {
7030                 v = f.getRawValue() ; // dates..
7031             }
7032             // combo boxes where name != hiddenName...
7033             if (f.name != f.getName()) {
7034                 ret[f.name] = f.getRawValue();
7035             }
7036             ret[f.getName()] = v;
7037         });
7038         
7039         return ret;
7040     },
7041
7042     /**
7043      * Clears all invalid messages in this form.
7044      * @return {BasicForm} this
7045      */
7046     clearInvalid : function(){
7047         var items = this.getItems();
7048         
7049         items.each(function(f){
7050            f.clearInvalid();
7051         });
7052         
7053         
7054         
7055         return this;
7056     },
7057
7058     /**
7059      * Resets this form.
7060      * @return {BasicForm} this
7061      */
7062     reset : function(){
7063         var items = this.getItems();
7064         items.each(function(f){
7065             f.reset();
7066         });
7067         
7068         Roo.each(this.childForms || [], function (f) {
7069             f.reset();
7070         });
7071        
7072         
7073         return this;
7074     },
7075     getItems : function()
7076     {
7077         var r=new Roo.util.MixedCollection(false, function(o){
7078             return o.id || (o.id = Roo.id());
7079         });
7080         var iter = function(el) {
7081             if (el.inputEl) {
7082                 r.add(el);
7083             }
7084             if (!el.items) {
7085                 return;
7086             }
7087             Roo.each(el.items,function(e) {
7088                 iter(e);
7089             });
7090             
7091             
7092         };
7093         
7094         iter(this);
7095         return r;
7096         
7097         
7098         
7099         
7100     }
7101     
7102 });
7103
7104  
7105 /*
7106  * Based on:
7107  * Ext JS Library 1.1.1
7108  * Copyright(c) 2006-2007, Ext JS, LLC.
7109  *
7110  * Originally Released Under LGPL - original licence link has changed is not relivant.
7111  *
7112  * Fork - LGPL
7113  * <script type="text/javascript">
7114  */
7115 /**
7116  * @class Roo.form.VTypes
7117  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7118  * @singleton
7119  */
7120 Roo.form.VTypes = function(){
7121     // closure these in so they are only created once.
7122     var alpha = /^[a-zA-Z_]+$/;
7123     var alphanum = /^[a-zA-Z0-9_]+$/;
7124     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7125     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7126
7127     // All these messages and functions are configurable
7128     return {
7129         /**
7130          * The function used to validate email addresses
7131          * @param {String} value The email address
7132          */
7133         'email' : function(v){
7134             return email.test(v);
7135         },
7136         /**
7137          * The error text to display when the email validation function returns false
7138          * @type String
7139          */
7140         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7141         /**
7142          * The keystroke filter mask to be applied on email input
7143          * @type RegExp
7144          */
7145         'emailMask' : /[a-z0-9_\.\-@]/i,
7146
7147         /**
7148          * The function used to validate URLs
7149          * @param {String} value The URL
7150          */
7151         'url' : function(v){
7152             return url.test(v);
7153         },
7154         /**
7155          * The error text to display when the url validation function returns false
7156          * @type String
7157          */
7158         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7159         
7160         /**
7161          * The function used to validate alpha values
7162          * @param {String} value The value
7163          */
7164         'alpha' : function(v){
7165             return alpha.test(v);
7166         },
7167         /**
7168          * The error text to display when the alpha validation function returns false
7169          * @type String
7170          */
7171         'alphaText' : 'This field should only contain letters and _',
7172         /**
7173          * The keystroke filter mask to be applied on alpha input
7174          * @type RegExp
7175          */
7176         'alphaMask' : /[a-z_]/i,
7177
7178         /**
7179          * The function used to validate alphanumeric values
7180          * @param {String} value The value
7181          */
7182         'alphanum' : function(v){
7183             return alphanum.test(v);
7184         },
7185         /**
7186          * The error text to display when the alphanumeric validation function returns false
7187          * @type String
7188          */
7189         'alphanumText' : 'This field should only contain letters, numbers and _',
7190         /**
7191          * The keystroke filter mask to be applied on alphanumeric input
7192          * @type RegExp
7193          */
7194         'alphanumMask' : /[a-z0-9_]/i
7195     };
7196 }();/*
7197  * - LGPL
7198  *
7199  * Input
7200  * 
7201  */
7202
7203 /**
7204  * @class Roo.bootstrap.Input
7205  * @extends Roo.bootstrap.Component
7206  * Bootstrap Input class
7207  * @cfg {Boolean} disabled is it disabled
7208  * @cfg {String} fieldLabel - the label associated
7209  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7210  * @cfg {String} name name of the input
7211  * @cfg {string} fieldLabel - the label associated
7212  * @cfg {string}  inputType - input / file submit ...
7213  * @cfg {string} placeholder - placeholder to put in text.
7214  * @cfg {string}  before - input group add on before
7215  * @cfg {string} after - input group add on after
7216  * @cfg {string} size - (lg|sm) or leave empty..
7217  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7218  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7219  * @cfg {Number} md colspan out of 12 for computer-sized screens
7220  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7221  * @cfg {string} value default value of the input
7222  * @cfg {Number} labelWidth set the width of label (0-12)
7223  * @cfg {String} labelAlign (top|left)
7224  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7225  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7226
7227  * @cfg {String} align (left|center|right) Default left
7228  * 
7229  * 
7230  * 
7231  * @constructor
7232  * Create a new Input
7233  * @param {Object} config The config object
7234  */
7235
7236 Roo.bootstrap.Input = function(config){
7237     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7238    
7239         this.addEvents({
7240             /**
7241              * @event focus
7242              * Fires when this field receives input focus.
7243              * @param {Roo.form.Field} this
7244              */
7245             focus : true,
7246             /**
7247              * @event blur
7248              * Fires when this field loses input focus.
7249              * @param {Roo.form.Field} this
7250              */
7251             blur : true,
7252             /**
7253              * @event specialkey
7254              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7255              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7256              * @param {Roo.form.Field} this
7257              * @param {Roo.EventObject} e The event object
7258              */
7259             specialkey : true,
7260             /**
7261              * @event change
7262              * Fires just before the field blurs if the field value has changed.
7263              * @param {Roo.form.Field} this
7264              * @param {Mixed} newValue The new value
7265              * @param {Mixed} oldValue The original value
7266              */
7267             change : true,
7268             /**
7269              * @event invalid
7270              * Fires after the field has been marked as invalid.
7271              * @param {Roo.form.Field} this
7272              * @param {String} msg The validation message
7273              */
7274             invalid : true,
7275             /**
7276              * @event valid
7277              * Fires after the field has been validated with no errors.
7278              * @param {Roo.form.Field} this
7279              */
7280             valid : true,
7281              /**
7282              * @event keyup
7283              * Fires after the key up
7284              * @param {Roo.form.Field} this
7285              * @param {Roo.EventObject}  e The event Object
7286              */
7287             keyup : true
7288         });
7289 };
7290
7291 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7292      /**
7293      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7294       automatic validation (defaults to "keyup").
7295      */
7296     validationEvent : "keyup",
7297      /**
7298      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7299      */
7300     validateOnBlur : true,
7301     /**
7302      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7303      */
7304     validationDelay : 250,
7305      /**
7306      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7307      */
7308     focusClass : "x-form-focus",  // not needed???
7309     
7310        
7311     /**
7312      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7313      */
7314     invalidClass : "has-warning",
7315     
7316     /**
7317      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7318      */
7319     validClass : "has-success",
7320     
7321     /**
7322      * @cfg {Boolean} hasFeedback (true|false) default true
7323      */
7324     hasFeedback : true,
7325     
7326     /**
7327      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7328      */
7329     invalidFeedbackClass : "glyphicon-warning-sign",
7330     
7331     /**
7332      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7333      */
7334     validFeedbackClass : "glyphicon-ok",
7335     
7336     /**
7337      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7338      */
7339     selectOnFocus : false,
7340     
7341      /**
7342      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7343      */
7344     maskRe : null,
7345        /**
7346      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7347      */
7348     vtype : null,
7349     
7350       /**
7351      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7352      */
7353     disableKeyFilter : false,
7354     
7355        /**
7356      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7357      */
7358     disabled : false,
7359      /**
7360      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7361      */
7362     allowBlank : true,
7363     /**
7364      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7365      */
7366     blankText : "This field is required",
7367     
7368      /**
7369      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7370      */
7371     minLength : 0,
7372     /**
7373      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7374      */
7375     maxLength : Number.MAX_VALUE,
7376     /**
7377      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7378      */
7379     minLengthText : "The minimum length for this field is {0}",
7380     /**
7381      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7382      */
7383     maxLengthText : "The maximum length for this field is {0}",
7384   
7385     
7386     /**
7387      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7388      * If available, this function will be called only after the basic validators all return true, and will be passed the
7389      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7390      */
7391     validator : null,
7392     /**
7393      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7394      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7395      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7396      */
7397     regex : null,
7398     /**
7399      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7400      */
7401     regexText : "",
7402     
7403     autocomplete: false,
7404     
7405     
7406     fieldLabel : '',
7407     inputType : 'text',
7408     
7409     name : false,
7410     placeholder: false,
7411     before : false,
7412     after : false,
7413     size : false,
7414     hasFocus : false,
7415     preventMark: false,
7416     isFormField : true,
7417     value : '',
7418     labelWidth : 2,
7419     labelAlign : false,
7420     readOnly : false,
7421     align : false,
7422     formatedValue : false,
7423     
7424     parentLabelAlign : function()
7425     {
7426         var parent = this;
7427         while (parent.parent()) {
7428             parent = parent.parent();
7429             if (typeof(parent.labelAlign) !='undefined') {
7430                 return parent.labelAlign;
7431             }
7432         }
7433         return 'left';
7434         
7435     },
7436     
7437     getAutoCreate : function(){
7438         
7439         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7440         
7441         var id = Roo.id();
7442         
7443         var cfg = {};
7444         
7445         if(this.inputType != 'hidden'){
7446             cfg.cls = 'form-group' //input-group
7447         }
7448         
7449         var input =  {
7450             tag: 'input',
7451             id : id,
7452             type : this.inputType,
7453             value : this.value,
7454             cls : 'form-control',
7455             placeholder : this.placeholder || '',
7456             autocomplete : this.autocomplete || 'new-password'
7457         };
7458         
7459         
7460         if(this.align){
7461             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7462         }
7463         
7464         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7465             input.maxLength = this.maxLength;
7466         }
7467         
7468         if (this.disabled) {
7469             input.disabled=true;
7470         }
7471         
7472         if (this.readOnly) {
7473             input.readonly=true;
7474         }
7475         
7476         if (this.name) {
7477             input.name = this.name;
7478         }
7479         if (this.size) {
7480             input.cls += ' input-' + this.size;
7481         }
7482         var settings=this;
7483         ['xs','sm','md','lg'].map(function(size){
7484             if (settings[size]) {
7485                 cfg.cls += ' col-' + size + '-' + settings[size];
7486             }
7487         });
7488         
7489         var inputblock = input;
7490         
7491         var feedback = {
7492             tag: 'span',
7493             cls: 'glyphicon form-control-feedback'
7494         };
7495             
7496         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7497             
7498             inputblock = {
7499                 cls : 'has-feedback',
7500                 cn :  [
7501                     input,
7502                     feedback
7503                 ] 
7504             };  
7505         }
7506         
7507         if (this.before || this.after) {
7508             
7509             inputblock = {
7510                 cls : 'input-group',
7511                 cn :  [] 
7512             };
7513             
7514             if (this.before && typeof(this.before) == 'string') {
7515                 
7516                 inputblock.cn.push({
7517                     tag :'span',
7518                     cls : 'roo-input-before input-group-addon',
7519                     html : this.before
7520                 });
7521             }
7522             if (this.before && typeof(this.before) == 'object') {
7523                 this.before = Roo.factory(this.before);
7524                 Roo.log(this.before);
7525                 inputblock.cn.push({
7526                     tag :'span',
7527                     cls : 'roo-input-before input-group-' +
7528                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7529                 });
7530             }
7531             
7532             inputblock.cn.push(input);
7533             
7534             if (this.after && typeof(this.after) == 'string') {
7535                 inputblock.cn.push({
7536                     tag :'span',
7537                     cls : 'roo-input-after input-group-addon',
7538                     html : this.after
7539                 });
7540             }
7541             if (this.after && typeof(this.after) == 'object') {
7542                 this.after = Roo.factory(this.after);
7543                 Roo.log(this.after);
7544                 inputblock.cn.push({
7545                     tag :'span',
7546                     cls : 'roo-input-after input-group-' +
7547                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7548                 });
7549             }
7550             
7551             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7552                 inputblock.cls += ' has-feedback';
7553                 inputblock.cn.push(feedback);
7554             }
7555         };
7556         
7557         if (align ==='left' && this.fieldLabel.length) {
7558                 Roo.log("left and has label");
7559                 cfg.cn = [
7560                     
7561                     {
7562                         tag: 'label',
7563                         'for' :  id,
7564                         cls : 'control-label col-sm-' + this.labelWidth,
7565                         html : this.fieldLabel
7566                         
7567                     },
7568                     {
7569                         cls : "col-sm-" + (12 - this.labelWidth), 
7570                         cn: [
7571                             inputblock
7572                         ]
7573                     }
7574                     
7575                 ];
7576         } else if ( this.fieldLabel.length) {
7577                 Roo.log(" label");
7578                  cfg.cn = [
7579                    
7580                     {
7581                         tag: 'label',
7582                         //cls : 'input-group-addon',
7583                         html : this.fieldLabel
7584                         
7585                     },
7586                     
7587                     inputblock
7588                     
7589                 ];
7590
7591         } else {
7592             
7593                 Roo.log(" no label && no align");
7594                 cfg.cn = [
7595                     
7596                         inputblock
7597                     
7598                 ];
7599                 
7600                 
7601         };
7602         Roo.log('input-parentType: ' + this.parentType);
7603         
7604         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7605            cfg.cls += ' navbar-form';
7606            Roo.log(cfg);
7607         }
7608         
7609         return cfg;
7610         
7611     },
7612     /**
7613      * return the real input element.
7614      */
7615     inputEl: function ()
7616     {
7617         return this.el.select('input.form-control',true).first();
7618     },
7619     
7620     tooltipEl : function()
7621     {
7622         return this.inputEl();
7623     },
7624     
7625     setDisabled : function(v)
7626     {
7627         var i  = this.inputEl().dom;
7628         if (!v) {
7629             i.removeAttribute('disabled');
7630             return;
7631             
7632         }
7633         i.setAttribute('disabled','true');
7634     },
7635     initEvents : function()
7636     {
7637           
7638         this.inputEl().on("keydown" , this.fireKey,  this);
7639         this.inputEl().on("focus", this.onFocus,  this);
7640         this.inputEl().on("blur", this.onBlur,  this);
7641         
7642         this.inputEl().relayEvent('keyup', this);
7643
7644         // reference to original value for reset
7645         this.originalValue = this.getValue();
7646         //Roo.form.TextField.superclass.initEvents.call(this);
7647         if(this.validationEvent == 'keyup'){
7648             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7649             this.inputEl().on('keyup', this.filterValidation, this);
7650         }
7651         else if(this.validationEvent !== false){
7652             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7653         }
7654         
7655         if(this.selectOnFocus){
7656             this.on("focus", this.preFocus, this);
7657             
7658         }
7659         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7660             this.inputEl().on("keypress", this.filterKeys, this);
7661         }
7662        /* if(this.grow){
7663             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7664             this.el.on("click", this.autoSize,  this);
7665         }
7666         */
7667         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7668             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7669         }
7670         
7671         if (typeof(this.before) == 'object') {
7672             this.before.render(this.el.select('.roo-input-before',true).first());
7673         }
7674         if (typeof(this.after) == 'object') {
7675             this.after.render(this.el.select('.roo-input-after',true).first());
7676         }
7677         
7678         
7679     },
7680     filterValidation : function(e){
7681         if(!e.isNavKeyPress()){
7682             this.validationTask.delay(this.validationDelay);
7683         }
7684     },
7685      /**
7686      * Validates the field value
7687      * @return {Boolean} True if the value is valid, else false
7688      */
7689     validate : function(){
7690         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7691         if(this.disabled || this.validateValue(this.getRawValue())){
7692             this.markValid();
7693             return true;
7694         }
7695         
7696         this.markInvalid();
7697         return false;
7698     },
7699     
7700     
7701     /**
7702      * Validates a value according to the field's validation rules and marks the field as invalid
7703      * if the validation fails
7704      * @param {Mixed} value The value to validate
7705      * @return {Boolean} True if the value is valid, else false
7706      */
7707     validateValue : function(value){
7708         if(value.length < 1)  { // if it's blank
7709             if(this.allowBlank){
7710                 return true;
7711             }
7712             return false;
7713         }
7714         
7715         if(value.length < this.minLength){
7716             return false;
7717         }
7718         if(value.length > this.maxLength){
7719             return false;
7720         }
7721         if(this.vtype){
7722             var vt = Roo.form.VTypes;
7723             if(!vt[this.vtype](value, this)){
7724                 return false;
7725             }
7726         }
7727         if(typeof this.validator == "function"){
7728             var msg = this.validator(value);
7729             if(msg !== true){
7730                 return false;
7731             }
7732         }
7733         
7734         if(this.regex && !this.regex.test(value)){
7735             return false;
7736         }
7737         
7738         return true;
7739     },
7740
7741     
7742     
7743      // private
7744     fireKey : function(e){
7745         //Roo.log('field ' + e.getKey());
7746         if(e.isNavKeyPress()){
7747             this.fireEvent("specialkey", this, e);
7748         }
7749     },
7750     focus : function (selectText){
7751         if(this.rendered){
7752             this.inputEl().focus();
7753             if(selectText === true){
7754                 this.inputEl().dom.select();
7755             }
7756         }
7757         return this;
7758     } ,
7759     
7760     onFocus : function(){
7761         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7762            // this.el.addClass(this.focusClass);
7763         }
7764         if(!this.hasFocus){
7765             this.hasFocus = true;
7766             this.startValue = this.getValue();
7767             this.fireEvent("focus", this);
7768         }
7769     },
7770     
7771     beforeBlur : Roo.emptyFn,
7772
7773     
7774     // private
7775     onBlur : function(){
7776         this.beforeBlur();
7777         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7778             //this.el.removeClass(this.focusClass);
7779         }
7780         this.hasFocus = false;
7781         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7782             this.validate();
7783         }
7784         var v = this.getValue();
7785         if(String(v) !== String(this.startValue)){
7786             this.fireEvent('change', this, v, this.startValue);
7787         }
7788         this.fireEvent("blur", this);
7789     },
7790     
7791     /**
7792      * Resets the current field value to the originally loaded value and clears any validation messages
7793      */
7794     reset : function(){
7795         this.setValue(this.originalValue);
7796         this.validate();
7797     },
7798      /**
7799      * Returns the name of the field
7800      * @return {Mixed} name The name field
7801      */
7802     getName: function(){
7803         return this.name;
7804     },
7805      /**
7806      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7807      * @return {Mixed} value The field value
7808      */
7809     getValue : function(){
7810         
7811         var v = this.inputEl().getValue();
7812         
7813         return v;
7814     },
7815     /**
7816      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7817      * @return {Mixed} value The field value
7818      */
7819     getRawValue : function(){
7820         var v = this.inputEl().getValue();
7821         
7822         return v;
7823     },
7824     
7825     /**
7826      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7827      * @param {Mixed} value The value to set
7828      */
7829     setRawValue : function(v){
7830         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7831     },
7832     
7833     selectText : function(start, end){
7834         var v = this.getRawValue();
7835         if(v.length > 0){
7836             start = start === undefined ? 0 : start;
7837             end = end === undefined ? v.length : end;
7838             var d = this.inputEl().dom;
7839             if(d.setSelectionRange){
7840                 d.setSelectionRange(start, end);
7841             }else if(d.createTextRange){
7842                 var range = d.createTextRange();
7843                 range.moveStart("character", start);
7844                 range.moveEnd("character", v.length-end);
7845                 range.select();
7846             }
7847         }
7848     },
7849     
7850     /**
7851      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7852      * @param {Mixed} value The value to set
7853      */
7854     setValue : function(v){
7855         this.value = v;
7856         if(this.rendered){
7857             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7858             this.validate();
7859         }
7860     },
7861     
7862     /*
7863     processValue : function(value){
7864         if(this.stripCharsRe){
7865             var newValue = value.replace(this.stripCharsRe, '');
7866             if(newValue !== value){
7867                 this.setRawValue(newValue);
7868                 return newValue;
7869             }
7870         }
7871         return value;
7872     },
7873   */
7874     preFocus : function(){
7875         
7876         if(this.selectOnFocus){
7877             this.inputEl().dom.select();
7878         }
7879     },
7880     filterKeys : function(e){
7881         var k = e.getKey();
7882         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7883             return;
7884         }
7885         var c = e.getCharCode(), cc = String.fromCharCode(c);
7886         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7887             return;
7888         }
7889         if(!this.maskRe.test(cc)){
7890             e.stopEvent();
7891         }
7892     },
7893      /**
7894      * Clear any invalid styles/messages for this field
7895      */
7896     clearInvalid : function(){
7897         
7898         if(!this.el || this.preventMark){ // not rendered
7899             return;
7900         }
7901         this.el.removeClass(this.invalidClass);
7902         
7903         this.fireEvent('valid', this);
7904     },
7905     
7906      /**
7907      * Mark this field as valid
7908      */
7909     markValid : function(){
7910         if(!this.el  || this.preventMark){ // not rendered
7911             return;
7912         }
7913         
7914         this.el.removeClass([this.invalidClass, this.validClass]);
7915         
7916         if(this.disabled || this.allowBlank){
7917             return;
7918         }
7919         
7920         this.el.addClass(this.validClass);
7921         
7922         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
7923             
7924             var feedback = this.el.select('.form-control-feedback', true).first();
7925             
7926             if(feedback){
7927                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
7928                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
7929             }
7930             
7931         }
7932         
7933         this.fireEvent('valid', this);
7934     },
7935     
7936      /**
7937      * Mark this field as invalid
7938      * @param {String} msg The validation message
7939      */
7940     markInvalid : function(msg){
7941         if(!this.el  || this.preventMark){ // not rendered
7942             return;
7943         }
7944         
7945         this.el.removeClass([this.invalidClass, this.validClass]);
7946         
7947         if(this.disabled || this.allowBlank){
7948             return;
7949         }
7950         
7951         this.el.addClass(this.invalidClass);
7952         
7953         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7954             
7955             var feedback = this.el.select('.form-control-feedback', true).first();
7956             
7957             if(feedback){
7958                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
7959                 
7960                 if(this.getValue().length){
7961                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
7962                 }
7963                 
7964             }
7965             
7966         }
7967         
7968         this.fireEvent('invalid', this, msg);
7969     },
7970     // private
7971     SafariOnKeyDown : function(event)
7972     {
7973         // this is a workaround for a password hang bug on chrome/ webkit.
7974         
7975         var isSelectAll = false;
7976         
7977         if(this.inputEl().dom.selectionEnd > 0){
7978             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7979         }
7980         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7981             event.preventDefault();
7982             this.setValue('');
7983             return;
7984         }
7985         
7986         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
7987             
7988             event.preventDefault();
7989             // this is very hacky as keydown always get's upper case.
7990             //
7991             var cc = String.fromCharCode(event.getCharCode());
7992             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7993             
7994         }
7995     },
7996     adjustWidth : function(tag, w){
7997         tag = tag.toLowerCase();
7998         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7999             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8000                 if(tag == 'input'){
8001                     return w + 2;
8002                 }
8003                 if(tag == 'textarea'){
8004                     return w-2;
8005                 }
8006             }else if(Roo.isOpera){
8007                 if(tag == 'input'){
8008                     return w + 2;
8009                 }
8010                 if(tag == 'textarea'){
8011                     return w-2;
8012                 }
8013             }
8014         }
8015         return w;
8016     }
8017     
8018 });
8019
8020  
8021 /*
8022  * - LGPL
8023  *
8024  * Input
8025  * 
8026  */
8027
8028 /**
8029  * @class Roo.bootstrap.TextArea
8030  * @extends Roo.bootstrap.Input
8031  * Bootstrap TextArea class
8032  * @cfg {Number} cols Specifies the visible width of a text area
8033  * @cfg {Number} rows Specifies the visible number of lines in a text area
8034  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8035  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8036  * @cfg {string} html text
8037  * 
8038  * @constructor
8039  * Create a new TextArea
8040  * @param {Object} config The config object
8041  */
8042
8043 Roo.bootstrap.TextArea = function(config){
8044     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8045    
8046 };
8047
8048 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8049      
8050     cols : false,
8051     rows : 5,
8052     readOnly : false,
8053     warp : 'soft',
8054     resize : false,
8055     value: false,
8056     html: false,
8057     
8058     getAutoCreate : function(){
8059         
8060         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8061         
8062         var id = Roo.id();
8063         
8064         var cfg = {};
8065         
8066         var input =  {
8067             tag: 'textarea',
8068             id : id,
8069             warp : this.warp,
8070             rows : this.rows,
8071             value : this.value || '',
8072             html: this.html || '',
8073             cls : 'form-control',
8074             placeholder : this.placeholder || '' 
8075             
8076         };
8077         
8078         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8079             input.maxLength = this.maxLength;
8080         }
8081         
8082         if(this.resize){
8083             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8084         }
8085         
8086         if(this.cols){
8087             input.cols = this.cols;
8088         }
8089         
8090         if (this.readOnly) {
8091             input.readonly = true;
8092         }
8093         
8094         if (this.name) {
8095             input.name = this.name;
8096         }
8097         
8098         if (this.size) {
8099             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8100         }
8101         
8102         var settings=this;
8103         ['xs','sm','md','lg'].map(function(size){
8104             if (settings[size]) {
8105                 cfg.cls += ' col-' + size + '-' + settings[size];
8106             }
8107         });
8108         
8109         var inputblock = input;
8110         
8111         if(this.hasFeedback && !this.allowBlank){
8112             
8113             var feedback = {
8114                 tag: 'span',
8115                 cls: 'glyphicon form-control-feedback'
8116             };
8117
8118             inputblock = {
8119                 cls : 'has-feedback',
8120                 cn :  [
8121                     input,
8122                     feedback
8123                 ] 
8124             };  
8125         }
8126         
8127         
8128         if (this.before || this.after) {
8129             
8130             inputblock = {
8131                 cls : 'input-group',
8132                 cn :  [] 
8133             };
8134             if (this.before) {
8135                 inputblock.cn.push({
8136                     tag :'span',
8137                     cls : 'input-group-addon',
8138                     html : this.before
8139                 });
8140             }
8141             
8142             inputblock.cn.push(input);
8143             
8144             if(this.hasFeedback && !this.allowBlank){
8145                 inputblock.cls += ' has-feedback';
8146                 inputblock.cn.push(feedback);
8147             }
8148             
8149             if (this.after) {
8150                 inputblock.cn.push({
8151                     tag :'span',
8152                     cls : 'input-group-addon',
8153                     html : this.after
8154                 });
8155             }
8156             
8157         }
8158         
8159         if (align ==='left' && this.fieldLabel.length) {
8160                 Roo.log("left and has label");
8161                 cfg.cn = [
8162                     
8163                     {
8164                         tag: 'label',
8165                         'for' :  id,
8166                         cls : 'control-label col-sm-' + this.labelWidth,
8167                         html : this.fieldLabel
8168                         
8169                     },
8170                     {
8171                         cls : "col-sm-" + (12 - this.labelWidth), 
8172                         cn: [
8173                             inputblock
8174                         ]
8175                     }
8176                     
8177                 ];
8178         } else if ( this.fieldLabel.length) {
8179                 Roo.log(" label");
8180                  cfg.cn = [
8181                    
8182                     {
8183                         tag: 'label',
8184                         //cls : 'input-group-addon',
8185                         html : this.fieldLabel
8186                         
8187                     },
8188                     
8189                     inputblock
8190                     
8191                 ];
8192
8193         } else {
8194             
8195                    Roo.log(" no label && no align");
8196                 cfg.cn = [
8197                     
8198                         inputblock
8199                     
8200                 ];
8201                 
8202                 
8203         }
8204         
8205         if (this.disabled) {
8206             input.disabled=true;
8207         }
8208         
8209         return cfg;
8210         
8211     },
8212     /**
8213      * return the real textarea element.
8214      */
8215     inputEl: function ()
8216     {
8217         return this.el.select('textarea.form-control',true).first();
8218     }
8219 });
8220
8221  
8222 /*
8223  * - LGPL
8224  *
8225  * trigger field - base class for combo..
8226  * 
8227  */
8228  
8229 /**
8230  * @class Roo.bootstrap.TriggerField
8231  * @extends Roo.bootstrap.Input
8232  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8233  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8234  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8235  * for which you can provide a custom implementation.  For example:
8236  * <pre><code>
8237 var trigger = new Roo.bootstrap.TriggerField();
8238 trigger.onTriggerClick = myTriggerFn;
8239 trigger.applyTo('my-field');
8240 </code></pre>
8241  *
8242  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8243  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8244  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8245  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8246  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8247
8248  * @constructor
8249  * Create a new TriggerField.
8250  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8251  * to the base TextField)
8252  */
8253 Roo.bootstrap.TriggerField = function(config){
8254     this.mimicing = false;
8255     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8256 };
8257
8258 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8259     /**
8260      * @cfg {String} triggerClass A CSS class to apply to the trigger
8261      */
8262      /**
8263      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8264      */
8265     hideTrigger:false,
8266
8267     /** @cfg {Boolean} grow @hide */
8268     /** @cfg {Number} growMin @hide */
8269     /** @cfg {Number} growMax @hide */
8270
8271     /**
8272      * @hide 
8273      * @method
8274      */
8275     autoSize: Roo.emptyFn,
8276     // private
8277     monitorTab : true,
8278     // private
8279     deferHeight : true,
8280
8281     
8282     actionMode : 'wrap',
8283     
8284     caret : false,
8285     
8286     
8287     getAutoCreate : function(){
8288        
8289         var align = this.labelAlign || this.parentLabelAlign();
8290         
8291         var id = Roo.id();
8292         
8293         var cfg = {
8294             cls: 'form-group' //input-group
8295         };
8296         
8297         
8298         var input =  {
8299             tag: 'input',
8300             id : id,
8301             type : this.inputType,
8302             cls : 'form-control',
8303             autocomplete: 'new-password',
8304             placeholder : this.placeholder || '' 
8305             
8306         };
8307         if (this.name) {
8308             input.name = this.name;
8309         }
8310         if (this.size) {
8311             input.cls += ' input-' + this.size;
8312         }
8313         
8314         if (this.disabled) {
8315             input.disabled=true;
8316         }
8317         
8318         var inputblock = input;
8319         
8320         if(this.hasFeedback && !this.allowBlank){
8321             
8322             var feedback = {
8323                 tag: 'span',
8324                 cls: 'glyphicon form-control-feedback'
8325             };
8326
8327             inputblock = {
8328                 cls : 'has-feedback',
8329                 cn :  [
8330                     input,
8331                     feedback
8332                 ] 
8333             };  
8334         }
8335         
8336         if (this.before || this.after) {
8337             
8338             inputblock = {
8339                 cls : 'input-group',
8340                 cn :  [] 
8341             };
8342             if (this.before) {
8343                 inputblock.cn.push({
8344                     tag :'span',
8345                     cls : 'input-group-addon',
8346                     html : this.before
8347                 });
8348             }
8349             
8350             inputblock.cn.push(input);
8351             
8352             if(this.hasFeedback && !this.allowBlank){
8353                 inputblock.cls += ' has-feedback';
8354                 inputblock.cn.push(feedback);
8355             }
8356             
8357             if (this.after) {
8358                 inputblock.cn.push({
8359                     tag :'span',
8360                     cls : 'input-group-addon',
8361                     html : this.after
8362                 });
8363             }
8364             
8365         };
8366         
8367         var box = {
8368             tag: 'div',
8369             cn: [
8370                 {
8371                     tag: 'input',
8372                     type : 'hidden',
8373                     cls: 'form-hidden-field'
8374                 },
8375                 inputblock
8376             ]
8377             
8378         };
8379         
8380         if(this.multiple){
8381             Roo.log('multiple');
8382             
8383             box = {
8384                 tag: 'div',
8385                 cn: [
8386                     {
8387                         tag: 'input',
8388                         type : 'hidden',
8389                         cls: 'form-hidden-field'
8390                     },
8391                     {
8392                         tag: 'ul',
8393                         cls: 'select2-choices',
8394                         cn:[
8395                             {
8396                                 tag: 'li',
8397                                 cls: 'select2-search-field',
8398                                 cn: [
8399
8400                                     inputblock
8401                                 ]
8402                             }
8403                         ]
8404                     }
8405                 ]
8406             }
8407         };
8408         
8409         var combobox = {
8410             cls: 'select2-container input-group',
8411             cn: [
8412                 box
8413 //                {
8414 //                    tag: 'ul',
8415 //                    cls: 'typeahead typeahead-long dropdown-menu',
8416 //                    style: 'display:none'
8417 //                }
8418             ]
8419         };
8420         
8421         if(!this.multiple && this.showToggleBtn){
8422             
8423             var caret = {
8424                         tag: 'span',
8425                         cls: 'caret'
8426              };
8427             if (this.caret != false) {
8428                 caret = {
8429                      tag: 'i',
8430                      cls: 'fa fa-' + this.caret
8431                 };
8432                 
8433             }
8434             
8435             combobox.cn.push({
8436                 tag :'span',
8437                 cls : 'input-group-addon btn dropdown-toggle',
8438                 cn : [
8439                     caret,
8440                     {
8441                         tag: 'span',
8442                         cls: 'combobox-clear',
8443                         cn  : [
8444                             {
8445                                 tag : 'i',
8446                                 cls: 'icon-remove'
8447                             }
8448                         ]
8449                     }
8450                 ]
8451
8452             })
8453         }
8454         
8455         if(this.multiple){
8456             combobox.cls += ' select2-container-multi';
8457         }
8458         
8459         if (align ==='left' && this.fieldLabel.length) {
8460             
8461                 Roo.log("left and has label");
8462                 cfg.cn = [
8463                     
8464                     {
8465                         tag: 'label',
8466                         'for' :  id,
8467                         cls : 'control-label col-sm-' + this.labelWidth,
8468                         html : this.fieldLabel
8469                         
8470                     },
8471                     {
8472                         cls : "col-sm-" + (12 - this.labelWidth), 
8473                         cn: [
8474                             combobox
8475                         ]
8476                     }
8477                     
8478                 ];
8479         } else if ( this.fieldLabel.length) {
8480                 Roo.log(" label");
8481                  cfg.cn = [
8482                    
8483                     {
8484                         tag: 'label',
8485                         //cls : 'input-group-addon',
8486                         html : this.fieldLabel
8487                         
8488                     },
8489                     
8490                     combobox
8491                     
8492                 ];
8493
8494         } else {
8495             
8496                 Roo.log(" no label && no align");
8497                 cfg = combobox
8498                      
8499                 
8500         }
8501          
8502         var settings=this;
8503         ['xs','sm','md','lg'].map(function(size){
8504             if (settings[size]) {
8505                 cfg.cls += ' col-' + size + '-' + settings[size];
8506             }
8507         });
8508         
8509         return cfg;
8510         
8511     },
8512     
8513     
8514     
8515     // private
8516     onResize : function(w, h){
8517 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8518 //        if(typeof w == 'number'){
8519 //            var x = w - this.trigger.getWidth();
8520 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8521 //            this.trigger.setStyle('left', x+'px');
8522 //        }
8523     },
8524
8525     // private
8526     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8527
8528     // private
8529     getResizeEl : function(){
8530         return this.inputEl();
8531     },
8532
8533     // private
8534     getPositionEl : function(){
8535         return this.inputEl();
8536     },
8537
8538     // private
8539     alignErrorIcon : function(){
8540         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8541     },
8542
8543     // private
8544     initEvents : function(){
8545         
8546         this.createList();
8547         
8548         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8549         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8550         if(!this.multiple && this.showToggleBtn){
8551             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8552             if(this.hideTrigger){
8553                 this.trigger.setDisplayed(false);
8554             }
8555             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8556         }
8557         
8558         if(this.multiple){
8559             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8560         }
8561         
8562         //this.trigger.addClassOnOver('x-form-trigger-over');
8563         //this.trigger.addClassOnClick('x-form-trigger-click');
8564         
8565         //if(!this.width){
8566         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8567         //}
8568     },
8569     
8570     createList : function()
8571     {
8572         this.list = Roo.get(document.body).createChild({
8573             tag: 'ul',
8574             cls: 'typeahead typeahead-long dropdown-menu',
8575             style: 'display:none'
8576         });
8577         
8578         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8579         
8580     },
8581
8582     // private
8583     initTrigger : function(){
8584        
8585     },
8586
8587     // private
8588     onDestroy : function(){
8589         if(this.trigger){
8590             this.trigger.removeAllListeners();
8591           //  this.trigger.remove();
8592         }
8593         //if(this.wrap){
8594         //    this.wrap.remove();
8595         //}
8596         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8597     },
8598
8599     // private
8600     onFocus : function(){
8601         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8602         /*
8603         if(!this.mimicing){
8604             this.wrap.addClass('x-trigger-wrap-focus');
8605             this.mimicing = true;
8606             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8607             if(this.monitorTab){
8608                 this.el.on("keydown", this.checkTab, this);
8609             }
8610         }
8611         */
8612     },
8613
8614     // private
8615     checkTab : function(e){
8616         if(e.getKey() == e.TAB){
8617             this.triggerBlur();
8618         }
8619     },
8620
8621     // private
8622     onBlur : function(){
8623         // do nothing
8624     },
8625
8626     // private
8627     mimicBlur : function(e, t){
8628         /*
8629         if(!this.wrap.contains(t) && this.validateBlur()){
8630             this.triggerBlur();
8631         }
8632         */
8633     },
8634
8635     // private
8636     triggerBlur : function(){
8637         this.mimicing = false;
8638         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8639         if(this.monitorTab){
8640             this.el.un("keydown", this.checkTab, this);
8641         }
8642         //this.wrap.removeClass('x-trigger-wrap-focus');
8643         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8644     },
8645
8646     // private
8647     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8648     validateBlur : function(e, t){
8649         return true;
8650     },
8651
8652     // private
8653     onDisable : function(){
8654         this.inputEl().dom.disabled = true;
8655         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8656         //if(this.wrap){
8657         //    this.wrap.addClass('x-item-disabled');
8658         //}
8659     },
8660
8661     // private
8662     onEnable : function(){
8663         this.inputEl().dom.disabled = false;
8664         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8665         //if(this.wrap){
8666         //    this.el.removeClass('x-item-disabled');
8667         //}
8668     },
8669
8670     // private
8671     onShow : function(){
8672         var ae = this.getActionEl();
8673         
8674         if(ae){
8675             ae.dom.style.display = '';
8676             ae.dom.style.visibility = 'visible';
8677         }
8678     },
8679
8680     // private
8681     
8682     onHide : function(){
8683         var ae = this.getActionEl();
8684         ae.dom.style.display = 'none';
8685     },
8686
8687     /**
8688      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8689      * by an implementing function.
8690      * @method
8691      * @param {EventObject} e
8692      */
8693     onTriggerClick : Roo.emptyFn
8694 });
8695  /*
8696  * Based on:
8697  * Ext JS Library 1.1.1
8698  * Copyright(c) 2006-2007, Ext JS, LLC.
8699  *
8700  * Originally Released Under LGPL - original licence link has changed is not relivant.
8701  *
8702  * Fork - LGPL
8703  * <script type="text/javascript">
8704  */
8705
8706
8707 /**
8708  * @class Roo.data.SortTypes
8709  * @singleton
8710  * Defines the default sorting (casting?) comparison functions used when sorting data.
8711  */
8712 Roo.data.SortTypes = {
8713     /**
8714      * Default sort that does nothing
8715      * @param {Mixed} s The value being converted
8716      * @return {Mixed} The comparison value
8717      */
8718     none : function(s){
8719         return s;
8720     },
8721     
8722     /**
8723      * The regular expression used to strip tags
8724      * @type {RegExp}
8725      * @property
8726      */
8727     stripTagsRE : /<\/?[^>]+>/gi,
8728     
8729     /**
8730      * Strips all HTML tags to sort on text only
8731      * @param {Mixed} s The value being converted
8732      * @return {String} The comparison value
8733      */
8734     asText : function(s){
8735         return String(s).replace(this.stripTagsRE, "");
8736     },
8737     
8738     /**
8739      * Strips all HTML tags to sort on text only - Case insensitive
8740      * @param {Mixed} s The value being converted
8741      * @return {String} The comparison value
8742      */
8743     asUCText : function(s){
8744         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8745     },
8746     
8747     /**
8748      * Case insensitive string
8749      * @param {Mixed} s The value being converted
8750      * @return {String} The comparison value
8751      */
8752     asUCString : function(s) {
8753         return String(s).toUpperCase();
8754     },
8755     
8756     /**
8757      * Date sorting
8758      * @param {Mixed} s The value being converted
8759      * @return {Number} The comparison value
8760      */
8761     asDate : function(s) {
8762         if(!s){
8763             return 0;
8764         }
8765         if(s instanceof Date){
8766             return s.getTime();
8767         }
8768         return Date.parse(String(s));
8769     },
8770     
8771     /**
8772      * Float sorting
8773      * @param {Mixed} s The value being converted
8774      * @return {Float} The comparison value
8775      */
8776     asFloat : function(s) {
8777         var val = parseFloat(String(s).replace(/,/g, ""));
8778         if(isNaN(val)) val = 0;
8779         return val;
8780     },
8781     
8782     /**
8783      * Integer sorting
8784      * @param {Mixed} s The value being converted
8785      * @return {Number} The comparison value
8786      */
8787     asInt : function(s) {
8788         var val = parseInt(String(s).replace(/,/g, ""));
8789         if(isNaN(val)) val = 0;
8790         return val;
8791     }
8792 };/*
8793  * Based on:
8794  * Ext JS Library 1.1.1
8795  * Copyright(c) 2006-2007, Ext JS, LLC.
8796  *
8797  * Originally Released Under LGPL - original licence link has changed is not relivant.
8798  *
8799  * Fork - LGPL
8800  * <script type="text/javascript">
8801  */
8802
8803 /**
8804 * @class Roo.data.Record
8805  * Instances of this class encapsulate both record <em>definition</em> information, and record
8806  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8807  * to access Records cached in an {@link Roo.data.Store} object.<br>
8808  * <p>
8809  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8810  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8811  * objects.<br>
8812  * <p>
8813  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8814  * @constructor
8815  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8816  * {@link #create}. The parameters are the same.
8817  * @param {Array} data An associative Array of data values keyed by the field name.
8818  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8819  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8820  * not specified an integer id is generated.
8821  */
8822 Roo.data.Record = function(data, id){
8823     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8824     this.data = data;
8825 };
8826
8827 /**
8828  * Generate a constructor for a specific record layout.
8829  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8830  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8831  * Each field definition object may contain the following properties: <ul>
8832  * <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,
8833  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8834  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8835  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8836  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8837  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8838  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8839  * this may be omitted.</p></li>
8840  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8841  * <ul><li>auto (Default, implies no conversion)</li>
8842  * <li>string</li>
8843  * <li>int</li>
8844  * <li>float</li>
8845  * <li>boolean</li>
8846  * <li>date</li></ul></p></li>
8847  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8848  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8849  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8850  * by the Reader into an object that will be stored in the Record. It is passed the
8851  * following parameters:<ul>
8852  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8853  * </ul></p></li>
8854  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8855  * </ul>
8856  * <br>usage:<br><pre><code>
8857 var TopicRecord = Roo.data.Record.create(
8858     {name: 'title', mapping: 'topic_title'},
8859     {name: 'author', mapping: 'username'},
8860     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8861     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8862     {name: 'lastPoster', mapping: 'user2'},
8863     {name: 'excerpt', mapping: 'post_text'}
8864 );
8865
8866 var myNewRecord = new TopicRecord({
8867     title: 'Do my job please',
8868     author: 'noobie',
8869     totalPosts: 1,
8870     lastPost: new Date(),
8871     lastPoster: 'Animal',
8872     excerpt: 'No way dude!'
8873 });
8874 myStore.add(myNewRecord);
8875 </code></pre>
8876  * @method create
8877  * @static
8878  */
8879 Roo.data.Record.create = function(o){
8880     var f = function(){
8881         f.superclass.constructor.apply(this, arguments);
8882     };
8883     Roo.extend(f, Roo.data.Record);
8884     var p = f.prototype;
8885     p.fields = new Roo.util.MixedCollection(false, function(field){
8886         return field.name;
8887     });
8888     for(var i = 0, len = o.length; i < len; i++){
8889         p.fields.add(new Roo.data.Field(o[i]));
8890     }
8891     f.getField = function(name){
8892         return p.fields.get(name);  
8893     };
8894     return f;
8895 };
8896
8897 Roo.data.Record.AUTO_ID = 1000;
8898 Roo.data.Record.EDIT = 'edit';
8899 Roo.data.Record.REJECT = 'reject';
8900 Roo.data.Record.COMMIT = 'commit';
8901
8902 Roo.data.Record.prototype = {
8903     /**
8904      * Readonly flag - true if this record has been modified.
8905      * @type Boolean
8906      */
8907     dirty : false,
8908     editing : false,
8909     error: null,
8910     modified: null,
8911
8912     // private
8913     join : function(store){
8914         this.store = store;
8915     },
8916
8917     /**
8918      * Set the named field to the specified value.
8919      * @param {String} name The name of the field to set.
8920      * @param {Object} value The value to set the field to.
8921      */
8922     set : function(name, value){
8923         if(this.data[name] == value){
8924             return;
8925         }
8926         this.dirty = true;
8927         if(!this.modified){
8928             this.modified = {};
8929         }
8930         if(typeof this.modified[name] == 'undefined'){
8931             this.modified[name] = this.data[name];
8932         }
8933         this.data[name] = value;
8934         if(!this.editing && this.store){
8935             this.store.afterEdit(this);
8936         }       
8937     },
8938
8939     /**
8940      * Get the value of the named field.
8941      * @param {String} name The name of the field to get the value of.
8942      * @return {Object} The value of the field.
8943      */
8944     get : function(name){
8945         return this.data[name]; 
8946     },
8947
8948     // private
8949     beginEdit : function(){
8950         this.editing = true;
8951         this.modified = {}; 
8952     },
8953
8954     // private
8955     cancelEdit : function(){
8956         this.editing = false;
8957         delete this.modified;
8958     },
8959
8960     // private
8961     endEdit : function(){
8962         this.editing = false;
8963         if(this.dirty && this.store){
8964             this.store.afterEdit(this);
8965         }
8966     },
8967
8968     /**
8969      * Usually called by the {@link Roo.data.Store} which owns the Record.
8970      * Rejects all changes made to the Record since either creation, or the last commit operation.
8971      * Modified fields are reverted to their original values.
8972      * <p>
8973      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8974      * of reject operations.
8975      */
8976     reject : function(){
8977         var m = this.modified;
8978         for(var n in m){
8979             if(typeof m[n] != "function"){
8980                 this.data[n] = m[n];
8981             }
8982         }
8983         this.dirty = false;
8984         delete this.modified;
8985         this.editing = false;
8986         if(this.store){
8987             this.store.afterReject(this);
8988         }
8989     },
8990
8991     /**
8992      * Usually called by the {@link Roo.data.Store} which owns the Record.
8993      * Commits all changes made to the Record since either creation, or the last commit operation.
8994      * <p>
8995      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8996      * of commit operations.
8997      */
8998     commit : function(){
8999         this.dirty = false;
9000         delete this.modified;
9001         this.editing = false;
9002         if(this.store){
9003             this.store.afterCommit(this);
9004         }
9005     },
9006
9007     // private
9008     hasError : function(){
9009         return this.error != null;
9010     },
9011
9012     // private
9013     clearError : function(){
9014         this.error = null;
9015     },
9016
9017     /**
9018      * Creates a copy of this record.
9019      * @param {String} id (optional) A new record id if you don't want to use this record's id
9020      * @return {Record}
9021      */
9022     copy : function(newId) {
9023         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9024     }
9025 };/*
9026  * Based on:
9027  * Ext JS Library 1.1.1
9028  * Copyright(c) 2006-2007, Ext JS, LLC.
9029  *
9030  * Originally Released Under LGPL - original licence link has changed is not relivant.
9031  *
9032  * Fork - LGPL
9033  * <script type="text/javascript">
9034  */
9035
9036
9037
9038 /**
9039  * @class Roo.data.Store
9040  * @extends Roo.util.Observable
9041  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9042  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9043  * <p>
9044  * 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
9045  * has no knowledge of the format of the data returned by the Proxy.<br>
9046  * <p>
9047  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9048  * instances from the data object. These records are cached and made available through accessor functions.
9049  * @constructor
9050  * Creates a new Store.
9051  * @param {Object} config A config object containing the objects needed for the Store to access data,
9052  * and read the data into Records.
9053  */
9054 Roo.data.Store = function(config){
9055     this.data = new Roo.util.MixedCollection(false);
9056     this.data.getKey = function(o){
9057         return o.id;
9058     };
9059     this.baseParams = {};
9060     // private
9061     this.paramNames = {
9062         "start" : "start",
9063         "limit" : "limit",
9064         "sort" : "sort",
9065         "dir" : "dir",
9066         "multisort" : "_multisort"
9067     };
9068
9069     if(config && config.data){
9070         this.inlineData = config.data;
9071         delete config.data;
9072     }
9073
9074     Roo.apply(this, config);
9075     
9076     if(this.reader){ // reader passed
9077         this.reader = Roo.factory(this.reader, Roo.data);
9078         this.reader.xmodule = this.xmodule || false;
9079         if(!this.recordType){
9080             this.recordType = this.reader.recordType;
9081         }
9082         if(this.reader.onMetaChange){
9083             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9084         }
9085     }
9086
9087     if(this.recordType){
9088         this.fields = this.recordType.prototype.fields;
9089     }
9090     this.modified = [];
9091
9092     this.addEvents({
9093         /**
9094          * @event datachanged
9095          * Fires when the data cache has changed, and a widget which is using this Store
9096          * as a Record cache should refresh its view.
9097          * @param {Store} this
9098          */
9099         datachanged : true,
9100         /**
9101          * @event metachange
9102          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9103          * @param {Store} this
9104          * @param {Object} meta The JSON metadata
9105          */
9106         metachange : true,
9107         /**
9108          * @event add
9109          * Fires when Records have been added to the Store
9110          * @param {Store} this
9111          * @param {Roo.data.Record[]} records The array of Records added
9112          * @param {Number} index The index at which the record(s) were added
9113          */
9114         add : true,
9115         /**
9116          * @event remove
9117          * Fires when a Record has been removed from the Store
9118          * @param {Store} this
9119          * @param {Roo.data.Record} record The Record that was removed
9120          * @param {Number} index The index at which the record was removed
9121          */
9122         remove : true,
9123         /**
9124          * @event update
9125          * Fires when a Record has been updated
9126          * @param {Store} this
9127          * @param {Roo.data.Record} record The Record that was updated
9128          * @param {String} operation The update operation being performed.  Value may be one of:
9129          * <pre><code>
9130  Roo.data.Record.EDIT
9131  Roo.data.Record.REJECT
9132  Roo.data.Record.COMMIT
9133          * </code></pre>
9134          */
9135         update : true,
9136         /**
9137          * @event clear
9138          * Fires when the data cache has been cleared.
9139          * @param {Store} this
9140          */
9141         clear : true,
9142         /**
9143          * @event beforeload
9144          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9145          * the load action will be canceled.
9146          * @param {Store} this
9147          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9148          */
9149         beforeload : true,
9150         /**
9151          * @event beforeloadadd
9152          * Fires after a new set of Records has been loaded.
9153          * @param {Store} this
9154          * @param {Roo.data.Record[]} records The Records that were loaded
9155          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9156          */
9157         beforeloadadd : true,
9158         /**
9159          * @event load
9160          * Fires after a new set of Records has been loaded, before they are added to the store.
9161          * @param {Store} this
9162          * @param {Roo.data.Record[]} records The Records that were loaded
9163          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9164          * @params {Object} return from reader
9165          */
9166         load : true,
9167         /**
9168          * @event loadexception
9169          * Fires if an exception occurs in the Proxy during loading.
9170          * Called with the signature of the Proxy's "loadexception" event.
9171          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9172          * 
9173          * @param {Proxy} 
9174          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9175          * @param {Object} load options 
9176          * @param {Object} jsonData from your request (normally this contains the Exception)
9177          */
9178         loadexception : true
9179     });
9180     
9181     if(this.proxy){
9182         this.proxy = Roo.factory(this.proxy, Roo.data);
9183         this.proxy.xmodule = this.xmodule || false;
9184         this.relayEvents(this.proxy,  ["loadexception"]);
9185     }
9186     this.sortToggle = {};
9187     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9188
9189     Roo.data.Store.superclass.constructor.call(this);
9190
9191     if(this.inlineData){
9192         this.loadData(this.inlineData);
9193         delete this.inlineData;
9194     }
9195 };
9196
9197 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9198      /**
9199     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9200     * without a remote query - used by combo/forms at present.
9201     */
9202     
9203     /**
9204     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9205     */
9206     /**
9207     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9208     */
9209     /**
9210     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9211     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9212     */
9213     /**
9214     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9215     * on any HTTP request
9216     */
9217     /**
9218     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9219     */
9220     /**
9221     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9222     */
9223     multiSort: false,
9224     /**
9225     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9226     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9227     */
9228     remoteSort : false,
9229
9230     /**
9231     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9232      * loaded or when a record is removed. (defaults to false).
9233     */
9234     pruneModifiedRecords : false,
9235
9236     // private
9237     lastOptions : null,
9238
9239     /**
9240      * Add Records to the Store and fires the add event.
9241      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9242      */
9243     add : function(records){
9244         records = [].concat(records);
9245         for(var i = 0, len = records.length; i < len; i++){
9246             records[i].join(this);
9247         }
9248         var index = this.data.length;
9249         this.data.addAll(records);
9250         this.fireEvent("add", this, records, index);
9251     },
9252
9253     /**
9254      * Remove a Record from the Store and fires the remove event.
9255      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9256      */
9257     remove : function(record){
9258         var index = this.data.indexOf(record);
9259         this.data.removeAt(index);
9260         if(this.pruneModifiedRecords){
9261             this.modified.remove(record);
9262         }
9263         this.fireEvent("remove", this, record, index);
9264     },
9265
9266     /**
9267      * Remove all Records from the Store and fires the clear event.
9268      */
9269     removeAll : function(){
9270         this.data.clear();
9271         if(this.pruneModifiedRecords){
9272             this.modified = [];
9273         }
9274         this.fireEvent("clear", this);
9275     },
9276
9277     /**
9278      * Inserts Records to the Store at the given index and fires the add event.
9279      * @param {Number} index The start index at which to insert the passed Records.
9280      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9281      */
9282     insert : function(index, records){
9283         records = [].concat(records);
9284         for(var i = 0, len = records.length; i < len; i++){
9285             this.data.insert(index, records[i]);
9286             records[i].join(this);
9287         }
9288         this.fireEvent("add", this, records, index);
9289     },
9290
9291     /**
9292      * Get the index within the cache of the passed Record.
9293      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9294      * @return {Number} The index of the passed Record. Returns -1 if not found.
9295      */
9296     indexOf : function(record){
9297         return this.data.indexOf(record);
9298     },
9299
9300     /**
9301      * Get the index within the cache of the Record with the passed id.
9302      * @param {String} id The id of the Record to find.
9303      * @return {Number} The index of the Record. Returns -1 if not found.
9304      */
9305     indexOfId : function(id){
9306         return this.data.indexOfKey(id);
9307     },
9308
9309     /**
9310      * Get the Record with the specified id.
9311      * @param {String} id The id of the Record to find.
9312      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9313      */
9314     getById : function(id){
9315         return this.data.key(id);
9316     },
9317
9318     /**
9319      * Get the Record at the specified index.
9320      * @param {Number} index The index of the Record to find.
9321      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9322      */
9323     getAt : function(index){
9324         return this.data.itemAt(index);
9325     },
9326
9327     /**
9328      * Returns a range of Records between specified indices.
9329      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9330      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9331      * @return {Roo.data.Record[]} An array of Records
9332      */
9333     getRange : function(start, end){
9334         return this.data.getRange(start, end);
9335     },
9336
9337     // private
9338     storeOptions : function(o){
9339         o = Roo.apply({}, o);
9340         delete o.callback;
9341         delete o.scope;
9342         this.lastOptions = o;
9343     },
9344
9345     /**
9346      * Loads the Record cache from the configured Proxy using the configured Reader.
9347      * <p>
9348      * If using remote paging, then the first load call must specify the <em>start</em>
9349      * and <em>limit</em> properties in the options.params property to establish the initial
9350      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9351      * <p>
9352      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9353      * and this call will return before the new data has been loaded. Perform any post-processing
9354      * in a callback function, or in a "load" event handler.</strong>
9355      * <p>
9356      * @param {Object} options An object containing properties which control loading options:<ul>
9357      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9358      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9359      * passed the following arguments:<ul>
9360      * <li>r : Roo.data.Record[]</li>
9361      * <li>options: Options object from the load call</li>
9362      * <li>success: Boolean success indicator</li></ul></li>
9363      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9364      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9365      * </ul>
9366      */
9367     load : function(options){
9368         options = options || {};
9369         if(this.fireEvent("beforeload", this, options) !== false){
9370             this.storeOptions(options);
9371             var p = Roo.apply(options.params || {}, this.baseParams);
9372             // if meta was not loaded from remote source.. try requesting it.
9373             if (!this.reader.metaFromRemote) {
9374                 p._requestMeta = 1;
9375             }
9376             if(this.sortInfo && this.remoteSort){
9377                 var pn = this.paramNames;
9378                 p[pn["sort"]] = this.sortInfo.field;
9379                 p[pn["dir"]] = this.sortInfo.direction;
9380             }
9381             if (this.multiSort) {
9382                 var pn = this.paramNames;
9383                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9384             }
9385             
9386             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9387         }
9388     },
9389
9390     /**
9391      * Reloads the Record cache from the configured Proxy using the configured Reader and
9392      * the options from the last load operation performed.
9393      * @param {Object} options (optional) An object containing properties which may override the options
9394      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9395      * the most recently used options are reused).
9396      */
9397     reload : function(options){
9398         this.load(Roo.applyIf(options||{}, this.lastOptions));
9399     },
9400
9401     // private
9402     // Called as a callback by the Reader during a load operation.
9403     loadRecords : function(o, options, success){
9404         if(!o || success === false){
9405             if(success !== false){
9406                 this.fireEvent("load", this, [], options, o);
9407             }
9408             if(options.callback){
9409                 options.callback.call(options.scope || this, [], options, false);
9410             }
9411             return;
9412         }
9413         // if data returned failure - throw an exception.
9414         if (o.success === false) {
9415             // show a message if no listener is registered.
9416             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9417                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9418             }
9419             // loadmask wil be hooked into this..
9420             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9421             return;
9422         }
9423         var r = o.records, t = o.totalRecords || r.length;
9424         
9425         this.fireEvent("beforeloadadd", this, r, options, o);
9426         
9427         if(!options || options.add !== true){
9428             if(this.pruneModifiedRecords){
9429                 this.modified = [];
9430             }
9431             for(var i = 0, len = r.length; i < len; i++){
9432                 r[i].join(this);
9433             }
9434             if(this.snapshot){
9435                 this.data = this.snapshot;
9436                 delete this.snapshot;
9437             }
9438             this.data.clear();
9439             this.data.addAll(r);
9440             this.totalLength = t;
9441             this.applySort();
9442             this.fireEvent("datachanged", this);
9443         }else{
9444             this.totalLength = Math.max(t, this.data.length+r.length);
9445             this.add(r);
9446         }
9447         this.fireEvent("load", this, r, options, o);
9448         if(options.callback){
9449             options.callback.call(options.scope || this, r, options, true);
9450         }
9451     },
9452
9453
9454     /**
9455      * Loads data from a passed data block. A Reader which understands the format of the data
9456      * must have been configured in the constructor.
9457      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9458      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9459      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9460      */
9461     loadData : function(o, append){
9462         var r = this.reader.readRecords(o);
9463         this.loadRecords(r, {add: append}, true);
9464     },
9465
9466     /**
9467      * Gets the number of cached records.
9468      * <p>
9469      * <em>If using paging, this may not be the total size of the dataset. If the data object
9470      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9471      * the data set size</em>
9472      */
9473     getCount : function(){
9474         return this.data.length || 0;
9475     },
9476
9477     /**
9478      * Gets the total number of records in the dataset as returned by the server.
9479      * <p>
9480      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9481      * the dataset size</em>
9482      */
9483     getTotalCount : function(){
9484         return this.totalLength || 0;
9485     },
9486
9487     /**
9488      * Returns the sort state of the Store as an object with two properties:
9489      * <pre><code>
9490  field {String} The name of the field by which the Records are sorted
9491  direction {String} The sort order, "ASC" or "DESC"
9492      * </code></pre>
9493      */
9494     getSortState : function(){
9495         return this.sortInfo;
9496     },
9497
9498     // private
9499     applySort : function(){
9500         if(this.sortInfo && !this.remoteSort){
9501             var s = this.sortInfo, f = s.field;
9502             var st = this.fields.get(f).sortType;
9503             var fn = function(r1, r2){
9504                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9505                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9506             };
9507             this.data.sort(s.direction, fn);
9508             if(this.snapshot && this.snapshot != this.data){
9509                 this.snapshot.sort(s.direction, fn);
9510             }
9511         }
9512     },
9513
9514     /**
9515      * Sets the default sort column and order to be used by the next load operation.
9516      * @param {String} fieldName The name of the field to sort by.
9517      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9518      */
9519     setDefaultSort : function(field, dir){
9520         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9521     },
9522
9523     /**
9524      * Sort the Records.
9525      * If remote sorting is used, the sort is performed on the server, and the cache is
9526      * reloaded. If local sorting is used, the cache is sorted internally.
9527      * @param {String} fieldName The name of the field to sort by.
9528      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9529      */
9530     sort : function(fieldName, dir){
9531         var f = this.fields.get(fieldName);
9532         if(!dir){
9533             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9534             
9535             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9536                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9537             }else{
9538                 dir = f.sortDir;
9539             }
9540         }
9541         this.sortToggle[f.name] = dir;
9542         this.sortInfo = {field: f.name, direction: dir};
9543         if(!this.remoteSort){
9544             this.applySort();
9545             this.fireEvent("datachanged", this);
9546         }else{
9547             this.load(this.lastOptions);
9548         }
9549     },
9550
9551     /**
9552      * Calls the specified function for each of the Records in the cache.
9553      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9554      * Returning <em>false</em> aborts and exits the iteration.
9555      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9556      */
9557     each : function(fn, scope){
9558         this.data.each(fn, scope);
9559     },
9560
9561     /**
9562      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9563      * (e.g., during paging).
9564      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9565      */
9566     getModifiedRecords : function(){
9567         return this.modified;
9568     },
9569
9570     // private
9571     createFilterFn : function(property, value, anyMatch){
9572         if(!value.exec){ // not a regex
9573             value = String(value);
9574             if(value.length == 0){
9575                 return false;
9576             }
9577             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9578         }
9579         return function(r){
9580             return value.test(r.data[property]);
9581         };
9582     },
9583
9584     /**
9585      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9586      * @param {String} property A field on your records
9587      * @param {Number} start The record index to start at (defaults to 0)
9588      * @param {Number} end The last record index to include (defaults to length - 1)
9589      * @return {Number} The sum
9590      */
9591     sum : function(property, start, end){
9592         var rs = this.data.items, v = 0;
9593         start = start || 0;
9594         end = (end || end === 0) ? end : rs.length-1;
9595
9596         for(var i = start; i <= end; i++){
9597             v += (rs[i].data[property] || 0);
9598         }
9599         return v;
9600     },
9601
9602     /**
9603      * Filter the records by a specified property.
9604      * @param {String} field A field on your records
9605      * @param {String/RegExp} value Either a string that the field
9606      * should start with or a RegExp to test against the field
9607      * @param {Boolean} anyMatch True to match any part not just the beginning
9608      */
9609     filter : function(property, value, anyMatch){
9610         var fn = this.createFilterFn(property, value, anyMatch);
9611         return fn ? this.filterBy(fn) : this.clearFilter();
9612     },
9613
9614     /**
9615      * Filter by a function. The specified function will be called with each
9616      * record in this data source. If the function returns true the record is included,
9617      * otherwise it is filtered.
9618      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9619      * @param {Object} scope (optional) The scope of the function (defaults to this)
9620      */
9621     filterBy : function(fn, scope){
9622         this.snapshot = this.snapshot || this.data;
9623         this.data = this.queryBy(fn, scope||this);
9624         this.fireEvent("datachanged", this);
9625     },
9626
9627     /**
9628      * Query the records by a specified property.
9629      * @param {String} field A field on your records
9630      * @param {String/RegExp} value Either a string that the field
9631      * should start with or a RegExp to test against the field
9632      * @param {Boolean} anyMatch True to match any part not just the beginning
9633      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9634      */
9635     query : function(property, value, anyMatch){
9636         var fn = this.createFilterFn(property, value, anyMatch);
9637         return fn ? this.queryBy(fn) : this.data.clone();
9638     },
9639
9640     /**
9641      * Query by a function. The specified function will be called with each
9642      * record in this data source. If the function returns true the record is included
9643      * in the results.
9644      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9645      * @param {Object} scope (optional) The scope of the function (defaults to this)
9646       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9647      **/
9648     queryBy : function(fn, scope){
9649         var data = this.snapshot || this.data;
9650         return data.filterBy(fn, scope||this);
9651     },
9652
9653     /**
9654      * Collects unique values for a particular dataIndex from this store.
9655      * @param {String} dataIndex The property to collect
9656      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9657      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9658      * @return {Array} An array of the unique values
9659      **/
9660     collect : function(dataIndex, allowNull, bypassFilter){
9661         var d = (bypassFilter === true && this.snapshot) ?
9662                 this.snapshot.items : this.data.items;
9663         var v, sv, r = [], l = {};
9664         for(var i = 0, len = d.length; i < len; i++){
9665             v = d[i].data[dataIndex];
9666             sv = String(v);
9667             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9668                 l[sv] = true;
9669                 r[r.length] = v;
9670             }
9671         }
9672         return r;
9673     },
9674
9675     /**
9676      * Revert to a view of the Record cache with no filtering applied.
9677      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9678      */
9679     clearFilter : function(suppressEvent){
9680         if(this.snapshot && this.snapshot != this.data){
9681             this.data = this.snapshot;
9682             delete this.snapshot;
9683             if(suppressEvent !== true){
9684                 this.fireEvent("datachanged", this);
9685             }
9686         }
9687     },
9688
9689     // private
9690     afterEdit : function(record){
9691         if(this.modified.indexOf(record) == -1){
9692             this.modified.push(record);
9693         }
9694         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9695     },
9696     
9697     // private
9698     afterReject : function(record){
9699         this.modified.remove(record);
9700         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9701     },
9702
9703     // private
9704     afterCommit : function(record){
9705         this.modified.remove(record);
9706         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9707     },
9708
9709     /**
9710      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9711      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9712      */
9713     commitChanges : function(){
9714         var m = this.modified.slice(0);
9715         this.modified = [];
9716         for(var i = 0, len = m.length; i < len; i++){
9717             m[i].commit();
9718         }
9719     },
9720
9721     /**
9722      * Cancel outstanding changes on all changed records.
9723      */
9724     rejectChanges : function(){
9725         var m = this.modified.slice(0);
9726         this.modified = [];
9727         for(var i = 0, len = m.length; i < len; i++){
9728             m[i].reject();
9729         }
9730     },
9731
9732     onMetaChange : function(meta, rtype, o){
9733         this.recordType = rtype;
9734         this.fields = rtype.prototype.fields;
9735         delete this.snapshot;
9736         this.sortInfo = meta.sortInfo || this.sortInfo;
9737         this.modified = [];
9738         this.fireEvent('metachange', this, this.reader.meta);
9739     },
9740     
9741     moveIndex : function(data, type)
9742     {
9743         var index = this.indexOf(data);
9744         
9745         var newIndex = index + type;
9746         
9747         this.remove(data);
9748         
9749         this.insert(newIndex, data);
9750         
9751     }
9752 });/*
9753  * Based on:
9754  * Ext JS Library 1.1.1
9755  * Copyright(c) 2006-2007, Ext JS, LLC.
9756  *
9757  * Originally Released Under LGPL - original licence link has changed is not relivant.
9758  *
9759  * Fork - LGPL
9760  * <script type="text/javascript">
9761  */
9762
9763 /**
9764  * @class Roo.data.SimpleStore
9765  * @extends Roo.data.Store
9766  * Small helper class to make creating Stores from Array data easier.
9767  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9768  * @cfg {Array} fields An array of field definition objects, or field name strings.
9769  * @cfg {Array} data The multi-dimensional array of data
9770  * @constructor
9771  * @param {Object} config
9772  */
9773 Roo.data.SimpleStore = function(config){
9774     Roo.data.SimpleStore.superclass.constructor.call(this, {
9775         isLocal : true,
9776         reader: new Roo.data.ArrayReader({
9777                 id: config.id
9778             },
9779             Roo.data.Record.create(config.fields)
9780         ),
9781         proxy : new Roo.data.MemoryProxy(config.data)
9782     });
9783     this.load();
9784 };
9785 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9786  * Based on:
9787  * Ext JS Library 1.1.1
9788  * Copyright(c) 2006-2007, Ext JS, LLC.
9789  *
9790  * Originally Released Under LGPL - original licence link has changed is not relivant.
9791  *
9792  * Fork - LGPL
9793  * <script type="text/javascript">
9794  */
9795
9796 /**
9797 /**
9798  * @extends Roo.data.Store
9799  * @class Roo.data.JsonStore
9800  * Small helper class to make creating Stores for JSON data easier. <br/>
9801 <pre><code>
9802 var store = new Roo.data.JsonStore({
9803     url: 'get-images.php',
9804     root: 'images',
9805     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9806 });
9807 </code></pre>
9808  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9809  * JsonReader and HttpProxy (unless inline data is provided).</b>
9810  * @cfg {Array} fields An array of field definition objects, or field name strings.
9811  * @constructor
9812  * @param {Object} config
9813  */
9814 Roo.data.JsonStore = function(c){
9815     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9816         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9817         reader: new Roo.data.JsonReader(c, c.fields)
9818     }));
9819 };
9820 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9821  * Based on:
9822  * Ext JS Library 1.1.1
9823  * Copyright(c) 2006-2007, Ext JS, LLC.
9824  *
9825  * Originally Released Under LGPL - original licence link has changed is not relivant.
9826  *
9827  * Fork - LGPL
9828  * <script type="text/javascript">
9829  */
9830
9831  
9832 Roo.data.Field = function(config){
9833     if(typeof config == "string"){
9834         config = {name: config};
9835     }
9836     Roo.apply(this, config);
9837     
9838     if(!this.type){
9839         this.type = "auto";
9840     }
9841     
9842     var st = Roo.data.SortTypes;
9843     // named sortTypes are supported, here we look them up
9844     if(typeof this.sortType == "string"){
9845         this.sortType = st[this.sortType];
9846     }
9847     
9848     // set default sortType for strings and dates
9849     if(!this.sortType){
9850         switch(this.type){
9851             case "string":
9852                 this.sortType = st.asUCString;
9853                 break;
9854             case "date":
9855                 this.sortType = st.asDate;
9856                 break;
9857             default:
9858                 this.sortType = st.none;
9859         }
9860     }
9861
9862     // define once
9863     var stripRe = /[\$,%]/g;
9864
9865     // prebuilt conversion function for this field, instead of
9866     // switching every time we're reading a value
9867     if(!this.convert){
9868         var cv, dateFormat = this.dateFormat;
9869         switch(this.type){
9870             case "":
9871             case "auto":
9872             case undefined:
9873                 cv = function(v){ return v; };
9874                 break;
9875             case "string":
9876                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9877                 break;
9878             case "int":
9879                 cv = function(v){
9880                     return v !== undefined && v !== null && v !== '' ?
9881                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9882                     };
9883                 break;
9884             case "float":
9885                 cv = function(v){
9886                     return v !== undefined && v !== null && v !== '' ?
9887                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9888                     };
9889                 break;
9890             case "bool":
9891             case "boolean":
9892                 cv = function(v){ return v === true || v === "true" || v == 1; };
9893                 break;
9894             case "date":
9895                 cv = function(v){
9896                     if(!v){
9897                         return '';
9898                     }
9899                     if(v instanceof Date){
9900                         return v;
9901                     }
9902                     if(dateFormat){
9903                         if(dateFormat == "timestamp"){
9904                             return new Date(v*1000);
9905                         }
9906                         return Date.parseDate(v, dateFormat);
9907                     }
9908                     var parsed = Date.parse(v);
9909                     return parsed ? new Date(parsed) : null;
9910                 };
9911              break;
9912             
9913         }
9914         this.convert = cv;
9915     }
9916 };
9917
9918 Roo.data.Field.prototype = {
9919     dateFormat: null,
9920     defaultValue: "",
9921     mapping: null,
9922     sortType : null,
9923     sortDir : "ASC"
9924 };/*
9925  * Based on:
9926  * Ext JS Library 1.1.1
9927  * Copyright(c) 2006-2007, Ext JS, LLC.
9928  *
9929  * Originally Released Under LGPL - original licence link has changed is not relivant.
9930  *
9931  * Fork - LGPL
9932  * <script type="text/javascript">
9933  */
9934  
9935 // Base class for reading structured data from a data source.  This class is intended to be
9936 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9937
9938 /**
9939  * @class Roo.data.DataReader
9940  * Base class for reading structured data from a data source.  This class is intended to be
9941  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9942  */
9943
9944 Roo.data.DataReader = function(meta, recordType){
9945     
9946     this.meta = meta;
9947     
9948     this.recordType = recordType instanceof Array ? 
9949         Roo.data.Record.create(recordType) : recordType;
9950 };
9951
9952 Roo.data.DataReader.prototype = {
9953      /**
9954      * Create an empty record
9955      * @param {Object} data (optional) - overlay some values
9956      * @return {Roo.data.Record} record created.
9957      */
9958     newRow :  function(d) {
9959         var da =  {};
9960         this.recordType.prototype.fields.each(function(c) {
9961             switch( c.type) {
9962                 case 'int' : da[c.name] = 0; break;
9963                 case 'date' : da[c.name] = new Date(); break;
9964                 case 'float' : da[c.name] = 0.0; break;
9965                 case 'boolean' : da[c.name] = false; break;
9966                 default : da[c.name] = ""; break;
9967             }
9968             
9969         });
9970         return new this.recordType(Roo.apply(da, d));
9971     }
9972     
9973 };/*
9974  * Based on:
9975  * Ext JS Library 1.1.1
9976  * Copyright(c) 2006-2007, Ext JS, LLC.
9977  *
9978  * Originally Released Under LGPL - original licence link has changed is not relivant.
9979  *
9980  * Fork - LGPL
9981  * <script type="text/javascript">
9982  */
9983
9984 /**
9985  * @class Roo.data.DataProxy
9986  * @extends Roo.data.Observable
9987  * This class is an abstract base class for implementations which provide retrieval of
9988  * unformatted data objects.<br>
9989  * <p>
9990  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9991  * (of the appropriate type which knows how to parse the data object) to provide a block of
9992  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9993  * <p>
9994  * Custom implementations must implement the load method as described in
9995  * {@link Roo.data.HttpProxy#load}.
9996  */
9997 Roo.data.DataProxy = function(){
9998     this.addEvents({
9999         /**
10000          * @event beforeload
10001          * Fires before a network request is made to retrieve a data object.
10002          * @param {Object} This DataProxy object.
10003          * @param {Object} params The params parameter to the load function.
10004          */
10005         beforeload : true,
10006         /**
10007          * @event load
10008          * Fires before the load method's callback is called.
10009          * @param {Object} This DataProxy object.
10010          * @param {Object} o The data object.
10011          * @param {Object} arg The callback argument object passed to the load function.
10012          */
10013         load : true,
10014         /**
10015          * @event loadexception
10016          * Fires if an Exception occurs during data retrieval.
10017          * @param {Object} This DataProxy object.
10018          * @param {Object} o The data object.
10019          * @param {Object} arg The callback argument object passed to the load function.
10020          * @param {Object} e The Exception.
10021          */
10022         loadexception : true
10023     });
10024     Roo.data.DataProxy.superclass.constructor.call(this);
10025 };
10026
10027 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10028
10029     /**
10030      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10031      */
10032 /*
10033  * Based on:
10034  * Ext JS Library 1.1.1
10035  * Copyright(c) 2006-2007, Ext JS, LLC.
10036  *
10037  * Originally Released Under LGPL - original licence link has changed is not relivant.
10038  *
10039  * Fork - LGPL
10040  * <script type="text/javascript">
10041  */
10042 /**
10043  * @class Roo.data.MemoryProxy
10044  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10045  * to the Reader when its load method is called.
10046  * @constructor
10047  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10048  */
10049 Roo.data.MemoryProxy = function(data){
10050     if (data.data) {
10051         data = data.data;
10052     }
10053     Roo.data.MemoryProxy.superclass.constructor.call(this);
10054     this.data = data;
10055 };
10056
10057 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10058     /**
10059      * Load data from the requested source (in this case an in-memory
10060      * data object passed to the constructor), read the data object into
10061      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10062      * process that block using the passed callback.
10063      * @param {Object} params This parameter is not used by the MemoryProxy class.
10064      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10065      * object into a block of Roo.data.Records.
10066      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10067      * The function must be passed <ul>
10068      * <li>The Record block object</li>
10069      * <li>The "arg" argument from the load function</li>
10070      * <li>A boolean success indicator</li>
10071      * </ul>
10072      * @param {Object} scope The scope in which to call the callback
10073      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10074      */
10075     load : function(params, reader, callback, scope, arg){
10076         params = params || {};
10077         var result;
10078         try {
10079             result = reader.readRecords(this.data);
10080         }catch(e){
10081             this.fireEvent("loadexception", this, arg, null, e);
10082             callback.call(scope, null, arg, false);
10083             return;
10084         }
10085         callback.call(scope, result, arg, true);
10086     },
10087     
10088     // private
10089     update : function(params, records){
10090         
10091     }
10092 });/*
10093  * Based on:
10094  * Ext JS Library 1.1.1
10095  * Copyright(c) 2006-2007, Ext JS, LLC.
10096  *
10097  * Originally Released Under LGPL - original licence link has changed is not relivant.
10098  *
10099  * Fork - LGPL
10100  * <script type="text/javascript">
10101  */
10102 /**
10103  * @class Roo.data.HttpProxy
10104  * @extends Roo.data.DataProxy
10105  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10106  * configured to reference a certain URL.<br><br>
10107  * <p>
10108  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10109  * from which the running page was served.<br><br>
10110  * <p>
10111  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10112  * <p>
10113  * Be aware that to enable the browser to parse an XML document, the server must set
10114  * the Content-Type header in the HTTP response to "text/xml".
10115  * @constructor
10116  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10117  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10118  * will be used to make the request.
10119  */
10120 Roo.data.HttpProxy = function(conn){
10121     Roo.data.HttpProxy.superclass.constructor.call(this);
10122     // is conn a conn config or a real conn?
10123     this.conn = conn;
10124     this.useAjax = !conn || !conn.events;
10125   
10126 };
10127
10128 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10129     // thse are take from connection...
10130     
10131     /**
10132      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10133      */
10134     /**
10135      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10136      * extra parameters to each request made by this object. (defaults to undefined)
10137      */
10138     /**
10139      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10140      *  to each request made by this object. (defaults to undefined)
10141      */
10142     /**
10143      * @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)
10144      */
10145     /**
10146      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10147      */
10148      /**
10149      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10150      * @type Boolean
10151      */
10152   
10153
10154     /**
10155      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10156      * @type Boolean
10157      */
10158     /**
10159      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10160      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10161      * a finer-grained basis than the DataProxy events.
10162      */
10163     getConnection : function(){
10164         return this.useAjax ? Roo.Ajax : this.conn;
10165     },
10166
10167     /**
10168      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10169      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10170      * process that block using the passed callback.
10171      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10172      * for the request to the remote server.
10173      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10174      * object into a block of Roo.data.Records.
10175      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10176      * The function must be passed <ul>
10177      * <li>The Record block object</li>
10178      * <li>The "arg" argument from the load function</li>
10179      * <li>A boolean success indicator</li>
10180      * </ul>
10181      * @param {Object} scope The scope in which to call the callback
10182      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10183      */
10184     load : function(params, reader, callback, scope, arg){
10185         if(this.fireEvent("beforeload", this, params) !== false){
10186             var  o = {
10187                 params : params || {},
10188                 request: {
10189                     callback : callback,
10190                     scope : scope,
10191                     arg : arg
10192                 },
10193                 reader: reader,
10194                 callback : this.loadResponse,
10195                 scope: this
10196             };
10197             if(this.useAjax){
10198                 Roo.applyIf(o, this.conn);
10199                 if(this.activeRequest){
10200                     Roo.Ajax.abort(this.activeRequest);
10201                 }
10202                 this.activeRequest = Roo.Ajax.request(o);
10203             }else{
10204                 this.conn.request(o);
10205             }
10206         }else{
10207             callback.call(scope||this, null, arg, false);
10208         }
10209     },
10210
10211     // private
10212     loadResponse : function(o, success, response){
10213         delete this.activeRequest;
10214         if(!success){
10215             this.fireEvent("loadexception", this, o, response);
10216             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10217             return;
10218         }
10219         var result;
10220         try {
10221             result = o.reader.read(response);
10222         }catch(e){
10223             this.fireEvent("loadexception", this, o, response, e);
10224             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10225             return;
10226         }
10227         
10228         this.fireEvent("load", this, o, o.request.arg);
10229         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10230     },
10231
10232     // private
10233     update : function(dataSet){
10234
10235     },
10236
10237     // private
10238     updateResponse : function(dataSet){
10239
10240     }
10241 });/*
10242  * Based on:
10243  * Ext JS Library 1.1.1
10244  * Copyright(c) 2006-2007, Ext JS, LLC.
10245  *
10246  * Originally Released Under LGPL - original licence link has changed is not relivant.
10247  *
10248  * Fork - LGPL
10249  * <script type="text/javascript">
10250  */
10251
10252 /**
10253  * @class Roo.data.ScriptTagProxy
10254  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10255  * other than the originating domain of the running page.<br><br>
10256  * <p>
10257  * <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
10258  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10259  * <p>
10260  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10261  * source code that is used as the source inside a &lt;script> tag.<br><br>
10262  * <p>
10263  * In order for the browser to process the returned data, the server must wrap the data object
10264  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10265  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10266  * depending on whether the callback name was passed:
10267  * <p>
10268  * <pre><code>
10269 boolean scriptTag = false;
10270 String cb = request.getParameter("callback");
10271 if (cb != null) {
10272     scriptTag = true;
10273     response.setContentType("text/javascript");
10274 } else {
10275     response.setContentType("application/x-json");
10276 }
10277 Writer out = response.getWriter();
10278 if (scriptTag) {
10279     out.write(cb + "(");
10280 }
10281 out.print(dataBlock.toJsonString());
10282 if (scriptTag) {
10283     out.write(");");
10284 }
10285 </pre></code>
10286  *
10287  * @constructor
10288  * @param {Object} config A configuration object.
10289  */
10290 Roo.data.ScriptTagProxy = function(config){
10291     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10292     Roo.apply(this, config);
10293     this.head = document.getElementsByTagName("head")[0];
10294 };
10295
10296 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10297
10298 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10299     /**
10300      * @cfg {String} url The URL from which to request the data object.
10301      */
10302     /**
10303      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10304      */
10305     timeout : 30000,
10306     /**
10307      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10308      * the server the name of the callback function set up by the load call to process the returned data object.
10309      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10310      * javascript output which calls this named function passing the data object as its only parameter.
10311      */
10312     callbackParam : "callback",
10313     /**
10314      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10315      * name to the request.
10316      */
10317     nocache : true,
10318
10319     /**
10320      * Load data from the configured URL, read the data object into
10321      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10322      * process that block using the passed callback.
10323      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10324      * for the request to the remote server.
10325      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10326      * object into a block of Roo.data.Records.
10327      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10328      * The function must be passed <ul>
10329      * <li>The Record block object</li>
10330      * <li>The "arg" argument from the load function</li>
10331      * <li>A boolean success indicator</li>
10332      * </ul>
10333      * @param {Object} scope The scope in which to call the callback
10334      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10335      */
10336     load : function(params, reader, callback, scope, arg){
10337         if(this.fireEvent("beforeload", this, params) !== false){
10338
10339             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10340
10341             var url = this.url;
10342             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10343             if(this.nocache){
10344                 url += "&_dc=" + (new Date().getTime());
10345             }
10346             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10347             var trans = {
10348                 id : transId,
10349                 cb : "stcCallback"+transId,
10350                 scriptId : "stcScript"+transId,
10351                 params : params,
10352                 arg : arg,
10353                 url : url,
10354                 callback : callback,
10355                 scope : scope,
10356                 reader : reader
10357             };
10358             var conn = this;
10359
10360             window[trans.cb] = function(o){
10361                 conn.handleResponse(o, trans);
10362             };
10363
10364             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10365
10366             if(this.autoAbort !== false){
10367                 this.abort();
10368             }
10369
10370             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10371
10372             var script = document.createElement("script");
10373             script.setAttribute("src", url);
10374             script.setAttribute("type", "text/javascript");
10375             script.setAttribute("id", trans.scriptId);
10376             this.head.appendChild(script);
10377
10378             this.trans = trans;
10379         }else{
10380             callback.call(scope||this, null, arg, false);
10381         }
10382     },
10383
10384     // private
10385     isLoading : function(){
10386         return this.trans ? true : false;
10387     },
10388
10389     /**
10390      * Abort the current server request.
10391      */
10392     abort : function(){
10393         if(this.isLoading()){
10394             this.destroyTrans(this.trans);
10395         }
10396     },
10397
10398     // private
10399     destroyTrans : function(trans, isLoaded){
10400         this.head.removeChild(document.getElementById(trans.scriptId));
10401         clearTimeout(trans.timeoutId);
10402         if(isLoaded){
10403             window[trans.cb] = undefined;
10404             try{
10405                 delete window[trans.cb];
10406             }catch(e){}
10407         }else{
10408             // if hasn't been loaded, wait for load to remove it to prevent script error
10409             window[trans.cb] = function(){
10410                 window[trans.cb] = undefined;
10411                 try{
10412                     delete window[trans.cb];
10413                 }catch(e){}
10414             };
10415         }
10416     },
10417
10418     // private
10419     handleResponse : function(o, trans){
10420         this.trans = false;
10421         this.destroyTrans(trans, true);
10422         var result;
10423         try {
10424             result = trans.reader.readRecords(o);
10425         }catch(e){
10426             this.fireEvent("loadexception", this, o, trans.arg, e);
10427             trans.callback.call(trans.scope||window, null, trans.arg, false);
10428             return;
10429         }
10430         this.fireEvent("load", this, o, trans.arg);
10431         trans.callback.call(trans.scope||window, result, trans.arg, true);
10432     },
10433
10434     // private
10435     handleFailure : function(trans){
10436         this.trans = false;
10437         this.destroyTrans(trans, false);
10438         this.fireEvent("loadexception", this, null, trans.arg);
10439         trans.callback.call(trans.scope||window, null, trans.arg, false);
10440     }
10441 });/*
10442  * Based on:
10443  * Ext JS Library 1.1.1
10444  * Copyright(c) 2006-2007, Ext JS, LLC.
10445  *
10446  * Originally Released Under LGPL - original licence link has changed is not relivant.
10447  *
10448  * Fork - LGPL
10449  * <script type="text/javascript">
10450  */
10451
10452 /**
10453  * @class Roo.data.JsonReader
10454  * @extends Roo.data.DataReader
10455  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10456  * based on mappings in a provided Roo.data.Record constructor.
10457  * 
10458  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10459  * in the reply previously. 
10460  * 
10461  * <p>
10462  * Example code:
10463  * <pre><code>
10464 var RecordDef = Roo.data.Record.create([
10465     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10466     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10467 ]);
10468 var myReader = new Roo.data.JsonReader({
10469     totalProperty: "results",    // The property which contains the total dataset size (optional)
10470     root: "rows",                // The property which contains an Array of row objects
10471     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10472 }, RecordDef);
10473 </code></pre>
10474  * <p>
10475  * This would consume a JSON file like this:
10476  * <pre><code>
10477 { 'results': 2, 'rows': [
10478     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10479     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10480 }
10481 </code></pre>
10482  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10483  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10484  * paged from the remote server.
10485  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10486  * @cfg {String} root name of the property which contains the Array of row objects.
10487  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10488  * @constructor
10489  * Create a new JsonReader
10490  * @param {Object} meta Metadata configuration options
10491  * @param {Object} recordType Either an Array of field definition objects,
10492  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10493  */
10494 Roo.data.JsonReader = function(meta, recordType){
10495     
10496     meta = meta || {};
10497     // set some defaults:
10498     Roo.applyIf(meta, {
10499         totalProperty: 'total',
10500         successProperty : 'success',
10501         root : 'data',
10502         id : 'id'
10503     });
10504     
10505     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10506 };
10507 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10508     
10509     /**
10510      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10511      * Used by Store query builder to append _requestMeta to params.
10512      * 
10513      */
10514     metaFromRemote : false,
10515     /**
10516      * This method is only used by a DataProxy which has retrieved data from a remote server.
10517      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10518      * @return {Object} data A data block which is used by an Roo.data.Store object as
10519      * a cache of Roo.data.Records.
10520      */
10521     read : function(response){
10522         var json = response.responseText;
10523        
10524         var o = /* eval:var:o */ eval("("+json+")");
10525         if(!o) {
10526             throw {message: "JsonReader.read: Json object not found"};
10527         }
10528         
10529         if(o.metaData){
10530             
10531             delete this.ef;
10532             this.metaFromRemote = true;
10533             this.meta = o.metaData;
10534             this.recordType = Roo.data.Record.create(o.metaData.fields);
10535             this.onMetaChange(this.meta, this.recordType, o);
10536         }
10537         return this.readRecords(o);
10538     },
10539
10540     // private function a store will implement
10541     onMetaChange : function(meta, recordType, o){
10542
10543     },
10544
10545     /**
10546          * @ignore
10547          */
10548     simpleAccess: function(obj, subsc) {
10549         return obj[subsc];
10550     },
10551
10552         /**
10553          * @ignore
10554          */
10555     getJsonAccessor: function(){
10556         var re = /[\[\.]/;
10557         return function(expr) {
10558             try {
10559                 return(re.test(expr))
10560                     ? new Function("obj", "return obj." + expr)
10561                     : function(obj){
10562                         return obj[expr];
10563                     };
10564             } catch(e){}
10565             return Roo.emptyFn;
10566         };
10567     }(),
10568
10569     /**
10570      * Create a data block containing Roo.data.Records from an XML document.
10571      * @param {Object} o An object which contains an Array of row objects in the property specified
10572      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10573      * which contains the total size of the dataset.
10574      * @return {Object} data A data block which is used by an Roo.data.Store object as
10575      * a cache of Roo.data.Records.
10576      */
10577     readRecords : function(o){
10578         /**
10579          * After any data loads, the raw JSON data is available for further custom processing.
10580          * @type Object
10581          */
10582         this.o = o;
10583         var s = this.meta, Record = this.recordType,
10584             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10585
10586 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10587         if (!this.ef) {
10588             if(s.totalProperty) {
10589                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10590                 }
10591                 if(s.successProperty) {
10592                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10593                 }
10594                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10595                 if (s.id) {
10596                         var g = this.getJsonAccessor(s.id);
10597                         this.getId = function(rec) {
10598                                 var r = g(rec);  
10599                                 return (r === undefined || r === "") ? null : r;
10600                         };
10601                 } else {
10602                         this.getId = function(){return null;};
10603                 }
10604             this.ef = [];
10605             for(var jj = 0; jj < fl; jj++){
10606                 f = fi[jj];
10607                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10608                 this.ef[jj] = this.getJsonAccessor(map);
10609             }
10610         }
10611
10612         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10613         if(s.totalProperty){
10614             var vt = parseInt(this.getTotal(o), 10);
10615             if(!isNaN(vt)){
10616                 totalRecords = vt;
10617             }
10618         }
10619         if(s.successProperty){
10620             var vs = this.getSuccess(o);
10621             if(vs === false || vs === 'false'){
10622                 success = false;
10623             }
10624         }
10625         var records = [];
10626         for(var i = 0; i < c; i++){
10627                 var n = root[i];
10628             var values = {};
10629             var id = this.getId(n);
10630             for(var j = 0; j < fl; j++){
10631                 f = fi[j];
10632             var v = this.ef[j](n);
10633             if (!f.convert) {
10634                 Roo.log('missing convert for ' + f.name);
10635                 Roo.log(f);
10636                 continue;
10637             }
10638             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10639             }
10640             var record = new Record(values, id);
10641             record.json = n;
10642             records[i] = record;
10643         }
10644         return {
10645             raw : o,
10646             success : success,
10647             records : records,
10648             totalRecords : totalRecords
10649         };
10650     }
10651 });/*
10652  * Based on:
10653  * Ext JS Library 1.1.1
10654  * Copyright(c) 2006-2007, Ext JS, LLC.
10655  *
10656  * Originally Released Under LGPL - original licence link has changed is not relivant.
10657  *
10658  * Fork - LGPL
10659  * <script type="text/javascript">
10660  */
10661
10662 /**
10663  * @class Roo.data.ArrayReader
10664  * @extends Roo.data.DataReader
10665  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10666  * Each element of that Array represents a row of data fields. The
10667  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10668  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10669  * <p>
10670  * Example code:.
10671  * <pre><code>
10672 var RecordDef = Roo.data.Record.create([
10673     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10674     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10675 ]);
10676 var myReader = new Roo.data.ArrayReader({
10677     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10678 }, RecordDef);
10679 </code></pre>
10680  * <p>
10681  * This would consume an Array like this:
10682  * <pre><code>
10683 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10684   </code></pre>
10685  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10686  * @constructor
10687  * Create a new JsonReader
10688  * @param {Object} meta Metadata configuration options.
10689  * @param {Object} recordType Either an Array of field definition objects
10690  * as specified to {@link Roo.data.Record#create},
10691  * or an {@link Roo.data.Record} object
10692  * created using {@link Roo.data.Record#create}.
10693  */
10694 Roo.data.ArrayReader = function(meta, recordType){
10695     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10696 };
10697
10698 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10699     /**
10700      * Create a data block containing Roo.data.Records from an XML document.
10701      * @param {Object} o An Array of row objects which represents the dataset.
10702      * @return {Object} data A data block which is used by an Roo.data.Store object as
10703      * a cache of Roo.data.Records.
10704      */
10705     readRecords : function(o){
10706         var sid = this.meta ? this.meta.id : null;
10707         var recordType = this.recordType, fields = recordType.prototype.fields;
10708         var records = [];
10709         var root = o;
10710             for(var i = 0; i < root.length; i++){
10711                     var n = root[i];
10712                 var values = {};
10713                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10714                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10715                 var f = fields.items[j];
10716                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10717                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10718                 v = f.convert(v);
10719                 values[f.name] = v;
10720             }
10721                 var record = new recordType(values, id);
10722                 record.json = n;
10723                 records[records.length] = record;
10724             }
10725             return {
10726                 records : records,
10727                 totalRecords : records.length
10728             };
10729     }
10730 });/*
10731  * - LGPL
10732  * * 
10733  */
10734
10735 /**
10736  * @class Roo.bootstrap.ComboBox
10737  * @extends Roo.bootstrap.TriggerField
10738  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10739  * @cfg {Boolean} append (true|false) default false
10740  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10741  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10742  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10743  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10744  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10745  * @constructor
10746  * Create a new ComboBox.
10747  * @param {Object} config Configuration options
10748  */
10749 Roo.bootstrap.ComboBox = function(config){
10750     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10751     this.addEvents({
10752         /**
10753          * @event expand
10754          * Fires when the dropdown list is expanded
10755              * @param {Roo.bootstrap.ComboBox} combo This combo box
10756              */
10757         'expand' : true,
10758         /**
10759          * @event collapse
10760          * Fires when the dropdown list is collapsed
10761              * @param {Roo.bootstrap.ComboBox} combo This combo box
10762              */
10763         'collapse' : true,
10764         /**
10765          * @event beforeselect
10766          * Fires before a list item is selected. Return false to cancel the selection.
10767              * @param {Roo.bootstrap.ComboBox} combo This combo box
10768              * @param {Roo.data.Record} record The data record returned from the underlying store
10769              * @param {Number} index The index of the selected item in the dropdown list
10770              */
10771         'beforeselect' : true,
10772         /**
10773          * @event select
10774          * Fires when a list item is selected
10775              * @param {Roo.bootstrap.ComboBox} combo This combo box
10776              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10777              * @param {Number} index The index of the selected item in the dropdown list
10778              */
10779         'select' : true,
10780         /**
10781          * @event beforequery
10782          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10783          * The event object passed has these properties:
10784              * @param {Roo.bootstrap.ComboBox} combo This combo box
10785              * @param {String} query The query
10786              * @param {Boolean} forceAll true to force "all" query
10787              * @param {Boolean} cancel true to cancel the query
10788              * @param {Object} e The query event object
10789              */
10790         'beforequery': true,
10791          /**
10792          * @event add
10793          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10794              * @param {Roo.bootstrap.ComboBox} combo This combo box
10795              */
10796         'add' : true,
10797         /**
10798          * @event edit
10799          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10800              * @param {Roo.bootstrap.ComboBox} combo This combo box
10801              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10802              */
10803         'edit' : true,
10804         /**
10805          * @event remove
10806          * Fires when the remove value from the combobox array
10807              * @param {Roo.bootstrap.ComboBox} combo This combo box
10808              */
10809         'remove' : true,
10810         /**
10811          * @event specialfilter
10812          * Fires when specialfilter
10813             * @param {Roo.bootstrap.ComboBox} combo This combo box
10814             */
10815         'specialfilter' : true
10816         
10817     });
10818     
10819     this.item = [];
10820     this.tickItems = [];
10821     
10822     this.selectedIndex = -1;
10823     if(this.mode == 'local'){
10824         if(config.queryDelay === undefined){
10825             this.queryDelay = 10;
10826         }
10827         if(config.minChars === undefined){
10828             this.minChars = 0;
10829         }
10830     }
10831 };
10832
10833 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10834      
10835     /**
10836      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10837      * rendering into an Roo.Editor, defaults to false)
10838      */
10839     /**
10840      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10841      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10842      */
10843     /**
10844      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10845      */
10846     /**
10847      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10848      * the dropdown list (defaults to undefined, with no header element)
10849      */
10850
10851      /**
10852      * @cfg {String/Roo.Template} tpl The template to use to render the output
10853      */
10854      
10855      /**
10856      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10857      */
10858     listWidth: undefined,
10859     /**
10860      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10861      * mode = 'remote' or 'text' if mode = 'local')
10862      */
10863     displayField: undefined,
10864     
10865     /**
10866      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10867      * mode = 'remote' or 'value' if mode = 'local'). 
10868      * Note: use of a valueField requires the user make a selection
10869      * in order for a value to be mapped.
10870      */
10871     valueField: undefined,
10872     
10873     
10874     /**
10875      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10876      * field's data value (defaults to the underlying DOM element's name)
10877      */
10878     hiddenName: undefined,
10879     /**
10880      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10881      */
10882     listClass: '',
10883     /**
10884      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10885      */
10886     selectedClass: 'active',
10887     
10888     /**
10889      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10890      */
10891     shadow:'sides',
10892     /**
10893      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10894      * anchor positions (defaults to 'tl-bl')
10895      */
10896     listAlign: 'tl-bl?',
10897     /**
10898      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10899      */
10900     maxHeight: 300,
10901     /**
10902      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10903      * query specified by the allQuery config option (defaults to 'query')
10904      */
10905     triggerAction: 'query',
10906     /**
10907      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10908      * (defaults to 4, does not apply if editable = false)
10909      */
10910     minChars : 4,
10911     /**
10912      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10913      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10914      */
10915     typeAhead: false,
10916     /**
10917      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10918      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10919      */
10920     queryDelay: 500,
10921     /**
10922      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10923      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10924      */
10925     pageSize: 0,
10926     /**
10927      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10928      * when editable = true (defaults to false)
10929      */
10930     selectOnFocus:false,
10931     /**
10932      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10933      */
10934     queryParam: 'query',
10935     /**
10936      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10937      * when mode = 'remote' (defaults to 'Loading...')
10938      */
10939     loadingText: 'Loading...',
10940     /**
10941      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10942      */
10943     resizable: false,
10944     /**
10945      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10946      */
10947     handleHeight : 8,
10948     /**
10949      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10950      * traditional select (defaults to true)
10951      */
10952     editable: true,
10953     /**
10954      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10955      */
10956     allQuery: '',
10957     /**
10958      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10959      */
10960     mode: 'remote',
10961     /**
10962      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10963      * listWidth has a higher value)
10964      */
10965     minListWidth : 70,
10966     /**
10967      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10968      * allow the user to set arbitrary text into the field (defaults to false)
10969      */
10970     forceSelection:false,
10971     /**
10972      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10973      * if typeAhead = true (defaults to 250)
10974      */
10975     typeAheadDelay : 250,
10976     /**
10977      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10978      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10979      */
10980     valueNotFoundText : undefined,
10981     /**
10982      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10983      */
10984     blockFocus : false,
10985     
10986     /**
10987      * @cfg {Boolean} disableClear Disable showing of clear button.
10988      */
10989     disableClear : false,
10990     /**
10991      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10992      */
10993     alwaysQuery : false,
10994     
10995     /**
10996      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10997      */
10998     multiple : false,
10999     
11000     /**
11001      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11002      */
11003     invalidClass : "has-warning",
11004     
11005     /**
11006      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11007      */
11008     validClass : "has-success",
11009     
11010     /**
11011      * @cfg {Boolean} specialFilter (true|false) special filter default false
11012      */
11013     specialFilter : false,
11014     
11015     //private
11016     addicon : false,
11017     editicon: false,
11018     
11019     page: 0,
11020     hasQuery: false,
11021     append: false,
11022     loadNext: false,
11023     autoFocus : true,
11024     tickable : false,
11025     btnPosition : 'right',
11026     triggerList : true,
11027     showToggleBtn : true,
11028     // element that contains real text value.. (when hidden is used..)
11029     
11030     getAutoCreate : function()
11031     {
11032         var cfg = false;
11033         
11034         /*
11035          *  Normal ComboBox
11036          */
11037         if(!this.tickable){
11038             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11039             return cfg;
11040         }
11041         
11042         /*
11043          *  ComboBox with tickable selections
11044          */
11045              
11046         var align = this.labelAlign || this.parentLabelAlign();
11047         
11048         cfg = {
11049             cls : 'form-group roo-combobox-tickable' //input-group
11050         };
11051         
11052         var buttons = {
11053             tag : 'div',
11054             cls : 'tickable-buttons',
11055             cn : [
11056                 {
11057                     tag : 'button',
11058                     type : 'button',
11059                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11060                     html : 'Edit'
11061                 },
11062                 {
11063                     tag : 'button',
11064                     type : 'button',
11065                     name : 'ok',
11066                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11067                     html : 'Done'
11068                 },
11069                 {
11070                     tag : 'button',
11071                     type : 'button',
11072                     name : 'cancel',
11073                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11074                     html : 'Cancel'
11075                 }
11076             ]
11077         };
11078         
11079         if(this.editable){
11080             buttons.cn.unshift({
11081                 tag: 'input',
11082                 cls: 'select2-search-field-input'
11083             });
11084         }
11085         
11086         var _this = this;
11087         
11088         Roo.each(buttons.cn, function(c){
11089             if (_this.size) {
11090                 c.cls += ' btn-' + _this.size;
11091             }
11092
11093             if (_this.disabled) {
11094                 c.disabled = true;
11095             }
11096         });
11097         
11098         var box = {
11099             tag: 'div',
11100             cn: [
11101                 {
11102                     tag: 'input',
11103                     type : 'hidden',
11104                     cls: 'form-hidden-field'
11105                 },
11106                 {
11107                     tag: 'ul',
11108                     cls: 'select2-choices',
11109                     cn:[
11110                         {
11111                             tag: 'li',
11112                             cls: 'select2-search-field',
11113                             cn: [
11114
11115                                 buttons
11116                             ]
11117                         }
11118                     ]
11119                 }
11120             ]
11121         }
11122         
11123         var combobox = {
11124             cls: 'select2-container input-group select2-container-multi',
11125             cn: [
11126                 box
11127 //                {
11128 //                    tag: 'ul',
11129 //                    cls: 'typeahead typeahead-long dropdown-menu',
11130 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11131 //                }
11132             ]
11133         };
11134         
11135         if(this.hasFeedback && !this.allowBlank){
11136             
11137             var feedback = {
11138                 tag: 'span',
11139                 cls: 'glyphicon form-control-feedback'
11140             };
11141
11142             combobox.cn.push(feedback);
11143         }
11144         
11145         if (align ==='left' && this.fieldLabel.length) {
11146             
11147                 Roo.log("left and has label");
11148                 cfg.cn = [
11149                     
11150                     {
11151                         tag: 'label',
11152                         'for' :  id,
11153                         cls : 'control-label col-sm-' + this.labelWidth,
11154                         html : this.fieldLabel
11155                         
11156                     },
11157                     {
11158                         cls : "col-sm-" + (12 - this.labelWidth), 
11159                         cn: [
11160                             combobox
11161                         ]
11162                     }
11163                     
11164                 ];
11165         } else if ( this.fieldLabel.length) {
11166                 Roo.log(" label");
11167                  cfg.cn = [
11168                    
11169                     {
11170                         tag: 'label',
11171                         //cls : 'input-group-addon',
11172                         html : this.fieldLabel
11173                         
11174                     },
11175                     
11176                     combobox
11177                     
11178                 ];
11179
11180         } else {
11181             
11182                 Roo.log(" no label && no align");
11183                 cfg = combobox
11184                      
11185                 
11186         }
11187          
11188         var settings=this;
11189         ['xs','sm','md','lg'].map(function(size){
11190             if (settings[size]) {
11191                 cfg.cls += ' col-' + size + '-' + settings[size];
11192             }
11193         });
11194         
11195         return cfg;
11196         
11197     },
11198     
11199     // private
11200     initEvents: function()
11201     {
11202         
11203         if (!this.store) {
11204             throw "can not find store for combo";
11205         }
11206         this.store = Roo.factory(this.store, Roo.data);
11207         
11208         if(this.tickable){
11209             this.initTickableEvents();
11210             return;
11211         }
11212         
11213         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11214         
11215         if(this.hiddenName){
11216             
11217             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11218             
11219             this.hiddenField.dom.value =
11220                 this.hiddenValue !== undefined ? this.hiddenValue :
11221                 this.value !== undefined ? this.value : '';
11222
11223             // prevent input submission
11224             this.el.dom.removeAttribute('name');
11225             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11226              
11227              
11228         }
11229         //if(Roo.isGecko){
11230         //    this.el.dom.setAttribute('autocomplete', 'off');
11231         //}
11232         
11233         var cls = 'x-combo-list';
11234         
11235         //this.list = new Roo.Layer({
11236         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11237         //});
11238         
11239         var _this = this;
11240         
11241         (function(){
11242             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11243             _this.list.setWidth(lw);
11244         }).defer(100);
11245         
11246         this.list.on('mouseover', this.onViewOver, this);
11247         this.list.on('mousemove', this.onViewMove, this);
11248         
11249         this.list.on('scroll', this.onViewScroll, this);
11250         
11251         /*
11252         this.list.swallowEvent('mousewheel');
11253         this.assetHeight = 0;
11254
11255         if(this.title){
11256             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11257             this.assetHeight += this.header.getHeight();
11258         }
11259
11260         this.innerList = this.list.createChild({cls:cls+'-inner'});
11261         this.innerList.on('mouseover', this.onViewOver, this);
11262         this.innerList.on('mousemove', this.onViewMove, this);
11263         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11264         
11265         if(this.allowBlank && !this.pageSize && !this.disableClear){
11266             this.footer = this.list.createChild({cls:cls+'-ft'});
11267             this.pageTb = new Roo.Toolbar(this.footer);
11268            
11269         }
11270         if(this.pageSize){
11271             this.footer = this.list.createChild({cls:cls+'-ft'});
11272             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11273                     {pageSize: this.pageSize});
11274             
11275         }
11276         
11277         if (this.pageTb && this.allowBlank && !this.disableClear) {
11278             var _this = this;
11279             this.pageTb.add(new Roo.Toolbar.Fill(), {
11280                 cls: 'x-btn-icon x-btn-clear',
11281                 text: '&#160;',
11282                 handler: function()
11283                 {
11284                     _this.collapse();
11285                     _this.clearValue();
11286                     _this.onSelect(false, -1);
11287                 }
11288             });
11289         }
11290         if (this.footer) {
11291             this.assetHeight += this.footer.getHeight();
11292         }
11293         */
11294             
11295         if(!this.tpl){
11296             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11297         }
11298
11299         this.view = new Roo.View(this.list, this.tpl, {
11300             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11301         });
11302         //this.view.wrapEl.setDisplayed(false);
11303         this.view.on('click', this.onViewClick, this);
11304         
11305         
11306         
11307         this.store.on('beforeload', this.onBeforeLoad, this);
11308         this.store.on('load', this.onLoad, this);
11309         this.store.on('loadexception', this.onLoadException, this);
11310         /*
11311         if(this.resizable){
11312             this.resizer = new Roo.Resizable(this.list,  {
11313                pinned:true, handles:'se'
11314             });
11315             this.resizer.on('resize', function(r, w, h){
11316                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11317                 this.listWidth = w;
11318                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11319                 this.restrictHeight();
11320             }, this);
11321             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11322         }
11323         */
11324         if(!this.editable){
11325             this.editable = true;
11326             this.setEditable(false);
11327         }
11328         
11329         /*
11330         
11331         if (typeof(this.events.add.listeners) != 'undefined') {
11332             
11333             this.addicon = this.wrap.createChild(
11334                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11335        
11336             this.addicon.on('click', function(e) {
11337                 this.fireEvent('add', this);
11338             }, this);
11339         }
11340         if (typeof(this.events.edit.listeners) != 'undefined') {
11341             
11342             this.editicon = this.wrap.createChild(
11343                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11344             if (this.addicon) {
11345                 this.editicon.setStyle('margin-left', '40px');
11346             }
11347             this.editicon.on('click', function(e) {
11348                 
11349                 // we fire even  if inothing is selected..
11350                 this.fireEvent('edit', this, this.lastData );
11351                 
11352             }, this);
11353         }
11354         */
11355         
11356         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11357             "up" : function(e){
11358                 this.inKeyMode = true;
11359                 this.selectPrev();
11360             },
11361
11362             "down" : function(e){
11363                 if(!this.isExpanded()){
11364                     this.onTriggerClick();
11365                 }else{
11366                     this.inKeyMode = true;
11367                     this.selectNext();
11368                 }
11369             },
11370
11371             "enter" : function(e){
11372 //                this.onViewClick();
11373                 //return true;
11374                 this.collapse();
11375                 
11376                 if(this.fireEvent("specialkey", this, e)){
11377                     this.onViewClick(false);
11378                 }
11379                 
11380                 return true;
11381             },
11382
11383             "esc" : function(e){
11384                 this.collapse();
11385             },
11386
11387             "tab" : function(e){
11388                 this.collapse();
11389                 
11390                 if(this.fireEvent("specialkey", this, e)){
11391                     this.onViewClick(false);
11392                 }
11393                 
11394                 return true;
11395             },
11396
11397             scope : this,
11398
11399             doRelay : function(foo, bar, hname){
11400                 if(hname == 'down' || this.scope.isExpanded()){
11401                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11402                 }
11403                 return true;
11404             },
11405
11406             forceKeyDown: true
11407         });
11408         
11409         
11410         this.queryDelay = Math.max(this.queryDelay || 10,
11411                 this.mode == 'local' ? 10 : 250);
11412         
11413         
11414         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11415         
11416         if(this.typeAhead){
11417             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11418         }
11419         if(this.editable !== false){
11420             this.inputEl().on("keyup", this.onKeyUp, this);
11421         }
11422         if(this.forceSelection){
11423             this.inputEl().on('blur', this.doForce, this);
11424         }
11425         
11426         if(this.multiple){
11427             this.choices = this.el.select('ul.select2-choices', true).first();
11428             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11429         }
11430     },
11431     
11432     initTickableEvents: function()
11433     {   
11434         this.createList();
11435         
11436         if(this.hiddenName){
11437             
11438             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11439             
11440             this.hiddenField.dom.value =
11441                 this.hiddenValue !== undefined ? this.hiddenValue :
11442                 this.value !== undefined ? this.value : '';
11443
11444             // prevent input submission
11445             this.el.dom.removeAttribute('name');
11446             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11447              
11448              
11449         }
11450         
11451 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11452         
11453         this.choices = this.el.select('ul.select2-choices', true).first();
11454         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11455         if(this.triggerList){
11456             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11457         }
11458          
11459         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11460         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11461         
11462         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11463         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11464         
11465         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11466         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11467         
11468         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11469         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11470         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11471         
11472         this.okBtn.hide();
11473         this.cancelBtn.hide();
11474         
11475         var _this = this;
11476         
11477         (function(){
11478             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11479             _this.list.setWidth(lw);
11480         }).defer(100);
11481         
11482         this.list.on('mouseover', this.onViewOver, this);
11483         this.list.on('mousemove', this.onViewMove, this);
11484         
11485         this.list.on('scroll', this.onViewScroll, this);
11486         
11487         if(!this.tpl){
11488             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>';
11489         }
11490
11491         this.view = new Roo.View(this.list, this.tpl, {
11492             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11493         });
11494         
11495         //this.view.wrapEl.setDisplayed(false);
11496         this.view.on('click', this.onViewClick, this);
11497         
11498         
11499         
11500         this.store.on('beforeload', this.onBeforeLoad, this);
11501         this.store.on('load', this.onLoad, this);
11502         this.store.on('loadexception', this.onLoadException, this);
11503         
11504         if(this.editable){
11505             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11506                 "up" : function(e){
11507                     this.inKeyMode = true;
11508                     this.selectPrev();
11509                 },
11510
11511                 "down" : function(e){
11512                     this.inKeyMode = true;
11513                     this.selectNext();
11514                 },
11515
11516                 "enter" : function(e){
11517                     if(this.fireEvent("specialkey", this, e)){
11518                         this.onViewClick(false);
11519                     }
11520                     
11521                     return true;
11522                 },
11523
11524                 "esc" : function(e){
11525                     this.onTickableFooterButtonClick(e, false, false);
11526                 },
11527
11528                 "tab" : function(e){
11529                     this.fireEvent("specialkey", this, e);
11530                     
11531                     this.onTickableFooterButtonClick(e, false, false);
11532                     
11533                     return true;
11534                 },
11535
11536                 scope : this,
11537
11538                 doRelay : function(e, fn, key){
11539                     if(this.scope.isExpanded()){
11540                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11541                     }
11542                     return true;
11543                 },
11544
11545                 forceKeyDown: true
11546             });
11547         }
11548         
11549         this.queryDelay = Math.max(this.queryDelay || 10,
11550                 this.mode == 'local' ? 10 : 250);
11551         
11552         
11553         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11554         
11555         if(this.typeAhead){
11556             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11557         }
11558         
11559         if(this.editable !== false){
11560             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11561         }
11562         
11563     },
11564
11565     onDestroy : function(){
11566         if(this.view){
11567             this.view.setStore(null);
11568             this.view.el.removeAllListeners();
11569             this.view.el.remove();
11570             this.view.purgeListeners();
11571         }
11572         if(this.list){
11573             this.list.dom.innerHTML  = '';
11574         }
11575         
11576         if(this.store){
11577             this.store.un('beforeload', this.onBeforeLoad, this);
11578             this.store.un('load', this.onLoad, this);
11579             this.store.un('loadexception', this.onLoadException, this);
11580         }
11581         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11582     },
11583
11584     // private
11585     fireKey : function(e){
11586         if(e.isNavKeyPress() && !this.list.isVisible()){
11587             this.fireEvent("specialkey", this, e);
11588         }
11589     },
11590
11591     // private
11592     onResize: function(w, h){
11593 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11594 //        
11595 //        if(typeof w != 'number'){
11596 //            // we do not handle it!?!?
11597 //            return;
11598 //        }
11599 //        var tw = this.trigger.getWidth();
11600 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11601 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11602 //        var x = w - tw;
11603 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11604 //            
11605 //        //this.trigger.setStyle('left', x+'px');
11606 //        
11607 //        if(this.list && this.listWidth === undefined){
11608 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11609 //            this.list.setWidth(lw);
11610 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11611 //        }
11612         
11613     
11614         
11615     },
11616
11617     /**
11618      * Allow or prevent the user from directly editing the field text.  If false is passed,
11619      * the user will only be able to select from the items defined in the dropdown list.  This method
11620      * is the runtime equivalent of setting the 'editable' config option at config time.
11621      * @param {Boolean} value True to allow the user to directly edit the field text
11622      */
11623     setEditable : function(value){
11624         if(value == this.editable){
11625             return;
11626         }
11627         this.editable = value;
11628         if(!value){
11629             this.inputEl().dom.setAttribute('readOnly', true);
11630             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11631             this.inputEl().addClass('x-combo-noedit');
11632         }else{
11633             this.inputEl().dom.setAttribute('readOnly', false);
11634             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11635             this.inputEl().removeClass('x-combo-noedit');
11636         }
11637     },
11638
11639     // private
11640     
11641     onBeforeLoad : function(combo,opts){
11642         if(!this.hasFocus){
11643             return;
11644         }
11645          if (!opts.add) {
11646             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11647          }
11648         this.restrictHeight();
11649         this.selectedIndex = -1;
11650     },
11651
11652     // private
11653     onLoad : function(){
11654         
11655         this.hasQuery = false;
11656         
11657         if(!this.hasFocus){
11658             return;
11659         }
11660         
11661         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11662             this.loading.hide();
11663         }
11664              
11665         if(this.store.getCount() > 0){
11666             this.expand();
11667             this.restrictHeight();
11668             if(this.lastQuery == this.allQuery){
11669                 if(this.editable && !this.tickable){
11670                     this.inputEl().dom.select();
11671                 }
11672                 
11673                 if(
11674                     !this.selectByValue(this.value, true) &&
11675                     this.autoFocus && 
11676                     (
11677                         !this.store.lastOptions ||
11678                         typeof(this.store.lastOptions.add) == 'undefined' || 
11679                         this.store.lastOptions.add != true
11680                     )
11681                 ){
11682                     this.select(0, true);
11683                 }
11684             }else{
11685                 if(this.autoFocus){
11686                     this.selectNext();
11687                 }
11688                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11689                     this.taTask.delay(this.typeAheadDelay);
11690                 }
11691             }
11692         }else{
11693             this.onEmptyResults();
11694         }
11695         
11696         //this.el.focus();
11697     },
11698     // private
11699     onLoadException : function()
11700     {
11701         this.hasQuery = false;
11702         
11703         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11704             this.loading.hide();
11705         }
11706         
11707         if(this.tickable && this.editable){
11708             return;
11709         }
11710         
11711         this.collapse();
11712         
11713         Roo.log(this.store.reader.jsonData);
11714         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11715             // fixme
11716             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11717         }
11718         
11719         
11720     },
11721     // private
11722     onTypeAhead : function(){
11723         if(this.store.getCount() > 0){
11724             var r = this.store.getAt(0);
11725             var newValue = r.data[this.displayField];
11726             var len = newValue.length;
11727             var selStart = this.getRawValue().length;
11728             
11729             if(selStart != len){
11730                 this.setRawValue(newValue);
11731                 this.selectText(selStart, newValue.length);
11732             }
11733         }
11734     },
11735
11736     // private
11737     onSelect : function(record, index){
11738         
11739         if(this.fireEvent('beforeselect', this, record, index) !== false){
11740         
11741             this.setFromData(index > -1 ? record.data : false);
11742             
11743             this.collapse();
11744             this.fireEvent('select', this, record, index);
11745         }
11746     },
11747
11748     /**
11749      * Returns the currently selected field value or empty string if no value is set.
11750      * @return {String} value The selected value
11751      */
11752     getValue : function(){
11753         
11754         if(this.multiple){
11755             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11756         }
11757         
11758         if(this.valueField){
11759             return typeof this.value != 'undefined' ? this.value : '';
11760         }else{
11761             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11762         }
11763     },
11764
11765     /**
11766      * Clears any text/value currently set in the field
11767      */
11768     clearValue : function(){
11769         if(this.hiddenField){
11770             this.hiddenField.dom.value = '';
11771         }
11772         this.value = '';
11773         this.setRawValue('');
11774         this.lastSelectionText = '';
11775         this.lastData = false;
11776         
11777     },
11778
11779     /**
11780      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11781      * will be displayed in the field.  If the value does not match the data value of an existing item,
11782      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11783      * Otherwise the field will be blank (although the value will still be set).
11784      * @param {String} value The value to match
11785      */
11786     setValue : function(v){
11787         if(this.multiple){
11788             this.syncValue();
11789             return;
11790         }
11791         
11792         var text = v;
11793         if(this.valueField){
11794             var r = this.findRecord(this.valueField, v);
11795             if(r){
11796                 text = r.data[this.displayField];
11797             }else if(this.valueNotFoundText !== undefined){
11798                 text = this.valueNotFoundText;
11799             }
11800         }
11801         this.lastSelectionText = text;
11802         if(this.hiddenField){
11803             this.hiddenField.dom.value = v;
11804         }
11805         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11806         this.value = v;
11807     },
11808     /**
11809      * @property {Object} the last set data for the element
11810      */
11811     
11812     lastData : false,
11813     /**
11814      * Sets the value of the field based on a object which is related to the record format for the store.
11815      * @param {Object} value the value to set as. or false on reset?
11816      */
11817     setFromData : function(o){
11818         
11819         if(this.multiple){
11820             this.addItem(o);
11821             return;
11822         }
11823             
11824         var dv = ''; // display value
11825         var vv = ''; // value value..
11826         this.lastData = o;
11827         if (this.displayField) {
11828             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11829         } else {
11830             // this is an error condition!!!
11831             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11832         }
11833         
11834         if(this.valueField){
11835             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11836         }
11837         
11838         if(this.hiddenField){
11839             this.hiddenField.dom.value = vv;
11840             
11841             this.lastSelectionText = dv;
11842             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11843             this.value = vv;
11844             return;
11845         }
11846         // no hidden field.. - we store the value in 'value', but still display
11847         // display field!!!!
11848         this.lastSelectionText = dv;
11849         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11850         this.value = vv;
11851         
11852         
11853     },
11854     // private
11855     reset : function(){
11856         // overridden so that last data is reset..
11857         
11858         if(this.multiple){
11859             this.clearItem();
11860             return;
11861         }
11862         
11863         this.setValue(this.originalValue);
11864         this.clearInvalid();
11865         this.lastData = false;
11866         if (this.view) {
11867             this.view.clearSelections();
11868         }
11869     },
11870     // private
11871     findRecord : function(prop, value){
11872         var record;
11873         if(this.store.getCount() > 0){
11874             this.store.each(function(r){
11875                 if(r.data[prop] == value){
11876                     record = r;
11877                     return false;
11878                 }
11879                 return true;
11880             });
11881         }
11882         return record;
11883     },
11884     
11885     getName: function()
11886     {
11887         // returns hidden if it's set..
11888         if (!this.rendered) {return ''};
11889         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11890         
11891     },
11892     // private
11893     onViewMove : function(e, t){
11894         this.inKeyMode = false;
11895     },
11896
11897     // private
11898     onViewOver : function(e, t){
11899         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11900             return;
11901         }
11902         var item = this.view.findItemFromChild(t);
11903         
11904         if(item){
11905             var index = this.view.indexOf(item);
11906             this.select(index, false);
11907         }
11908     },
11909
11910     // private
11911     onViewClick : function(view, doFocus, el, e)
11912     {
11913         var index = this.view.getSelectedIndexes()[0];
11914         
11915         var r = this.store.getAt(index);
11916         
11917         if(this.tickable){
11918             
11919             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
11920                 return;
11921             }
11922             
11923             var rm = false;
11924             var _this = this;
11925             
11926             Roo.each(this.tickItems, function(v,k){
11927                 
11928                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11929                     _this.tickItems.splice(k, 1);
11930                     
11931                     if(typeof(e) == 'undefined' && view == false){
11932                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
11933                     }
11934                     
11935                     rm = true;
11936                     return;
11937                 }
11938             });
11939             
11940             if(rm){
11941                 return;
11942             }
11943             
11944             this.tickItems.push(r.data);
11945             
11946             if(typeof(e) == 'undefined' && view == false){
11947                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
11948             }
11949                     
11950             return;
11951         }
11952         
11953         if(r){
11954             this.onSelect(r, index);
11955         }
11956         if(doFocus !== false && !this.blockFocus){
11957             this.inputEl().focus();
11958         }
11959     },
11960
11961     // private
11962     restrictHeight : function(){
11963         //this.innerList.dom.style.height = '';
11964         //var inner = this.innerList.dom;
11965         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11966         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11967         //this.list.beginUpdate();
11968         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11969         this.list.alignTo(this.inputEl(), this.listAlign);
11970         this.list.alignTo(this.inputEl(), this.listAlign);
11971         //this.list.endUpdate();
11972     },
11973
11974     // private
11975     onEmptyResults : function(){
11976         
11977         if(this.tickable && this.editable){
11978             this.restrictHeight();
11979             return;
11980         }
11981         
11982         this.collapse();
11983     },
11984
11985     /**
11986      * Returns true if the dropdown list is expanded, else false.
11987      */
11988     isExpanded : function(){
11989         return this.list.isVisible();
11990     },
11991
11992     /**
11993      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11994      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11995      * @param {String} value The data value of the item to select
11996      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11997      * selected item if it is not currently in view (defaults to true)
11998      * @return {Boolean} True if the value matched an item in the list, else false
11999      */
12000     selectByValue : function(v, scrollIntoView){
12001         if(v !== undefined && v !== null){
12002             var r = this.findRecord(this.valueField || this.displayField, v);
12003             if(r){
12004                 this.select(this.store.indexOf(r), scrollIntoView);
12005                 return true;
12006             }
12007         }
12008         return false;
12009     },
12010
12011     /**
12012      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12013      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12014      * @param {Number} index The zero-based index of the list item to select
12015      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12016      * selected item if it is not currently in view (defaults to true)
12017      */
12018     select : function(index, scrollIntoView){
12019         this.selectedIndex = index;
12020         this.view.select(index);
12021         if(scrollIntoView !== false){
12022             var el = this.view.getNode(index);
12023             /*
12024              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12025              */
12026             if(el){
12027                 this.list.scrollChildIntoView(el, false);
12028             }
12029         }
12030     },
12031
12032     // private
12033     selectNext : function(){
12034         var ct = this.store.getCount();
12035         if(ct > 0){
12036             if(this.selectedIndex == -1){
12037                 this.select(0);
12038             }else if(this.selectedIndex < ct-1){
12039                 this.select(this.selectedIndex+1);
12040             }
12041         }
12042     },
12043
12044     // private
12045     selectPrev : function(){
12046         var ct = this.store.getCount();
12047         if(ct > 0){
12048             if(this.selectedIndex == -1){
12049                 this.select(0);
12050             }else if(this.selectedIndex != 0){
12051                 this.select(this.selectedIndex-1);
12052             }
12053         }
12054     },
12055
12056     // private
12057     onKeyUp : function(e){
12058         if(this.editable !== false && !e.isSpecialKey()){
12059             this.lastKey = e.getKey();
12060             this.dqTask.delay(this.queryDelay);
12061         }
12062     },
12063
12064     // private
12065     validateBlur : function(){
12066         return !this.list || !this.list.isVisible();   
12067     },
12068
12069     // private
12070     initQuery : function(){
12071         
12072         var v = this.getRawValue();
12073         
12074         if(this.tickable && this.editable){
12075             v = this.tickableInputEl().getValue();
12076         }
12077         
12078         this.doQuery(v);
12079     },
12080
12081     // private
12082     doForce : function(){
12083         if(this.inputEl().dom.value.length > 0){
12084             this.inputEl().dom.value =
12085                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12086              
12087         }
12088     },
12089
12090     /**
12091      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12092      * query allowing the query action to be canceled if needed.
12093      * @param {String} query The SQL query to execute
12094      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12095      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12096      * saved in the current store (defaults to false)
12097      */
12098     doQuery : function(q, forceAll){
12099         
12100         if(q === undefined || q === null){
12101             q = '';
12102         }
12103         var qe = {
12104             query: q,
12105             forceAll: forceAll,
12106             combo: this,
12107             cancel:false
12108         };
12109         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12110             return false;
12111         }
12112         q = qe.query;
12113         
12114         forceAll = qe.forceAll;
12115         if(forceAll === true || (q.length >= this.minChars)){
12116             
12117             this.hasQuery = true;
12118             
12119             if(this.lastQuery != q || this.alwaysQuery){
12120                 this.lastQuery = q;
12121                 if(this.mode == 'local'){
12122                     this.selectedIndex = -1;
12123                     if(forceAll){
12124                         this.store.clearFilter();
12125                     }else{
12126                         
12127                         if(this.specialFilter){
12128                             this.fireEvent('specialfilter', this);
12129                             this.onLoad();
12130                             return;
12131                         }
12132                         
12133                         this.store.filter(this.displayField, q);
12134                     }
12135                     
12136                     this.store.fireEvent("datachanged", this.store);
12137                     
12138                     this.onLoad();
12139                     
12140                     
12141                 }else{
12142                     
12143                     this.store.baseParams[this.queryParam] = q;
12144                     
12145                     var options = {params : this.getParams(q)};
12146                     
12147                     if(this.loadNext){
12148                         options.add = true;
12149                         options.params.start = this.page * this.pageSize;
12150                     }
12151                     
12152                     this.store.load(options);
12153                     
12154                     /*
12155                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12156                      *  we should expand the list on onLoad
12157                      *  so command out it
12158                      */
12159 //                    this.expand();
12160                 }
12161             }else{
12162                 this.selectedIndex = -1;
12163                 this.onLoad();   
12164             }
12165         }
12166         
12167         this.loadNext = false;
12168     },
12169     
12170     // private
12171     getParams : function(q){
12172         var p = {};
12173         //p[this.queryParam] = q;
12174         
12175         if(this.pageSize){
12176             p.start = 0;
12177             p.limit = this.pageSize;
12178         }
12179         return p;
12180     },
12181
12182     /**
12183      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12184      */
12185     collapse : function(){
12186         if(!this.isExpanded()){
12187             return;
12188         }
12189         
12190         this.list.hide();
12191         
12192         if(this.tickable){
12193             this.hasFocus = false;
12194             this.okBtn.hide();
12195             this.cancelBtn.hide();
12196             this.trigger.show();
12197             
12198             if(this.editable){
12199                 this.tickableInputEl().dom.value = '';
12200                 this.tickableInputEl().blur();
12201             }
12202             
12203         }
12204         
12205         Roo.get(document).un('mousedown', this.collapseIf, this);
12206         Roo.get(document).un('mousewheel', this.collapseIf, this);
12207         if (!this.editable) {
12208             Roo.get(document).un('keydown', this.listKeyPress, this);
12209         }
12210         this.fireEvent('collapse', this);
12211     },
12212
12213     // private
12214     collapseIf : function(e){
12215         var in_combo  = e.within(this.el);
12216         var in_list =  e.within(this.list);
12217         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12218         
12219         if (in_combo || in_list || is_list) {
12220             //e.stopPropagation();
12221             return;
12222         }
12223         
12224         if(this.tickable){
12225             this.onTickableFooterButtonClick(e, false, false);
12226         }
12227
12228         this.collapse();
12229         
12230     },
12231
12232     /**
12233      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12234      */
12235     expand : function(){
12236        
12237         if(this.isExpanded() || !this.hasFocus){
12238             return;
12239         }
12240         
12241         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12242         this.list.setWidth(lw);
12243         
12244         
12245          Roo.log('expand');
12246         
12247         this.list.show();
12248         
12249         this.restrictHeight();
12250         
12251         if(this.tickable){
12252             
12253             this.tickItems = Roo.apply([], this.item);
12254             
12255             this.okBtn.show();
12256             this.cancelBtn.show();
12257             this.trigger.hide();
12258             
12259             if(this.editable){
12260                 this.tickableInputEl().focus();
12261             }
12262             
12263         }
12264         
12265         Roo.get(document).on('mousedown', this.collapseIf, this);
12266         Roo.get(document).on('mousewheel', this.collapseIf, this);
12267         if (!this.editable) {
12268             Roo.get(document).on('keydown', this.listKeyPress, this);
12269         }
12270         
12271         this.fireEvent('expand', this);
12272     },
12273
12274     // private
12275     // Implements the default empty TriggerField.onTriggerClick function
12276     onTriggerClick : function(e)
12277     {
12278         Roo.log('trigger click');
12279         
12280         if(this.disabled || !this.triggerList){
12281             return;
12282         }
12283         
12284         this.page = 0;
12285         this.loadNext = false;
12286         
12287         if(this.isExpanded()){
12288             this.collapse();
12289             if (!this.blockFocus) {
12290                 this.inputEl().focus();
12291             }
12292             
12293         }else {
12294             this.hasFocus = true;
12295             if(this.triggerAction == 'all') {
12296                 this.doQuery(this.allQuery, true);
12297             } else {
12298                 this.doQuery(this.getRawValue());
12299             }
12300             if (!this.blockFocus) {
12301                 this.inputEl().focus();
12302             }
12303         }
12304     },
12305     
12306     onTickableTriggerClick : function(e)
12307     {
12308         if(this.disabled){
12309             return;
12310         }
12311         
12312         this.page = 0;
12313         this.loadNext = false;
12314         this.hasFocus = true;
12315         
12316         if(this.triggerAction == 'all') {
12317             this.doQuery(this.allQuery, true);
12318         } else {
12319             this.doQuery(this.getRawValue());
12320         }
12321     },
12322     
12323     onSearchFieldClick : function(e)
12324     {
12325         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12326             this.onTickableFooterButtonClick(e, false, false);
12327             return;
12328         }
12329         
12330         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12331             return;
12332         }
12333         
12334         this.page = 0;
12335         this.loadNext = false;
12336         this.hasFocus = true;
12337         
12338         if(this.triggerAction == 'all') {
12339             this.doQuery(this.allQuery, true);
12340         } else {
12341             this.doQuery(this.getRawValue());
12342         }
12343     },
12344     
12345     listKeyPress : function(e)
12346     {
12347         //Roo.log('listkeypress');
12348         // scroll to first matching element based on key pres..
12349         if (e.isSpecialKey()) {
12350             return false;
12351         }
12352         var k = String.fromCharCode(e.getKey()).toUpperCase();
12353         //Roo.log(k);
12354         var match  = false;
12355         var csel = this.view.getSelectedNodes();
12356         var cselitem = false;
12357         if (csel.length) {
12358             var ix = this.view.indexOf(csel[0]);
12359             cselitem  = this.store.getAt(ix);
12360             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12361                 cselitem = false;
12362             }
12363             
12364         }
12365         
12366         this.store.each(function(v) { 
12367             if (cselitem) {
12368                 // start at existing selection.
12369                 if (cselitem.id == v.id) {
12370                     cselitem = false;
12371                 }
12372                 return true;
12373             }
12374                 
12375             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12376                 match = this.store.indexOf(v);
12377                 return false;
12378             }
12379             return true;
12380         }, this);
12381         
12382         if (match === false) {
12383             return true; // no more action?
12384         }
12385         // scroll to?
12386         this.view.select(match);
12387         var sn = Roo.get(this.view.getSelectedNodes()[0])
12388         sn.scrollIntoView(sn.dom.parentNode, false);
12389     },
12390     
12391     onViewScroll : function(e, t){
12392         
12393         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){
12394             return;
12395         }
12396         
12397         this.hasQuery = true;
12398         
12399         this.loading = this.list.select('.loading', true).first();
12400         
12401         if(this.loading === null){
12402             this.list.createChild({
12403                 tag: 'div',
12404                 cls: 'loading select2-more-results select2-active',
12405                 html: 'Loading more results...'
12406             })
12407             
12408             this.loading = this.list.select('.loading', true).first();
12409             
12410             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12411             
12412             this.loading.hide();
12413         }
12414         
12415         this.loading.show();
12416         
12417         var _combo = this;
12418         
12419         this.page++;
12420         this.loadNext = true;
12421         
12422         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12423         
12424         return;
12425     },
12426     
12427     addItem : function(o)
12428     {   
12429         var dv = ''; // display value
12430         
12431         if (this.displayField) {
12432             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12433         } else {
12434             // this is an error condition!!!
12435             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12436         }
12437         
12438         if(!dv.length){
12439             return;
12440         }
12441         
12442         var choice = this.choices.createChild({
12443             tag: 'li',
12444             cls: 'select2-search-choice',
12445             cn: [
12446                 {
12447                     tag: 'div',
12448                     html: dv
12449                 },
12450                 {
12451                     tag: 'a',
12452                     href: '#',
12453                     cls: 'select2-search-choice-close',
12454                     tabindex: '-1'
12455                 }
12456             ]
12457             
12458         }, this.searchField);
12459         
12460         var close = choice.select('a.select2-search-choice-close', true).first()
12461         
12462         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12463         
12464         this.item.push(o);
12465         
12466         this.lastData = o;
12467         
12468         this.syncValue();
12469         
12470         this.inputEl().dom.value = '';
12471         
12472         this.validate();
12473     },
12474     
12475     onRemoveItem : function(e, _self, o)
12476     {
12477         e.preventDefault();
12478         
12479         this.lastItem = Roo.apply([], this.item);
12480         
12481         var index = this.item.indexOf(o.data) * 1;
12482         
12483         if( index < 0){
12484             Roo.log('not this item?!');
12485             return;
12486         }
12487         
12488         this.item.splice(index, 1);
12489         o.item.remove();
12490         
12491         this.syncValue();
12492         
12493         this.fireEvent('remove', this, e);
12494         
12495         this.validate();
12496         
12497     },
12498     
12499     syncValue : function()
12500     {
12501         if(!this.item.length){
12502             this.clearValue();
12503             return;
12504         }
12505             
12506         var value = [];
12507         var _this = this;
12508         Roo.each(this.item, function(i){
12509             if(_this.valueField){
12510                 value.push(i[_this.valueField]);
12511                 return;
12512             }
12513
12514             value.push(i);
12515         });
12516
12517         this.value = value.join(',');
12518
12519         if(this.hiddenField){
12520             this.hiddenField.dom.value = this.value;
12521         }
12522         
12523         this.store.fireEvent("datachanged", this.store);
12524     },
12525     
12526     clearItem : function()
12527     {
12528         if(!this.multiple){
12529             return;
12530         }
12531         
12532         this.item = [];
12533         
12534         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12535            c.remove();
12536         });
12537         
12538         this.syncValue();
12539         
12540         this.validate();
12541     },
12542     
12543     inputEl: function ()
12544     {
12545         if(this.tickable){
12546             return this.searchField;
12547         }
12548         return this.el.select('input.form-control',true).first();
12549     },
12550     
12551     
12552     onTickableFooterButtonClick : function(e, btn, el)
12553     {
12554         e.preventDefault();
12555         
12556         this.lastItem = Roo.apply([], this.item);
12557         
12558         if(btn && btn.name == 'cancel'){
12559             this.tickItems = Roo.apply([], this.item);
12560             this.collapse();
12561             return;
12562         }
12563         
12564         this.clearItem();
12565         
12566         var _this = this;
12567         
12568         Roo.each(this.tickItems, function(o){
12569             _this.addItem(o);
12570         });
12571         
12572         this.collapse();
12573         
12574     },
12575     
12576     validate : function()
12577     {
12578         var v = this.getRawValue();
12579         
12580         if(this.multiple){
12581             v = this.getValue();
12582         }
12583         
12584         if(this.disabled || this.allowBlank || v.length){
12585             this.markValid();
12586             return true;
12587         }
12588         
12589         this.markInvalid();
12590         return false;
12591     },
12592     
12593     tickableInputEl : function()
12594     {
12595         if(!this.tickable || !this.editable){
12596             return this.inputEl();
12597         }
12598         
12599         return this.inputEl().select('.select2-search-field-input', true).first();
12600     }
12601     
12602     
12603
12604     /** 
12605     * @cfg {Boolean} grow 
12606     * @hide 
12607     */
12608     /** 
12609     * @cfg {Number} growMin 
12610     * @hide 
12611     */
12612     /** 
12613     * @cfg {Number} growMax 
12614     * @hide 
12615     */
12616     /**
12617      * @hide
12618      * @method autoSize
12619      */
12620 });
12621 /*
12622  * Based on:
12623  * Ext JS Library 1.1.1
12624  * Copyright(c) 2006-2007, Ext JS, LLC.
12625  *
12626  * Originally Released Under LGPL - original licence link has changed is not relivant.
12627  *
12628  * Fork - LGPL
12629  * <script type="text/javascript">
12630  */
12631
12632 /**
12633  * @class Roo.View
12634  * @extends Roo.util.Observable
12635  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12636  * This class also supports single and multi selection modes. <br>
12637  * Create a data model bound view:
12638  <pre><code>
12639  var store = new Roo.data.Store(...);
12640
12641  var view = new Roo.View({
12642     el : "my-element",
12643     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12644  
12645     singleSelect: true,
12646     selectedClass: "ydataview-selected",
12647     store: store
12648  });
12649
12650  // listen for node click?
12651  view.on("click", function(vw, index, node, e){
12652  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12653  });
12654
12655  // load XML data
12656  dataModel.load("foobar.xml");
12657  </code></pre>
12658  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12659  * <br><br>
12660  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12661  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12662  * 
12663  * Note: old style constructor is still suported (container, template, config)
12664  * 
12665  * @constructor
12666  * Create a new View
12667  * @param {Object} config The config object
12668  * 
12669  */
12670 Roo.View = function(config, depreciated_tpl, depreciated_config){
12671     
12672     this.parent = false;
12673     
12674     if (typeof(depreciated_tpl) == 'undefined') {
12675         // new way.. - universal constructor.
12676         Roo.apply(this, config);
12677         this.el  = Roo.get(this.el);
12678     } else {
12679         // old format..
12680         this.el  = Roo.get(config);
12681         this.tpl = depreciated_tpl;
12682         Roo.apply(this, depreciated_config);
12683     }
12684     this.wrapEl  = this.el.wrap().wrap();
12685     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12686     
12687     
12688     if(typeof(this.tpl) == "string"){
12689         this.tpl = new Roo.Template(this.tpl);
12690     } else {
12691         // support xtype ctors..
12692         this.tpl = new Roo.factory(this.tpl, Roo);
12693     }
12694     
12695     
12696     this.tpl.compile();
12697     
12698     /** @private */
12699     this.addEvents({
12700         /**
12701          * @event beforeclick
12702          * Fires before a click is processed. Returns false to cancel the default action.
12703          * @param {Roo.View} this
12704          * @param {Number} index The index of the target node
12705          * @param {HTMLElement} node The target node
12706          * @param {Roo.EventObject} e The raw event object
12707          */
12708             "beforeclick" : true,
12709         /**
12710          * @event click
12711          * Fires when a template node is clicked.
12712          * @param {Roo.View} this
12713          * @param {Number} index The index of the target node
12714          * @param {HTMLElement} node The target node
12715          * @param {Roo.EventObject} e The raw event object
12716          */
12717             "click" : true,
12718         /**
12719          * @event dblclick
12720          * Fires when a template node is double clicked.
12721          * @param {Roo.View} this
12722          * @param {Number} index The index of the target node
12723          * @param {HTMLElement} node The target node
12724          * @param {Roo.EventObject} e The raw event object
12725          */
12726             "dblclick" : true,
12727         /**
12728          * @event contextmenu
12729          * Fires when a template node is right clicked.
12730          * @param {Roo.View} this
12731          * @param {Number} index The index of the target node
12732          * @param {HTMLElement} node The target node
12733          * @param {Roo.EventObject} e The raw event object
12734          */
12735             "contextmenu" : true,
12736         /**
12737          * @event selectionchange
12738          * Fires when the selected nodes change.
12739          * @param {Roo.View} this
12740          * @param {Array} selections Array of the selected nodes
12741          */
12742             "selectionchange" : true,
12743     
12744         /**
12745          * @event beforeselect
12746          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12747          * @param {Roo.View} this
12748          * @param {HTMLElement} node The node to be selected
12749          * @param {Array} selections Array of currently selected nodes
12750          */
12751             "beforeselect" : true,
12752         /**
12753          * @event preparedata
12754          * Fires on every row to render, to allow you to change the data.
12755          * @param {Roo.View} this
12756          * @param {Object} data to be rendered (change this)
12757          */
12758           "preparedata" : true
12759           
12760           
12761         });
12762
12763
12764
12765     this.el.on({
12766         "click": this.onClick,
12767         "dblclick": this.onDblClick,
12768         "contextmenu": this.onContextMenu,
12769         scope:this
12770     });
12771
12772     this.selections = [];
12773     this.nodes = [];
12774     this.cmp = new Roo.CompositeElementLite([]);
12775     if(this.store){
12776         this.store = Roo.factory(this.store, Roo.data);
12777         this.setStore(this.store, true);
12778     }
12779     
12780     if ( this.footer && this.footer.xtype) {
12781            
12782          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12783         
12784         this.footer.dataSource = this.store
12785         this.footer.container = fctr;
12786         this.footer = Roo.factory(this.footer, Roo);
12787         fctr.insertFirst(this.el);
12788         
12789         // this is a bit insane - as the paging toolbar seems to detach the el..
12790 //        dom.parentNode.parentNode.parentNode
12791          // they get detached?
12792     }
12793     
12794     
12795     Roo.View.superclass.constructor.call(this);
12796     
12797     
12798 };
12799
12800 Roo.extend(Roo.View, Roo.util.Observable, {
12801     
12802      /**
12803      * @cfg {Roo.data.Store} store Data store to load data from.
12804      */
12805     store : false,
12806     
12807     /**
12808      * @cfg {String|Roo.Element} el The container element.
12809      */
12810     el : '',
12811     
12812     /**
12813      * @cfg {String|Roo.Template} tpl The template used by this View 
12814      */
12815     tpl : false,
12816     /**
12817      * @cfg {String} dataName the named area of the template to use as the data area
12818      *                          Works with domtemplates roo-name="name"
12819      */
12820     dataName: false,
12821     /**
12822      * @cfg {String} selectedClass The css class to add to selected nodes
12823      */
12824     selectedClass : "x-view-selected",
12825      /**
12826      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12827      */
12828     emptyText : "",
12829     
12830     /**
12831      * @cfg {String} text to display on mask (default Loading)
12832      */
12833     mask : false,
12834     /**
12835      * @cfg {Boolean} multiSelect Allow multiple selection
12836      */
12837     multiSelect : false,
12838     /**
12839      * @cfg {Boolean} singleSelect Allow single selection
12840      */
12841     singleSelect:  false,
12842     
12843     /**
12844      * @cfg {Boolean} toggleSelect - selecting 
12845      */
12846     toggleSelect : false,
12847     
12848     /**
12849      * @cfg {Boolean} tickable - selecting 
12850      */
12851     tickable : false,
12852     
12853     /**
12854      * Returns the element this view is bound to.
12855      * @return {Roo.Element}
12856      */
12857     getEl : function(){
12858         return this.wrapEl;
12859     },
12860     
12861     
12862
12863     /**
12864      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12865      */
12866     refresh : function(){
12867         //Roo.log('refresh');
12868         var t = this.tpl;
12869         
12870         // if we are using something like 'domtemplate', then
12871         // the what gets used is:
12872         // t.applySubtemplate(NAME, data, wrapping data..)
12873         // the outer template then get' applied with
12874         //     the store 'extra data'
12875         // and the body get's added to the
12876         //      roo-name="data" node?
12877         //      <span class='roo-tpl-{name}'></span> ?????
12878         
12879         
12880         
12881         this.clearSelections();
12882         this.el.update("");
12883         var html = [];
12884         var records = this.store.getRange();
12885         if(records.length < 1) {
12886             
12887             // is this valid??  = should it render a template??
12888             
12889             this.el.update(this.emptyText);
12890             return;
12891         }
12892         var el = this.el;
12893         if (this.dataName) {
12894             this.el.update(t.apply(this.store.meta)); //????
12895             el = this.el.child('.roo-tpl-' + this.dataName);
12896         }
12897         
12898         for(var i = 0, len = records.length; i < len; i++){
12899             var data = this.prepareData(records[i].data, i, records[i]);
12900             this.fireEvent("preparedata", this, data, i, records[i]);
12901             
12902             var d = Roo.apply({}, data);
12903             
12904             if(this.tickable){
12905                 Roo.apply(d, {'roo-id' : Roo.id()});
12906                 
12907                 var _this = this;
12908             
12909                 Roo.each(this.parent.item, function(item){
12910                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12911                         return;
12912                     }
12913                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12914                 });
12915             }
12916             
12917             html[html.length] = Roo.util.Format.trim(
12918                 this.dataName ?
12919                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12920                     t.apply(d)
12921             );
12922         }
12923         
12924         
12925         
12926         el.update(html.join(""));
12927         this.nodes = el.dom.childNodes;
12928         this.updateIndexes(0);
12929     },
12930     
12931
12932     /**
12933      * Function to override to reformat the data that is sent to
12934      * the template for each node.
12935      * DEPRICATED - use the preparedata event handler.
12936      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12937      * a JSON object for an UpdateManager bound view).
12938      */
12939     prepareData : function(data, index, record)
12940     {
12941         this.fireEvent("preparedata", this, data, index, record);
12942         return data;
12943     },
12944
12945     onUpdate : function(ds, record){
12946         // Roo.log('on update');   
12947         this.clearSelections();
12948         var index = this.store.indexOf(record);
12949         var n = this.nodes[index];
12950         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12951         n.parentNode.removeChild(n);
12952         this.updateIndexes(index, index);
12953     },
12954
12955     
12956     
12957 // --------- FIXME     
12958     onAdd : function(ds, records, index)
12959     {
12960         //Roo.log(['on Add', ds, records, index] );        
12961         this.clearSelections();
12962         if(this.nodes.length == 0){
12963             this.refresh();
12964             return;
12965         }
12966         var n = this.nodes[index];
12967         for(var i = 0, len = records.length; i < len; i++){
12968             var d = this.prepareData(records[i].data, i, records[i]);
12969             if(n){
12970                 this.tpl.insertBefore(n, d);
12971             }else{
12972                 
12973                 this.tpl.append(this.el, d);
12974             }
12975         }
12976         this.updateIndexes(index);
12977     },
12978
12979     onRemove : function(ds, record, index){
12980        // Roo.log('onRemove');
12981         this.clearSelections();
12982         var el = this.dataName  ?
12983             this.el.child('.roo-tpl-' + this.dataName) :
12984             this.el; 
12985         
12986         el.dom.removeChild(this.nodes[index]);
12987         this.updateIndexes(index);
12988     },
12989
12990     /**
12991      * Refresh an individual node.
12992      * @param {Number} index
12993      */
12994     refreshNode : function(index){
12995         this.onUpdate(this.store, this.store.getAt(index));
12996     },
12997
12998     updateIndexes : function(startIndex, endIndex){
12999         var ns = this.nodes;
13000         startIndex = startIndex || 0;
13001         endIndex = endIndex || ns.length - 1;
13002         for(var i = startIndex; i <= endIndex; i++){
13003             ns[i].nodeIndex = i;
13004         }
13005     },
13006
13007     /**
13008      * Changes the data store this view uses and refresh the view.
13009      * @param {Store} store
13010      */
13011     setStore : function(store, initial){
13012         if(!initial && this.store){
13013             this.store.un("datachanged", this.refresh);
13014             this.store.un("add", this.onAdd);
13015             this.store.un("remove", this.onRemove);
13016             this.store.un("update", this.onUpdate);
13017             this.store.un("clear", this.refresh);
13018             this.store.un("beforeload", this.onBeforeLoad);
13019             this.store.un("load", this.onLoad);
13020             this.store.un("loadexception", this.onLoad);
13021         }
13022         if(store){
13023           
13024             store.on("datachanged", this.refresh, this);
13025             store.on("add", this.onAdd, this);
13026             store.on("remove", this.onRemove, this);
13027             store.on("update", this.onUpdate, this);
13028             store.on("clear", this.refresh, this);
13029             store.on("beforeload", this.onBeforeLoad, this);
13030             store.on("load", this.onLoad, this);
13031             store.on("loadexception", this.onLoad, this);
13032         }
13033         
13034         if(store){
13035             this.refresh();
13036         }
13037     },
13038     /**
13039      * onbeforeLoad - masks the loading area.
13040      *
13041      */
13042     onBeforeLoad : function(store,opts)
13043     {
13044          //Roo.log('onBeforeLoad');   
13045         if (!opts.add) {
13046             this.el.update("");
13047         }
13048         this.el.mask(this.mask ? this.mask : "Loading" ); 
13049     },
13050     onLoad : function ()
13051     {
13052         this.el.unmask();
13053     },
13054     
13055
13056     /**
13057      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13058      * @param {HTMLElement} node
13059      * @return {HTMLElement} The template node
13060      */
13061     findItemFromChild : function(node){
13062         var el = this.dataName  ?
13063             this.el.child('.roo-tpl-' + this.dataName,true) :
13064             this.el.dom; 
13065         
13066         if(!node || node.parentNode == el){
13067                     return node;
13068             }
13069             var p = node.parentNode;
13070             while(p && p != el){
13071             if(p.parentNode == el){
13072                 return p;
13073             }
13074             p = p.parentNode;
13075         }
13076             return null;
13077     },
13078
13079     /** @ignore */
13080     onClick : function(e){
13081         var item = this.findItemFromChild(e.getTarget());
13082         if(item){
13083             var index = this.indexOf(item);
13084             if(this.onItemClick(item, index, e) !== false){
13085                 this.fireEvent("click", this, index, item, e);
13086             }
13087         }else{
13088             this.clearSelections();
13089         }
13090     },
13091
13092     /** @ignore */
13093     onContextMenu : function(e){
13094         var item = this.findItemFromChild(e.getTarget());
13095         if(item){
13096             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
13097         }
13098     },
13099
13100     /** @ignore */
13101     onDblClick : function(e){
13102         var item = this.findItemFromChild(e.getTarget());
13103         if(item){
13104             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
13105         }
13106     },
13107
13108     onItemClick : function(item, index, e)
13109     {
13110         if(this.fireEvent("beforeclick", this, index, item, e) === false){
13111             return false;
13112         }
13113         if (this.toggleSelect) {
13114             var m = this.isSelected(item) ? 'unselect' : 'select';
13115             //Roo.log(m);
13116             var _t = this;
13117             _t[m](item, true, false);
13118             return true;
13119         }
13120         if(this.multiSelect || this.singleSelect){
13121             if(this.multiSelect && e.shiftKey && this.lastSelection){
13122                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
13123             }else{
13124                 this.select(item, this.multiSelect && e.ctrlKey);
13125                 this.lastSelection = item;
13126             }
13127             
13128             if(!this.tickable){
13129                 e.preventDefault();
13130             }
13131             
13132         }
13133         return true;
13134     },
13135
13136     /**
13137      * Get the number of selected nodes.
13138      * @return {Number}
13139      */
13140     getSelectionCount : function(){
13141         return this.selections.length;
13142     },
13143
13144     /**
13145      * Get the currently selected nodes.
13146      * @return {Array} An array of HTMLElements
13147      */
13148     getSelectedNodes : function(){
13149         return this.selections;
13150     },
13151
13152     /**
13153      * Get the indexes of the selected nodes.
13154      * @return {Array}
13155      */
13156     getSelectedIndexes : function(){
13157         var indexes = [], s = this.selections;
13158         for(var i = 0, len = s.length; i < len; i++){
13159             indexes.push(s[i].nodeIndex);
13160         }
13161         return indexes;
13162     },
13163
13164     /**
13165      * Clear all selections
13166      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
13167      */
13168     clearSelections : function(suppressEvent){
13169         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
13170             this.cmp.elements = this.selections;
13171             this.cmp.removeClass(this.selectedClass);
13172             this.selections = [];
13173             if(!suppressEvent){
13174                 this.fireEvent("selectionchange", this, this.selections);
13175             }
13176         }
13177     },
13178
13179     /**
13180      * Returns true if the passed node is selected
13181      * @param {HTMLElement/Number} node The node or node index
13182      * @return {Boolean}
13183      */
13184     isSelected : function(node){
13185         var s = this.selections;
13186         if(s.length < 1){
13187             return false;
13188         }
13189         node = this.getNode(node);
13190         return s.indexOf(node) !== -1;
13191     },
13192
13193     /**
13194      * Selects nodes.
13195      * @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
13196      * @param {Boolean} keepExisting (optional) true to keep existing selections
13197      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13198      */
13199     select : function(nodeInfo, keepExisting, suppressEvent){
13200         if(nodeInfo instanceof Array){
13201             if(!keepExisting){
13202                 this.clearSelections(true);
13203             }
13204             for(var i = 0, len = nodeInfo.length; i < len; i++){
13205                 this.select(nodeInfo[i], true, true);
13206             }
13207             return;
13208         } 
13209         var node = this.getNode(nodeInfo);
13210         if(!node || this.isSelected(node)){
13211             return; // already selected.
13212         }
13213         if(!keepExisting){
13214             this.clearSelections(true);
13215         }
13216         
13217         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
13218             Roo.fly(node).addClass(this.selectedClass);
13219             this.selections.push(node);
13220             if(!suppressEvent){
13221                 this.fireEvent("selectionchange", this, this.selections);
13222             }
13223         }
13224         
13225         
13226     },
13227       /**
13228      * Unselects nodes.
13229      * @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
13230      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
13231      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13232      */
13233     unselect : function(nodeInfo, keepExisting, suppressEvent)
13234     {
13235         if(nodeInfo instanceof Array){
13236             Roo.each(this.selections, function(s) {
13237                 this.unselect(s, nodeInfo);
13238             }, this);
13239             return;
13240         }
13241         var node = this.getNode(nodeInfo);
13242         if(!node || !this.isSelected(node)){
13243             //Roo.log("not selected");
13244             return; // not selected.
13245         }
13246         // fireevent???
13247         var ns = [];
13248         Roo.each(this.selections, function(s) {
13249             if (s == node ) {
13250                 Roo.fly(node).removeClass(this.selectedClass);
13251
13252                 return;
13253             }
13254             ns.push(s);
13255         },this);
13256         
13257         this.selections= ns;
13258         this.fireEvent("selectionchange", this, this.selections);
13259     },
13260
13261     /**
13262      * Gets a template node.
13263      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13264      * @return {HTMLElement} The node or null if it wasn't found
13265      */
13266     getNode : function(nodeInfo){
13267         if(typeof nodeInfo == "string"){
13268             return document.getElementById(nodeInfo);
13269         }else if(typeof nodeInfo == "number"){
13270             return this.nodes[nodeInfo];
13271         }
13272         return nodeInfo;
13273     },
13274
13275     /**
13276      * Gets a range template nodes.
13277      * @param {Number} startIndex
13278      * @param {Number} endIndex
13279      * @return {Array} An array of nodes
13280      */
13281     getNodes : function(start, end){
13282         var ns = this.nodes;
13283         start = start || 0;
13284         end = typeof end == "undefined" ? ns.length - 1 : end;
13285         var nodes = [];
13286         if(start <= end){
13287             for(var i = start; i <= end; i++){
13288                 nodes.push(ns[i]);
13289             }
13290         } else{
13291             for(var i = start; i >= end; i--){
13292                 nodes.push(ns[i]);
13293             }
13294         }
13295         return nodes;
13296     },
13297
13298     /**
13299      * Finds the index of the passed node
13300      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13301      * @return {Number} The index of the node or -1
13302      */
13303     indexOf : function(node){
13304         node = this.getNode(node);
13305         if(typeof node.nodeIndex == "number"){
13306             return node.nodeIndex;
13307         }
13308         var ns = this.nodes;
13309         for(var i = 0, len = ns.length; i < len; i++){
13310             if(ns[i] == node){
13311                 return i;
13312             }
13313         }
13314         return -1;
13315     }
13316 });
13317 /*
13318  * - LGPL
13319  *
13320  * based on jquery fullcalendar
13321  * 
13322  */
13323
13324 Roo.bootstrap = Roo.bootstrap || {};
13325 /**
13326  * @class Roo.bootstrap.Calendar
13327  * @extends Roo.bootstrap.Component
13328  * Bootstrap Calendar class
13329  * @cfg {Boolean} loadMask (true|false) default false
13330  * @cfg {Object} header generate the user specific header of the calendar, default false
13331
13332  * @constructor
13333  * Create a new Container
13334  * @param {Object} config The config object
13335  */
13336
13337
13338
13339 Roo.bootstrap.Calendar = function(config){
13340     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
13341      this.addEvents({
13342         /**
13343              * @event select
13344              * Fires when a date is selected
13345              * @param {DatePicker} this
13346              * @param {Date} date The selected date
13347              */
13348         'select': true,
13349         /**
13350              * @event monthchange
13351              * Fires when the displayed month changes 
13352              * @param {DatePicker} this
13353              * @param {Date} date The selected month
13354              */
13355         'monthchange': true,
13356         /**
13357              * @event evententer
13358              * Fires when mouse over an event
13359              * @param {Calendar} this
13360              * @param {event} Event
13361              */
13362         'evententer': true,
13363         /**
13364              * @event eventleave
13365              * Fires when the mouse leaves an
13366              * @param {Calendar} this
13367              * @param {event}
13368              */
13369         'eventleave': true,
13370         /**
13371              * @event eventclick
13372              * Fires when the mouse click an
13373              * @param {Calendar} this
13374              * @param {event}
13375              */
13376         'eventclick': true
13377         
13378     });
13379
13380 };
13381
13382 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
13383     
13384      /**
13385      * @cfg {Number} startDay
13386      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
13387      */
13388     startDay : 0,
13389     
13390     loadMask : false,
13391     
13392     header : false,
13393       
13394     getAutoCreate : function(){
13395         
13396         
13397         var fc_button = function(name, corner, style, content ) {
13398             return Roo.apply({},{
13399                 tag : 'span',
13400                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
13401                          (corner.length ?
13402                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
13403                             ''
13404                         ),
13405                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
13406                 unselectable: 'on'
13407             });
13408         };
13409         
13410         var header = {};
13411         
13412         if(!this.header){
13413             header = {
13414                 tag : 'table',
13415                 cls : 'fc-header',
13416                 style : 'width:100%',
13417                 cn : [
13418                     {
13419                         tag: 'tr',
13420                         cn : [
13421                             {
13422                                 tag : 'td',
13423                                 cls : 'fc-header-left',
13424                                 cn : [
13425                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
13426                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
13427                                     { tag: 'span', cls: 'fc-header-space' },
13428                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
13429
13430
13431                                 ]
13432                             },
13433
13434                             {
13435                                 tag : 'td',
13436                                 cls : 'fc-header-center',
13437                                 cn : [
13438                                     {
13439                                         tag: 'span',
13440                                         cls: 'fc-header-title',
13441                                         cn : {
13442                                             tag: 'H2',
13443                                             html : 'month / year'
13444                                         }
13445                                     }
13446
13447                                 ]
13448                             },
13449                             {
13450                                 tag : 'td',
13451                                 cls : 'fc-header-right',
13452                                 cn : [
13453                               /*      fc_button('month', 'left', '', 'month' ),
13454                                     fc_button('week', '', '', 'week' ),
13455                                     fc_button('day', 'right', '', 'day' )
13456                                 */    
13457
13458                                 ]
13459                             }
13460
13461                         ]
13462                     }
13463                 ]
13464             };
13465         }
13466         
13467         header = this.header;
13468         
13469        
13470         var cal_heads = function() {
13471             var ret = [];
13472             // fixme - handle this.
13473             
13474             for (var i =0; i < Date.dayNames.length; i++) {
13475                 var d = Date.dayNames[i];
13476                 ret.push({
13477                     tag: 'th',
13478                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13479                     html : d.substring(0,3)
13480                 });
13481                 
13482             }
13483             ret[0].cls += ' fc-first';
13484             ret[6].cls += ' fc-last';
13485             return ret;
13486         };
13487         var cal_cell = function(n) {
13488             return  {
13489                 tag: 'td',
13490                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13491                 cn : [
13492                     {
13493                         cn : [
13494                             {
13495                                 cls: 'fc-day-number',
13496                                 html: 'D'
13497                             },
13498                             {
13499                                 cls: 'fc-day-content',
13500                              
13501                                 cn : [
13502                                      {
13503                                         style: 'position: relative;' // height: 17px;
13504                                     }
13505                                 ]
13506                             }
13507                             
13508                             
13509                         ]
13510                     }
13511                 ]
13512                 
13513             }
13514         };
13515         var cal_rows = function() {
13516             
13517             var ret = [];
13518             for (var r = 0; r < 6; r++) {
13519                 var row= {
13520                     tag : 'tr',
13521                     cls : 'fc-week',
13522                     cn : []
13523                 };
13524                 
13525                 for (var i =0; i < Date.dayNames.length; i++) {
13526                     var d = Date.dayNames[i];
13527                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13528
13529                 }
13530                 row.cn[0].cls+=' fc-first';
13531                 row.cn[0].cn[0].style = 'min-height:90px';
13532                 row.cn[6].cls+=' fc-last';
13533                 ret.push(row);
13534                 
13535             }
13536             ret[0].cls += ' fc-first';
13537             ret[4].cls += ' fc-prev-last';
13538             ret[5].cls += ' fc-last';
13539             return ret;
13540             
13541         };
13542         
13543         var cal_table = {
13544             tag: 'table',
13545             cls: 'fc-border-separate',
13546             style : 'width:100%',
13547             cellspacing  : 0,
13548             cn : [
13549                 { 
13550                     tag: 'thead',
13551                     cn : [
13552                         { 
13553                             tag: 'tr',
13554                             cls : 'fc-first fc-last',
13555                             cn : cal_heads()
13556                         }
13557                     ]
13558                 },
13559                 { 
13560                     tag: 'tbody',
13561                     cn : cal_rows()
13562                 }
13563                   
13564             ]
13565         };
13566          
13567          var cfg = {
13568             cls : 'fc fc-ltr',
13569             cn : [
13570                 header,
13571                 {
13572                     cls : 'fc-content',
13573                     style : "position: relative;",
13574                     cn : [
13575                         {
13576                             cls : 'fc-view fc-view-month fc-grid',
13577                             style : 'position: relative',
13578                             unselectable : 'on',
13579                             cn : [
13580                                 {
13581                                     cls : 'fc-event-container',
13582                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13583                                 },
13584                                 cal_table
13585                             ]
13586                         }
13587                     ]
13588     
13589                 }
13590            ] 
13591             
13592         };
13593         
13594          
13595         
13596         return cfg;
13597     },
13598     
13599     
13600     initEvents : function()
13601     {
13602         if(!this.store){
13603             throw "can not find store for calendar";
13604         }
13605         
13606         var mark = {
13607             tag: "div",
13608             cls:"x-dlg-mask",
13609             style: "text-align:center",
13610             cn: [
13611                 {
13612                     tag: "div",
13613                     style: "background-color:white;width:50%;margin:250 auto",
13614                     cn: [
13615                         {
13616                             tag: "img",
13617                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13618                         },
13619                         {
13620                             tag: "span",
13621                             html: "Loading"
13622                         }
13623                         
13624                     ]
13625                 }
13626             ]
13627         }
13628         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13629         
13630         var size = this.el.select('.fc-content', true).first().getSize();
13631         this.maskEl.setSize(size.width, size.height);
13632         this.maskEl.enableDisplayMode("block");
13633         if(!this.loadMask){
13634             this.maskEl.hide();
13635         }
13636         
13637         this.store = Roo.factory(this.store, Roo.data);
13638         this.store.on('load', this.onLoad, this);
13639         this.store.on('beforeload', this.onBeforeLoad, this);
13640         
13641         this.resize();
13642         
13643         this.cells = this.el.select('.fc-day',true);
13644         //Roo.log(this.cells);
13645         this.textNodes = this.el.query('.fc-day-number');
13646         this.cells.addClassOnOver('fc-state-hover');
13647         
13648         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13649         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13650         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13651         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13652         
13653         this.on('monthchange', this.onMonthChange, this);
13654         
13655         this.update(new Date().clearTime());
13656     },
13657     
13658     resize : function() {
13659         var sz  = this.el.getSize();
13660         
13661         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13662         this.el.select('.fc-day-content div',true).setHeight(34);
13663     },
13664     
13665     
13666     // private
13667     showPrevMonth : function(e){
13668         this.update(this.activeDate.add("mo", -1));
13669     },
13670     showToday : function(e){
13671         this.update(new Date().clearTime());
13672     },
13673     // private
13674     showNextMonth : function(e){
13675         this.update(this.activeDate.add("mo", 1));
13676     },
13677
13678     // private
13679     showPrevYear : function(){
13680         this.update(this.activeDate.add("y", -1));
13681     },
13682
13683     // private
13684     showNextYear : function(){
13685         this.update(this.activeDate.add("y", 1));
13686     },
13687
13688     
13689    // private
13690     update : function(date)
13691     {
13692         var vd = this.activeDate;
13693         this.activeDate = date;
13694 //        if(vd && this.el){
13695 //            var t = date.getTime();
13696 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13697 //                Roo.log('using add remove');
13698 //                
13699 //                this.fireEvent('monthchange', this, date);
13700 //                
13701 //                this.cells.removeClass("fc-state-highlight");
13702 //                this.cells.each(function(c){
13703 //                   if(c.dateValue == t){
13704 //                       c.addClass("fc-state-highlight");
13705 //                       setTimeout(function(){
13706 //                            try{c.dom.firstChild.focus();}catch(e){}
13707 //                       }, 50);
13708 //                       return false;
13709 //                   }
13710 //                   return true;
13711 //                });
13712 //                return;
13713 //            }
13714 //        }
13715         
13716         var days = date.getDaysInMonth();
13717         
13718         var firstOfMonth = date.getFirstDateOfMonth();
13719         var startingPos = firstOfMonth.getDay()-this.startDay;
13720         
13721         if(startingPos < this.startDay){
13722             startingPos += 7;
13723         }
13724         
13725         var pm = date.add(Date.MONTH, -1);
13726         var prevStart = pm.getDaysInMonth()-startingPos;
13727 //        
13728         this.cells = this.el.select('.fc-day',true);
13729         this.textNodes = this.el.query('.fc-day-number');
13730         this.cells.addClassOnOver('fc-state-hover');
13731         
13732         var cells = this.cells.elements;
13733         var textEls = this.textNodes;
13734         
13735         Roo.each(cells, function(cell){
13736             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13737         });
13738         
13739         days += startingPos;
13740
13741         // convert everything to numbers so it's fast
13742         var day = 86400000;
13743         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13744         //Roo.log(d);
13745         //Roo.log(pm);
13746         //Roo.log(prevStart);
13747         
13748         var today = new Date().clearTime().getTime();
13749         var sel = date.clearTime().getTime();
13750         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13751         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13752         var ddMatch = this.disabledDatesRE;
13753         var ddText = this.disabledDatesText;
13754         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13755         var ddaysText = this.disabledDaysText;
13756         var format = this.format;
13757         
13758         var setCellClass = function(cal, cell){
13759             cell.row = 0;
13760             cell.events = [];
13761             cell.more = [];
13762             //Roo.log('set Cell Class');
13763             cell.title = "";
13764             var t = d.getTime();
13765             
13766             //Roo.log(d);
13767             
13768             cell.dateValue = t;
13769             if(t == today){
13770                 cell.className += " fc-today";
13771                 cell.className += " fc-state-highlight";
13772                 cell.title = cal.todayText;
13773             }
13774             if(t == sel){
13775                 // disable highlight in other month..
13776                 //cell.className += " fc-state-highlight";
13777                 
13778             }
13779             // disabling
13780             if(t < min) {
13781                 cell.className = " fc-state-disabled";
13782                 cell.title = cal.minText;
13783                 return;
13784             }
13785             if(t > max) {
13786                 cell.className = " fc-state-disabled";
13787                 cell.title = cal.maxText;
13788                 return;
13789             }
13790             if(ddays){
13791                 if(ddays.indexOf(d.getDay()) != -1){
13792                     cell.title = ddaysText;
13793                     cell.className = " fc-state-disabled";
13794                 }
13795             }
13796             if(ddMatch && format){
13797                 var fvalue = d.dateFormat(format);
13798                 if(ddMatch.test(fvalue)){
13799                     cell.title = ddText.replace("%0", fvalue);
13800                     cell.className = " fc-state-disabled";
13801                 }
13802             }
13803             
13804             if (!cell.initialClassName) {
13805                 cell.initialClassName = cell.dom.className;
13806             }
13807             
13808             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13809         };
13810
13811         var i = 0;
13812         
13813         for(; i < startingPos; i++) {
13814             textEls[i].innerHTML = (++prevStart);
13815             d.setDate(d.getDate()+1);
13816             
13817             cells[i].className = "fc-past fc-other-month";
13818             setCellClass(this, cells[i]);
13819         }
13820         
13821         var intDay = 0;
13822         
13823         for(; i < days; i++){
13824             intDay = i - startingPos + 1;
13825             textEls[i].innerHTML = (intDay);
13826             d.setDate(d.getDate()+1);
13827             
13828             cells[i].className = ''; // "x-date-active";
13829             setCellClass(this, cells[i]);
13830         }
13831         var extraDays = 0;
13832         
13833         for(; i < 42; i++) {
13834             textEls[i].innerHTML = (++extraDays);
13835             d.setDate(d.getDate()+1);
13836             
13837             cells[i].className = "fc-future fc-other-month";
13838             setCellClass(this, cells[i]);
13839         }
13840         
13841         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13842         
13843         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13844         
13845         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13846         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13847         
13848         if(totalRows != 6){
13849             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13850             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13851         }
13852         
13853         this.fireEvent('monthchange', this, date);
13854         
13855         
13856         /*
13857         if(!this.internalRender){
13858             var main = this.el.dom.firstChild;
13859             var w = main.offsetWidth;
13860             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13861             Roo.fly(main).setWidth(w);
13862             this.internalRender = true;
13863             // opera does not respect the auto grow header center column
13864             // then, after it gets a width opera refuses to recalculate
13865             // without a second pass
13866             if(Roo.isOpera && !this.secondPass){
13867                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13868                 this.secondPass = true;
13869                 this.update.defer(10, this, [date]);
13870             }
13871         }
13872         */
13873         
13874     },
13875     
13876     findCell : function(dt) {
13877         dt = dt.clearTime().getTime();
13878         var ret = false;
13879         this.cells.each(function(c){
13880             //Roo.log("check " +c.dateValue + '?=' + dt);
13881             if(c.dateValue == dt){
13882                 ret = c;
13883                 return false;
13884             }
13885             return true;
13886         });
13887         
13888         return ret;
13889     },
13890     
13891     findCells : function(ev) {
13892         var s = ev.start.clone().clearTime().getTime();
13893        // Roo.log(s);
13894         var e= ev.end.clone().clearTime().getTime();
13895        // Roo.log(e);
13896         var ret = [];
13897         this.cells.each(function(c){
13898              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13899             
13900             if(c.dateValue > e){
13901                 return ;
13902             }
13903             if(c.dateValue < s){
13904                 return ;
13905             }
13906             ret.push(c);
13907         });
13908         
13909         return ret;    
13910     },
13911     
13912 //    findBestRow: function(cells)
13913 //    {
13914 //        var ret = 0;
13915 //        
13916 //        for (var i =0 ; i < cells.length;i++) {
13917 //            ret  = Math.max(cells[i].rows || 0,ret);
13918 //        }
13919 //        return ret;
13920 //        
13921 //    },
13922     
13923     
13924     addItem : function(ev)
13925     {
13926         // look for vertical location slot in
13927         var cells = this.findCells(ev);
13928         
13929 //        ev.row = this.findBestRow(cells);
13930         
13931         // work out the location.
13932         
13933         var crow = false;
13934         var rows = [];
13935         for(var i =0; i < cells.length; i++) {
13936             
13937             cells[i].row = cells[0].row;
13938             
13939             if(i == 0){
13940                 cells[i].row = cells[i].row + 1;
13941             }
13942             
13943             if (!crow) {
13944                 crow = {
13945                     start : cells[i],
13946                     end :  cells[i]
13947                 };
13948                 continue;
13949             }
13950             if (crow.start.getY() == cells[i].getY()) {
13951                 // on same row.
13952                 crow.end = cells[i];
13953                 continue;
13954             }
13955             // different row.
13956             rows.push(crow);
13957             crow = {
13958                 start: cells[i],
13959                 end : cells[i]
13960             };
13961             
13962         }
13963         
13964         rows.push(crow);
13965         ev.els = [];
13966         ev.rows = rows;
13967         ev.cells = cells;
13968         
13969         cells[0].events.push(ev);
13970         
13971         this.calevents.push(ev);
13972     },
13973     
13974     clearEvents: function() {
13975         
13976         if(!this.calevents){
13977             return;
13978         }
13979         
13980         Roo.each(this.cells.elements, function(c){
13981             c.row = 0;
13982             c.events = [];
13983             c.more = [];
13984         });
13985         
13986         Roo.each(this.calevents, function(e) {
13987             Roo.each(e.els, function(el) {
13988                 el.un('mouseenter' ,this.onEventEnter, this);
13989                 el.un('mouseleave' ,this.onEventLeave, this);
13990                 el.remove();
13991             },this);
13992         },this);
13993         
13994         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13995             e.remove();
13996         });
13997         
13998     },
13999     
14000     renderEvents: function()
14001     {   
14002         var _this = this;
14003         
14004         this.cells.each(function(c) {
14005             
14006             if(c.row < 5){
14007                 return;
14008             }
14009             
14010             var ev = c.events;
14011             
14012             var r = 4;
14013             if(c.row != c.events.length){
14014                 r = 4 - (4 - (c.row - c.events.length));
14015             }
14016             
14017             c.events = ev.slice(0, r);
14018             c.more = ev.slice(r);
14019             
14020             if(c.more.length && c.more.length == 1){
14021                 c.events.push(c.more.pop());
14022             }
14023             
14024             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14025             
14026         });
14027             
14028         this.cells.each(function(c) {
14029             
14030             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14031             
14032             
14033             for (var e = 0; e < c.events.length; e++){
14034                 var ev = c.events[e];
14035                 var rows = ev.rows;
14036                 
14037                 for(var i = 0; i < rows.length; i++) {
14038                 
14039                     // how many rows should it span..
14040
14041                     var  cfg = {
14042                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14043                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14044
14045                         unselectable : "on",
14046                         cn : [
14047                             {
14048                                 cls: 'fc-event-inner',
14049                                 cn : [
14050     //                                {
14051     //                                  tag:'span',
14052     //                                  cls: 'fc-event-time',
14053     //                                  html : cells.length > 1 ? '' : ev.time
14054     //                                },
14055                                     {
14056                                       tag:'span',
14057                                       cls: 'fc-event-title',
14058                                       html : String.format('{0}', ev.title)
14059                                     }
14060
14061
14062                                 ]
14063                             },
14064                             {
14065                                 cls: 'ui-resizable-handle ui-resizable-e',
14066                                 html : '&nbsp;&nbsp;&nbsp'
14067                             }
14068
14069                         ]
14070                     };
14071
14072                     if (i == 0) {
14073                         cfg.cls += ' fc-event-start';
14074                     }
14075                     if ((i+1) == rows.length) {
14076                         cfg.cls += ' fc-event-end';
14077                     }
14078
14079                     var ctr = _this.el.select('.fc-event-container',true).first();
14080                     var cg = ctr.createChild(cfg);
14081
14082                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
14083                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
14084
14085                     var r = (c.more.length) ? 1 : 0;
14086                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
14087                     cg.setWidth(ebox.right - sbox.x -2);
14088
14089                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
14090                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
14091                     cg.on('click', _this.onEventClick, _this, ev);
14092
14093                     ev.els.push(cg);
14094                     
14095                 }
14096                 
14097             }
14098             
14099             
14100             if(c.more.length){
14101                 var  cfg = {
14102                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
14103                     style : 'position: absolute',
14104                     unselectable : "on",
14105                     cn : [
14106                         {
14107                             cls: 'fc-event-inner',
14108                             cn : [
14109                                 {
14110                                   tag:'span',
14111                                   cls: 'fc-event-title',
14112                                   html : 'More'
14113                                 }
14114
14115
14116                             ]
14117                         },
14118                         {
14119                             cls: 'ui-resizable-handle ui-resizable-e',
14120                             html : '&nbsp;&nbsp;&nbsp'
14121                         }
14122
14123                     ]
14124                 };
14125
14126                 var ctr = _this.el.select('.fc-event-container',true).first();
14127                 var cg = ctr.createChild(cfg);
14128
14129                 var sbox = c.select('.fc-day-content',true).first().getBox();
14130                 var ebox = c.select('.fc-day-content',true).first().getBox();
14131                 //Roo.log(cg);
14132                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
14133                 cg.setWidth(ebox.right - sbox.x -2);
14134
14135                 cg.on('click', _this.onMoreEventClick, _this, c.more);
14136                 
14137             }
14138             
14139         });
14140         
14141         
14142         
14143     },
14144     
14145     onEventEnter: function (e, el,event,d) {
14146         this.fireEvent('evententer', this, el, event);
14147     },
14148     
14149     onEventLeave: function (e, el,event,d) {
14150         this.fireEvent('eventleave', this, el, event);
14151     },
14152     
14153     onEventClick: function (e, el,event,d) {
14154         this.fireEvent('eventclick', this, el, event);
14155     },
14156     
14157     onMonthChange: function () {
14158         this.store.load();
14159     },
14160     
14161     onMoreEventClick: function(e, el, more)
14162     {
14163         var _this = this;
14164         
14165         this.calpopover.placement = 'right';
14166         this.calpopover.setTitle('More');
14167         
14168         this.calpopover.setContent('');
14169         
14170         var ctr = this.calpopover.el.select('.popover-content', true).first();
14171         
14172         Roo.each(more, function(m){
14173             var cfg = {
14174                 cls : 'fc-event-hori fc-event-draggable',
14175                 html : m.title
14176             }
14177             var cg = ctr.createChild(cfg);
14178             
14179             cg.on('click', _this.onEventClick, _this, m);
14180         });
14181         
14182         this.calpopover.show(el);
14183         
14184         
14185     },
14186     
14187     onLoad: function () 
14188     {   
14189         this.calevents = [];
14190         var cal = this;
14191         
14192         if(this.store.getCount() > 0){
14193             this.store.data.each(function(d){
14194                cal.addItem({
14195                     id : d.data.id,
14196                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
14197                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
14198                     time : d.data.start_time,
14199                     title : d.data.title,
14200                     description : d.data.description,
14201                     venue : d.data.venue
14202                 });
14203             });
14204         }
14205         
14206         this.renderEvents();
14207         
14208         if(this.calevents.length && this.loadMask){
14209             this.maskEl.hide();
14210         }
14211     },
14212     
14213     onBeforeLoad: function()
14214     {
14215         this.clearEvents();
14216         if(this.loadMask){
14217             this.maskEl.show();
14218         }
14219     }
14220 });
14221
14222  
14223  /*
14224  * - LGPL
14225  *
14226  * element
14227  * 
14228  */
14229
14230 /**
14231  * @class Roo.bootstrap.Popover
14232  * @extends Roo.bootstrap.Component
14233  * Bootstrap Popover class
14234  * @cfg {String} html contents of the popover   (or false to use children..)
14235  * @cfg {String} title of popover (or false to hide)
14236  * @cfg {String} placement how it is placed
14237  * @cfg {String} trigger click || hover (or false to trigger manually)
14238  * @cfg {String} over what (parent or false to trigger manually.)
14239  * @cfg {Number} delay - delay before showing
14240  
14241  * @constructor
14242  * Create a new Popover
14243  * @param {Object} config The config object
14244  */
14245
14246 Roo.bootstrap.Popover = function(config){
14247     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
14248 };
14249
14250 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
14251     
14252     title: 'Fill in a title',
14253     html: false,
14254     
14255     placement : 'right',
14256     trigger : 'hover', // hover
14257     
14258     delay : 0,
14259     
14260     over: 'parent',
14261     
14262     can_build_overlaid : false,
14263     
14264     getChildContainer : function()
14265     {
14266         return this.el.select('.popover-content',true).first();
14267     },
14268     
14269     getAutoCreate : function(){
14270          Roo.log('make popover?');
14271         var cfg = {
14272            cls : 'popover roo-dynamic',
14273            style: 'display:block',
14274            cn : [
14275                 {
14276                     cls : 'arrow'
14277                 },
14278                 {
14279                     cls : 'popover-inner',
14280                     cn : [
14281                         {
14282                             tag: 'h3',
14283                             cls: 'popover-title',
14284                             html : this.title
14285                         },
14286                         {
14287                             cls : 'popover-content',
14288                             html : this.html
14289                         }
14290                     ]
14291                     
14292                 }
14293            ]
14294         };
14295         
14296         return cfg;
14297     },
14298     setTitle: function(str)
14299     {
14300         this.el.select('.popover-title',true).first().dom.innerHTML = str;
14301     },
14302     setContent: function(str)
14303     {
14304         this.el.select('.popover-content',true).first().dom.innerHTML = str;
14305     },
14306     // as it get's added to the bottom of the page.
14307     onRender : function(ct, position)
14308     {
14309         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
14310         if(!this.el){
14311             var cfg = Roo.apply({},  this.getAutoCreate());
14312             cfg.id = Roo.id();
14313             
14314             if (this.cls) {
14315                 cfg.cls += ' ' + this.cls;
14316             }
14317             if (this.style) {
14318                 cfg.style = this.style;
14319             }
14320             Roo.log("adding to ")
14321             this.el = Roo.get(document.body).createChild(cfg, position);
14322             Roo.log(this.el);
14323         }
14324         this.initEvents();
14325     },
14326     
14327     initEvents : function()
14328     {
14329         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
14330         this.el.enableDisplayMode('block');
14331         this.el.hide();
14332         if (this.over === false) {
14333             return; 
14334         }
14335         if (this.triggers === false) {
14336             return;
14337         }
14338         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14339         var triggers = this.trigger ? this.trigger.split(' ') : [];
14340         Roo.each(triggers, function(trigger) {
14341         
14342             if (trigger == 'click') {
14343                 on_el.on('click', this.toggle, this);
14344             } else if (trigger != 'manual') {
14345                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
14346                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
14347       
14348                 on_el.on(eventIn  ,this.enter, this);
14349                 on_el.on(eventOut, this.leave, this);
14350             }
14351         }, this);
14352         
14353     },
14354     
14355     
14356     // private
14357     timeout : null,
14358     hoverState : null,
14359     
14360     toggle : function () {
14361         this.hoverState == 'in' ? this.leave() : this.enter();
14362     },
14363     
14364     enter : function () {
14365        
14366     
14367         clearTimeout(this.timeout);
14368     
14369         this.hoverState = 'in';
14370     
14371         if (!this.delay || !this.delay.show) {
14372             this.show();
14373             return;
14374         }
14375         var _t = this;
14376         this.timeout = setTimeout(function () {
14377             if (_t.hoverState == 'in') {
14378                 _t.show();
14379             }
14380         }, this.delay.show)
14381     },
14382     leave : function() {
14383         clearTimeout(this.timeout);
14384     
14385         this.hoverState = 'out';
14386     
14387         if (!this.delay || !this.delay.hide) {
14388             this.hide();
14389             return;
14390         }
14391         var _t = this;
14392         this.timeout = setTimeout(function () {
14393             if (_t.hoverState == 'out') {
14394                 _t.hide();
14395             }
14396         }, this.delay.hide)
14397     },
14398     
14399     show : function (on_el)
14400     {
14401         if (!on_el) {
14402             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14403         }
14404         // set content.
14405         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
14406         if (this.html !== false) {
14407             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
14408         }
14409         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
14410         if (!this.title.length) {
14411             this.el.select('.popover-title',true).hide();
14412         }
14413         
14414         var placement = typeof this.placement == 'function' ?
14415             this.placement.call(this, this.el, on_el) :
14416             this.placement;
14417             
14418         var autoToken = /\s?auto?\s?/i;
14419         var autoPlace = autoToken.test(placement);
14420         if (autoPlace) {
14421             placement = placement.replace(autoToken, '') || 'top';
14422         }
14423         
14424         //this.el.detach()
14425         //this.el.setXY([0,0]);
14426         this.el.show();
14427         this.el.dom.style.display='block';
14428         this.el.addClass(placement);
14429         
14430         //this.el.appendTo(on_el);
14431         
14432         var p = this.getPosition();
14433         var box = this.el.getBox();
14434         
14435         if (autoPlace) {
14436             // fixme..
14437         }
14438         var align = Roo.bootstrap.Popover.alignment[placement];
14439         this.el.alignTo(on_el, align[0],align[1]);
14440         //var arrow = this.el.select('.arrow',true).first();
14441         //arrow.set(align[2], 
14442         
14443         this.el.addClass('in');
14444         this.hoverState = null;
14445         
14446         if (this.el.hasClass('fade')) {
14447             // fade it?
14448         }
14449         
14450     },
14451     hide : function()
14452     {
14453         this.el.setXY([0,0]);
14454         this.el.removeClass('in');
14455         this.el.hide();
14456         
14457     }
14458     
14459 });
14460
14461 Roo.bootstrap.Popover.alignment = {
14462     'left' : ['r-l', [-10,0], 'right'],
14463     'right' : ['l-r', [10,0], 'left'],
14464     'bottom' : ['t-b', [0,10], 'top'],
14465     'top' : [ 'b-t', [0,-10], 'bottom']
14466 };
14467
14468  /*
14469  * - LGPL
14470  *
14471  * Progress
14472  * 
14473  */
14474
14475 /**
14476  * @class Roo.bootstrap.Progress
14477  * @extends Roo.bootstrap.Component
14478  * Bootstrap Progress class
14479  * @cfg {Boolean} striped striped of the progress bar
14480  * @cfg {Boolean} active animated of the progress bar
14481  * 
14482  * 
14483  * @constructor
14484  * Create a new Progress
14485  * @param {Object} config The config object
14486  */
14487
14488 Roo.bootstrap.Progress = function(config){
14489     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14490 };
14491
14492 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14493     
14494     striped : false,
14495     active: false,
14496     
14497     getAutoCreate : function(){
14498         var cfg = {
14499             tag: 'div',
14500             cls: 'progress'
14501         };
14502         
14503         
14504         if(this.striped){
14505             cfg.cls += ' progress-striped';
14506         }
14507       
14508         if(this.active){
14509             cfg.cls += ' active';
14510         }
14511         
14512         
14513         return cfg;
14514     }
14515    
14516 });
14517
14518  
14519
14520  /*
14521  * - LGPL
14522  *
14523  * ProgressBar
14524  * 
14525  */
14526
14527 /**
14528  * @class Roo.bootstrap.ProgressBar
14529  * @extends Roo.bootstrap.Component
14530  * Bootstrap ProgressBar class
14531  * @cfg {Number} aria_valuenow aria-value now
14532  * @cfg {Number} aria_valuemin aria-value min
14533  * @cfg {Number} aria_valuemax aria-value max
14534  * @cfg {String} label label for the progress bar
14535  * @cfg {String} panel (success | info | warning | danger )
14536  * @cfg {String} role role of the progress bar
14537  * @cfg {String} sr_only text
14538  * 
14539  * 
14540  * @constructor
14541  * Create a new ProgressBar
14542  * @param {Object} config The config object
14543  */
14544
14545 Roo.bootstrap.ProgressBar = function(config){
14546     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14547 };
14548
14549 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14550     
14551     aria_valuenow : 0,
14552     aria_valuemin : 0,
14553     aria_valuemax : 100,
14554     label : false,
14555     panel : false,
14556     role : false,
14557     sr_only: false,
14558     
14559     getAutoCreate : function()
14560     {
14561         
14562         var cfg = {
14563             tag: 'div',
14564             cls: 'progress-bar',
14565             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14566         };
14567         
14568         if(this.sr_only){
14569             cfg.cn = {
14570                 tag: 'span',
14571                 cls: 'sr-only',
14572                 html: this.sr_only
14573             }
14574         }
14575         
14576         if(this.role){
14577             cfg.role = this.role;
14578         }
14579         
14580         if(this.aria_valuenow){
14581             cfg['aria-valuenow'] = this.aria_valuenow;
14582         }
14583         
14584         if(this.aria_valuemin){
14585             cfg['aria-valuemin'] = this.aria_valuemin;
14586         }
14587         
14588         if(this.aria_valuemax){
14589             cfg['aria-valuemax'] = this.aria_valuemax;
14590         }
14591         
14592         if(this.label && !this.sr_only){
14593             cfg.html = this.label;
14594         }
14595         
14596         if(this.panel){
14597             cfg.cls += ' progress-bar-' + this.panel;
14598         }
14599         
14600         return cfg;
14601     },
14602     
14603     update : function(aria_valuenow)
14604     {
14605         this.aria_valuenow = aria_valuenow;
14606         
14607         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14608     }
14609    
14610 });
14611
14612  
14613
14614  /*
14615  * - LGPL
14616  *
14617  * column
14618  * 
14619  */
14620
14621 /**
14622  * @class Roo.bootstrap.TabGroup
14623  * @extends Roo.bootstrap.Column
14624  * Bootstrap Column class
14625  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14626  * @cfg {Boolean} carousel true to make the group behave like a carousel
14627  * @cfg {Number} bullets show the panel pointer.. default 0
14628  * @cfg {Boolena} autoslide (true|false) auto slide .. default false
14629  * @cfg {Number} timer auto slide timer .. default 0 millisecond
14630  * 
14631  * @constructor
14632  * Create a new TabGroup
14633  * @param {Object} config The config object
14634  */
14635
14636 Roo.bootstrap.TabGroup = function(config){
14637     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14638     if (!this.navId) {
14639         this.navId = Roo.id();
14640     }
14641     this.tabs = [];
14642     Roo.bootstrap.TabGroup.register(this);
14643     
14644 };
14645
14646 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14647     
14648     carousel : false,
14649     transition : false,
14650     bullets : 0,
14651     timer : 0,
14652     autoslide : false,
14653     slideFn : false,
14654     
14655     getAutoCreate : function()
14656     {
14657         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14658         
14659         cfg.cls += ' tab-content';
14660         
14661         Roo.log('get auto create...............');
14662         
14663         if (this.carousel && !Roo.isTouch) {
14664             cfg.cls += ' carousel slide';
14665             
14666             cfg.cn = [{
14667                cls : 'carousel-inner'
14668             }];
14669         
14670             if(this.bullets > 0){
14671                 
14672                 var bullets = {
14673                     cls : 'carousel-bullets',
14674                     cn : []
14675                 };
14676                 
14677                 if(this.bullets_cls){
14678                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
14679                 }
14680                 
14681                 for (var i = 0; i < this.bullets; i++){
14682                     bullets.cn.push({
14683                         cls : 'bullet bullet-' + i
14684                     });
14685                 }
14686                 
14687                 bullets.cn.push({
14688                     cls : 'clear'
14689                 });
14690                 
14691                 cfg.cn[0].cn = bullets;
14692             }
14693         }
14694         
14695         return cfg;
14696     },
14697     
14698     initEvents:  function()
14699     {
14700         Roo.log('-------- init events on tab group ---------');
14701         
14702         if(this.bullets > 0 && !Roo.isTouch){
14703             this.initBullet();
14704         }
14705         
14706         if(this.autoslide){
14707             
14708             var _this = this;
14709             
14710             this.slideFn = window.setInterval(function() {
14711                 _this.showPanelNext();
14712             }, this.timer);
14713         }
14714         
14715     },
14716     
14717     getChildContainer : function()
14718     {
14719         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14720     },
14721     
14722     /**
14723     * register a Navigation item
14724     * @param {Roo.bootstrap.NavItem} the navitem to add
14725     */
14726     register : function(item)
14727     {
14728         this.tabs.push( item);
14729         item.navId = this.navId; // not really needed..
14730     
14731     },
14732     
14733     getActivePanel : function()
14734     {
14735         var r = false;
14736         Roo.each(this.tabs, function(t) {
14737             if (t.active) {
14738                 r = t;
14739                 return false;
14740             }
14741             return null;
14742         });
14743         return r;
14744         
14745     },
14746     getPanelByName : function(n)
14747     {
14748         var r = false;
14749         Roo.each(this.tabs, function(t) {
14750             if (t.tabId == n) {
14751                 r = t;
14752                 return false;
14753             }
14754             return null;
14755         });
14756         return r;
14757     },
14758     indexOfPanel : function(p)
14759     {
14760         var r = false;
14761         Roo.each(this.tabs, function(t,i) {
14762             if (t.tabId == p.tabId) {
14763                 r = i;
14764                 return false;
14765             }
14766             return null;
14767         });
14768         return r;
14769     },
14770     /**
14771      * show a specific panel
14772      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14773      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14774      */
14775     showPanel : function (pan)
14776     {
14777         if(this.transition){
14778             Roo.log("waiting for the transitionend");
14779             return;
14780         }
14781         
14782         if (typeof(pan) == 'number') {
14783             pan = this.tabs[pan];
14784         }
14785         if (typeof(pan) == 'string') {
14786             pan = this.getPanelByName(pan);
14787         }
14788         if (pan.tabId == this.getActivePanel().tabId) {
14789             return true;
14790         }
14791         var cur = this.getActivePanel();
14792         
14793         if (false === cur.fireEvent('beforedeactivate')) {
14794             return false;
14795         }
14796         
14797         if(this.bullets > 0 && !Roo.isTouch){
14798             this.setActiveBullet(this.indexOfPanel(pan));
14799         }
14800         
14801         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
14802             
14803             this.transition = true;
14804             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14805             var lr = dir == 'next' ? 'left' : 'right';
14806             pan.el.addClass(dir); // or prev
14807             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14808             cur.el.addClass(lr); // or right
14809             pan.el.addClass(lr);
14810             
14811             var _this = this;
14812             cur.el.on('transitionend', function() {
14813                 Roo.log("trans end?");
14814                 
14815                 pan.el.removeClass([lr,dir]);
14816                 pan.setActive(true);
14817                 
14818                 cur.el.removeClass([lr]);
14819                 cur.setActive(false);
14820                 
14821                 _this.transition = false;
14822                 
14823             }, this, { single:  true } );
14824             
14825             return true;
14826         }
14827         
14828         cur.setActive(false);
14829         pan.setActive(true);
14830         
14831         return true;
14832         
14833     },
14834     showPanelNext : function()
14835     {
14836         var i = this.indexOfPanel(this.getActivePanel());
14837         
14838         if (i >= this.tabs.length - 1 && !this.autoslide) {
14839             return;
14840         }
14841         
14842         if (i >= this.tabs.length - 1 && this.autoslide) {
14843             i = -1;
14844         }
14845         
14846         this.showPanel(this.tabs[i+1]);
14847     },
14848     
14849     showPanelPrev : function()
14850     {
14851         var i = this.indexOfPanel(this.getActivePanel());
14852         
14853         if (i  < 1 && !this.autoslide) {
14854             return;
14855         }
14856         
14857         if (i < 1 && this.autoslide) {
14858             i = this.tabs.length;
14859         }
14860         
14861         this.showPanel(this.tabs[i-1]);
14862     },
14863     
14864     initBullet : function()
14865     {
14866         if(Roo.isTouch){
14867             return;
14868         }
14869         
14870         var _this = this;
14871         
14872         for (var i = 0; i < this.bullets; i++){
14873             var bullet = this.el.select('.bullet-' + i, true).first();
14874
14875             if(!bullet){
14876                 continue;
14877             }
14878
14879             bullet.on('click', (function(e, el, o, ii, t){
14880
14881                 e.preventDefault();
14882
14883                 _this.showPanel(ii);
14884
14885                 if(_this.autoslide && _this.slideFn){
14886                     clearInterval(_this.slideFn);
14887                     _this.slideFn = window.setInterval(function() {
14888                         _this.showPanelNext();
14889                     }, _this.timer);
14890                 }
14891
14892             }).createDelegate(this, [i, bullet], true));
14893         }
14894     },
14895     
14896     setActiveBullet : function(i)
14897     {
14898         if(Roo.isTouch){
14899             return;
14900         }
14901         
14902         Roo.each(this.el.select('.bullet', true).elements, function(el){
14903             el.removeClass('selected');
14904         });
14905
14906         var bullet = this.el.select('.bullet-' + i, true).first();
14907         
14908         if(!bullet){
14909             return;
14910         }
14911         
14912         bullet.addClass('selected');
14913     }
14914     
14915     
14916   
14917 });
14918
14919  
14920
14921  
14922  
14923 Roo.apply(Roo.bootstrap.TabGroup, {
14924     
14925     groups: {},
14926      /**
14927     * register a Navigation Group
14928     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14929     */
14930     register : function(navgrp)
14931     {
14932         this.groups[navgrp.navId] = navgrp;
14933         
14934     },
14935     /**
14936     * fetch a Navigation Group based on the navigation ID
14937     * if one does not exist , it will get created.
14938     * @param {string} the navgroup to add
14939     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14940     */
14941     get: function(navId) {
14942         if (typeof(this.groups[navId]) == 'undefined') {
14943             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14944         }
14945         return this.groups[navId] ;
14946     }
14947     
14948     
14949     
14950 });
14951
14952  /*
14953  * - LGPL
14954  *
14955  * TabPanel
14956  * 
14957  */
14958
14959 /**
14960  * @class Roo.bootstrap.TabPanel
14961  * @extends Roo.bootstrap.Component
14962  * Bootstrap TabPanel class
14963  * @cfg {Boolean} active panel active
14964  * @cfg {String} html panel content
14965  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14966  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14967  * 
14968  * 
14969  * @constructor
14970  * Create a new TabPanel
14971  * @param {Object} config The config object
14972  */
14973
14974 Roo.bootstrap.TabPanel = function(config){
14975     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14976     this.addEvents({
14977         /**
14978              * @event changed
14979              * Fires when the active status changes
14980              * @param {Roo.bootstrap.TabPanel} this
14981              * @param {Boolean} state the new state
14982             
14983          */
14984         'changed': true,
14985         /**
14986              * @event beforedeactivate
14987              * Fires before a tab is de-activated - can be used to do validation on a form.
14988              * @param {Roo.bootstrap.TabPanel} this
14989              * @return {Boolean} false if there is an error
14990             
14991          */
14992         'beforedeactivate': true
14993      });
14994     
14995     this.tabId = this.tabId || Roo.id();
14996   
14997 };
14998
14999 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
15000     
15001     active: false,
15002     html: false,
15003     tabId: false,
15004     navId : false,
15005     
15006     getAutoCreate : function(){
15007         var cfg = {
15008             tag: 'div',
15009             // item is needed for carousel - not sure if it has any effect otherwise
15010             cls: 'tab-pane item',
15011             html: this.html || ''
15012         };
15013         
15014         if(this.active){
15015             cfg.cls += ' active';
15016         }
15017         
15018         if(this.tabId){
15019             cfg.tabId = this.tabId;
15020         }
15021         
15022         
15023         return cfg;
15024     },
15025     
15026     initEvents:  function()
15027     {
15028         Roo.log('-------- init events on tab panel ---------');
15029         
15030         var p = this.parent();
15031         this.navId = this.navId || p.navId;
15032         
15033         if (typeof(this.navId) != 'undefined') {
15034             // not really needed.. but just in case.. parent should be a NavGroup.
15035             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15036             Roo.log(['register', tg, this]);
15037             tg.register(this);
15038             
15039             var i = tg.tabs.length - 1;
15040             
15041             if(this.active && tg.bullets > 0 && i < tg.bullets){
15042                 tg.setActiveBullet(i);
15043             }
15044         }
15045         
15046     },
15047     
15048     
15049     onRender : function(ct, position)
15050     {
15051        // Roo.log("Call onRender: " + this.xtype);
15052         
15053         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
15054         
15055         
15056         
15057         
15058         
15059     },
15060     
15061     setActive: function(state)
15062     {
15063         Roo.log("panel - set active " + this.tabId + "=" + state);
15064         
15065         this.active = state;
15066         if (!state) {
15067             this.el.removeClass('active');
15068             
15069         } else  if (!this.el.hasClass('active')) {
15070             this.el.addClass('active');
15071         }
15072         
15073         this.fireEvent('changed', this, state);
15074     }
15075     
15076     
15077 });
15078  
15079
15080  
15081
15082  /*
15083  * - LGPL
15084  *
15085  * DateField
15086  * 
15087  */
15088
15089 /**
15090  * @class Roo.bootstrap.DateField
15091  * @extends Roo.bootstrap.Input
15092  * Bootstrap DateField class
15093  * @cfg {Number} weekStart default 0
15094  * @cfg {String} viewMode default empty, (months|years)
15095  * @cfg {String} minViewMode default empty, (months|years)
15096  * @cfg {Number} startDate default -Infinity
15097  * @cfg {Number} endDate default Infinity
15098  * @cfg {Boolean} todayHighlight default false
15099  * @cfg {Boolean} todayBtn default false
15100  * @cfg {Boolean} calendarWeeks default false
15101  * @cfg {Object} daysOfWeekDisabled default empty
15102  * @cfg {Boolean} singleMode default false (true | false)
15103  * 
15104  * @cfg {Boolean} keyboardNavigation default true
15105  * @cfg {String} language default en
15106  * 
15107  * @constructor
15108  * Create a new DateField
15109  * @param {Object} config The config object
15110  */
15111
15112 Roo.bootstrap.DateField = function(config){
15113     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
15114      this.addEvents({
15115             /**
15116              * @event show
15117              * Fires when this field show.
15118              * @param {Roo.bootstrap.DateField} this
15119              * @param {Mixed} date The date value
15120              */
15121             show : true,
15122             /**
15123              * @event show
15124              * Fires when this field hide.
15125              * @param {Roo.bootstrap.DateField} this
15126              * @param {Mixed} date The date value
15127              */
15128             hide : true,
15129             /**
15130              * @event select
15131              * Fires when select a date.
15132              * @param {Roo.bootstrap.DateField} this
15133              * @param {Mixed} date The date value
15134              */
15135             select : true
15136         });
15137 };
15138
15139 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
15140     
15141     /**
15142      * @cfg {String} format
15143      * The default date format string which can be overriden for localization support.  The format must be
15144      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
15145      */
15146     format : "m/d/y",
15147     /**
15148      * @cfg {String} altFormats
15149      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
15150      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
15151      */
15152     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
15153     
15154     weekStart : 0,
15155     
15156     viewMode : '',
15157     
15158     minViewMode : '',
15159     
15160     todayHighlight : false,
15161     
15162     todayBtn: false,
15163     
15164     language: 'en',
15165     
15166     keyboardNavigation: true,
15167     
15168     calendarWeeks: false,
15169     
15170     startDate: -Infinity,
15171     
15172     endDate: Infinity,
15173     
15174     daysOfWeekDisabled: [],
15175     
15176     _events: [],
15177     
15178     singleMode : false,
15179     
15180     UTCDate: function()
15181     {
15182         return new Date(Date.UTC.apply(Date, arguments));
15183     },
15184     
15185     UTCToday: function()
15186     {
15187         var today = new Date();
15188         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
15189     },
15190     
15191     getDate: function() {
15192             var d = this.getUTCDate();
15193             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
15194     },
15195     
15196     getUTCDate: function() {
15197             return this.date;
15198     },
15199     
15200     setDate: function(d) {
15201             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
15202     },
15203     
15204     setUTCDate: function(d) {
15205             this.date = d;
15206             this.setValue(this.formatDate(this.date));
15207     },
15208         
15209     onRender: function(ct, position)
15210     {
15211         
15212         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
15213         
15214         this.language = this.language || 'en';
15215         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
15216         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
15217         
15218         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
15219         this.format = this.format || 'm/d/y';
15220         this.isInline = false;
15221         this.isInput = true;
15222         this.component = this.el.select('.add-on', true).first() || false;
15223         this.component = (this.component && this.component.length === 0) ? false : this.component;
15224         this.hasInput = this.component && this.inputEL().length;
15225         
15226         if (typeof(this.minViewMode === 'string')) {
15227             switch (this.minViewMode) {
15228                 case 'months':
15229                     this.minViewMode = 1;
15230                     break;
15231                 case 'years':
15232                     this.minViewMode = 2;
15233                     break;
15234                 default:
15235                     this.minViewMode = 0;
15236                     break;
15237             }
15238         }
15239         
15240         if (typeof(this.viewMode === 'string')) {
15241             switch (this.viewMode) {
15242                 case 'months':
15243                     this.viewMode = 1;
15244                     break;
15245                 case 'years':
15246                     this.viewMode = 2;
15247                     break;
15248                 default:
15249                     this.viewMode = 0;
15250                     break;
15251             }
15252         }
15253                 
15254         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
15255         
15256 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
15257         
15258         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15259         
15260         this.picker().on('mousedown', this.onMousedown, this);
15261         this.picker().on('click', this.onClick, this);
15262         
15263         this.picker().addClass('datepicker-dropdown');
15264         
15265         this.startViewMode = this.viewMode;
15266         
15267         if(this.singleMode){
15268             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
15269                 v.setVisibilityMode(Roo.Element.DISPLAY)
15270                 v.hide();
15271             });
15272             
15273             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
15274                 v.setStyle('width', '189px');
15275             });
15276         }
15277         
15278         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
15279             if(!this.calendarWeeks){
15280                 v.remove();
15281                 return;
15282             }
15283             
15284             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15285             v.attr('colspan', function(i, val){
15286                 return parseInt(val) + 1;
15287             });
15288         })
15289                         
15290         
15291         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
15292         
15293         this.setStartDate(this.startDate);
15294         this.setEndDate(this.endDate);
15295         
15296         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
15297         
15298         this.fillDow();
15299         this.fillMonths();
15300         this.update();
15301         this.showMode();
15302         
15303         if(this.isInline) {
15304             this.show();
15305         }
15306     },
15307     
15308     picker : function()
15309     {
15310         return this.pickerEl;
15311 //        return this.el.select('.datepicker', true).first();
15312     },
15313     
15314     fillDow: function()
15315     {
15316         var dowCnt = this.weekStart;
15317         
15318         var dow = {
15319             tag: 'tr',
15320             cn: [
15321                 
15322             ]
15323         };
15324         
15325         if(this.calendarWeeks){
15326             dow.cn.push({
15327                 tag: 'th',
15328                 cls: 'cw',
15329                 html: '&nbsp;'
15330             })
15331         }
15332         
15333         while (dowCnt < this.weekStart + 7) {
15334             dow.cn.push({
15335                 tag: 'th',
15336                 cls: 'dow',
15337                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
15338             });
15339         }
15340         
15341         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
15342     },
15343     
15344     fillMonths: function()
15345     {    
15346         var i = 0;
15347         var months = this.picker().select('>.datepicker-months td', true).first();
15348         
15349         months.dom.innerHTML = '';
15350         
15351         while (i < 12) {
15352             var month = {
15353                 tag: 'span',
15354                 cls: 'month',
15355                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
15356             }
15357             
15358             months.createChild(month);
15359         }
15360         
15361     },
15362     
15363     update: function()
15364     {
15365         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;
15366         
15367         if (this.date < this.startDate) {
15368             this.viewDate = new Date(this.startDate);
15369         } else if (this.date > this.endDate) {
15370             this.viewDate = new Date(this.endDate);
15371         } else {
15372             this.viewDate = new Date(this.date);
15373         }
15374         
15375         this.fill();
15376     },
15377     
15378     fill: function() 
15379     {
15380         var d = new Date(this.viewDate),
15381                 year = d.getUTCFullYear(),
15382                 month = d.getUTCMonth(),
15383                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
15384                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
15385                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
15386                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
15387                 currentDate = this.date && this.date.valueOf(),
15388                 today = this.UTCToday();
15389         
15390         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
15391         
15392 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15393         
15394 //        this.picker.select('>tfoot th.today').
15395 //                                              .text(dates[this.language].today)
15396 //                                              .toggle(this.todayBtn !== false);
15397     
15398         this.updateNavArrows();
15399         this.fillMonths();
15400                                                 
15401         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
15402         
15403         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
15404          
15405         prevMonth.setUTCDate(day);
15406         
15407         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
15408         
15409         var nextMonth = new Date(prevMonth);
15410         
15411         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
15412         
15413         nextMonth = nextMonth.valueOf();
15414         
15415         var fillMonths = false;
15416         
15417         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
15418         
15419         while(prevMonth.valueOf() < nextMonth) {
15420             var clsName = '';
15421             
15422             if (prevMonth.getUTCDay() === this.weekStart) {
15423                 if(fillMonths){
15424                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
15425                 }
15426                     
15427                 fillMonths = {
15428                     tag: 'tr',
15429                     cn: []
15430                 };
15431                 
15432                 if(this.calendarWeeks){
15433                     // ISO 8601: First week contains first thursday.
15434                     // ISO also states week starts on Monday, but we can be more abstract here.
15435                     var
15436                     // Start of current week: based on weekstart/current date
15437                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
15438                     // Thursday of this week
15439                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
15440                     // First Thursday of year, year from thursday
15441                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
15442                     // Calendar week: ms between thursdays, div ms per day, div 7 days
15443                     calWeek =  (th - yth) / 864e5 / 7 + 1;
15444                     
15445                     fillMonths.cn.push({
15446                         tag: 'td',
15447                         cls: 'cw',
15448                         html: calWeek
15449                     });
15450                 }
15451             }
15452             
15453             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
15454                 clsName += ' old';
15455             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
15456                 clsName += ' new';
15457             }
15458             if (this.todayHighlight &&
15459                 prevMonth.getUTCFullYear() == today.getFullYear() &&
15460                 prevMonth.getUTCMonth() == today.getMonth() &&
15461                 prevMonth.getUTCDate() == today.getDate()) {
15462                 clsName += ' today';
15463             }
15464             
15465             if (currentDate && prevMonth.valueOf() === currentDate) {
15466                 clsName += ' active';
15467             }
15468             
15469             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
15470                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
15471                     clsName += ' disabled';
15472             }
15473             
15474             fillMonths.cn.push({
15475                 tag: 'td',
15476                 cls: 'day ' + clsName,
15477                 html: prevMonth.getDate()
15478             })
15479             
15480             prevMonth.setDate(prevMonth.getDate()+1);
15481         }
15482           
15483         var currentYear = this.date && this.date.getUTCFullYear();
15484         var currentMonth = this.date && this.date.getUTCMonth();
15485         
15486         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
15487         
15488         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
15489             v.removeClass('active');
15490             
15491             if(currentYear === year && k === currentMonth){
15492                 v.addClass('active');
15493             }
15494             
15495             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
15496                 v.addClass('disabled');
15497             }
15498             
15499         });
15500         
15501         
15502         year = parseInt(year/10, 10) * 10;
15503         
15504         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
15505         
15506         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
15507         
15508         year -= 1;
15509         for (var i = -1; i < 11; i++) {
15510             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
15511                 tag: 'span',
15512                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
15513                 html: year
15514             })
15515             
15516             year += 1;
15517         }
15518     },
15519     
15520     showMode: function(dir) 
15521     {
15522         if (dir) {
15523             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15524         }
15525         
15526         Roo.each(this.picker().select('>div',true).elements, function(v){
15527             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15528             v.hide();
15529         });
15530         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15531     },
15532     
15533     place: function()
15534     {
15535         if(this.isInline) return;
15536         
15537         this.picker().removeClass(['bottom', 'top']);
15538         
15539         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15540             /*
15541              * place to the top of element!
15542              *
15543              */
15544             
15545             this.picker().addClass('top');
15546             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15547             
15548             return;
15549         }
15550         
15551         this.picker().addClass('bottom');
15552         
15553         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15554     },
15555     
15556     parseDate : function(value)
15557     {
15558         if(!value || value instanceof Date){
15559             return value;
15560         }
15561         var v = Date.parseDate(value, this.format);
15562         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15563             v = Date.parseDate(value, 'Y-m-d');
15564         }
15565         if(!v && this.altFormats){
15566             if(!this.altFormatsArray){
15567                 this.altFormatsArray = this.altFormats.split("|");
15568             }
15569             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15570                 v = Date.parseDate(value, this.altFormatsArray[i]);
15571             }
15572         }
15573         return v;
15574     },
15575     
15576     formatDate : function(date, fmt)
15577     {   
15578         return (!date || !(date instanceof Date)) ?
15579         date : date.dateFormat(fmt || this.format);
15580     },
15581     
15582     onFocus : function()
15583     {
15584         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15585         this.show();
15586     },
15587     
15588     onBlur : function()
15589     {
15590         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15591         
15592         var d = this.inputEl().getValue();
15593         
15594         this.setValue(d);
15595                 
15596         this.hide();
15597     },
15598     
15599     show : function()
15600     {
15601         this.picker().show();
15602         this.update();
15603         this.place();
15604         
15605         this.fireEvent('show', this, this.date);
15606     },
15607     
15608     hide : function()
15609     {
15610         if(this.isInline) return;
15611         this.picker().hide();
15612         this.viewMode = this.startViewMode;
15613         this.showMode();
15614         
15615         this.fireEvent('hide', this, this.date);
15616         
15617     },
15618     
15619     onMousedown: function(e)
15620     {
15621         e.stopPropagation();
15622         e.preventDefault();
15623     },
15624     
15625     keyup: function(e)
15626     {
15627         Roo.bootstrap.DateField.superclass.keyup.call(this);
15628         this.update();
15629     },
15630
15631     setValue: function(v)
15632     {
15633         
15634         // v can be a string or a date..
15635         
15636         
15637         var d = new Date(this.parseDate(v) ).clearTime();
15638         
15639         if(isNaN(d.getTime())){
15640             this.date = this.viewDate = '';
15641             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15642             return;
15643         }
15644         
15645         v = this.formatDate(d);
15646         
15647         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15648         
15649         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15650      
15651         this.update();
15652
15653         this.fireEvent('select', this, this.date);
15654         
15655     },
15656     
15657     getValue: function()
15658     {
15659         return this.formatDate(this.date);
15660     },
15661     
15662     fireKey: function(e)
15663     {
15664         if (!this.picker().isVisible()){
15665             if (e.keyCode == 27) // allow escape to hide and re-show picker
15666                 this.show();
15667             return;
15668         }
15669         
15670         var dateChanged = false,
15671         dir, day, month,
15672         newDate, newViewDate;
15673         
15674         switch(e.keyCode){
15675             case 27: // escape
15676                 this.hide();
15677                 e.preventDefault();
15678                 break;
15679             case 37: // left
15680             case 39: // right
15681                 if (!this.keyboardNavigation) break;
15682                 dir = e.keyCode == 37 ? -1 : 1;
15683                 
15684                 if (e.ctrlKey){
15685                     newDate = this.moveYear(this.date, dir);
15686                     newViewDate = this.moveYear(this.viewDate, dir);
15687                 } else if (e.shiftKey){
15688                     newDate = this.moveMonth(this.date, dir);
15689                     newViewDate = this.moveMonth(this.viewDate, dir);
15690                 } else {
15691                     newDate = new Date(this.date);
15692                     newDate.setUTCDate(this.date.getUTCDate() + dir);
15693                     newViewDate = new Date(this.viewDate);
15694                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15695                 }
15696                 if (this.dateWithinRange(newDate)){
15697                     this.date = newDate;
15698                     this.viewDate = newViewDate;
15699                     this.setValue(this.formatDate(this.date));
15700 //                    this.update();
15701                     e.preventDefault();
15702                     dateChanged = true;
15703                 }
15704                 break;
15705             case 38: // up
15706             case 40: // down
15707                 if (!this.keyboardNavigation) break;
15708                 dir = e.keyCode == 38 ? -1 : 1;
15709                 if (e.ctrlKey){
15710                     newDate = this.moveYear(this.date, dir);
15711                     newViewDate = this.moveYear(this.viewDate, dir);
15712                 } else if (e.shiftKey){
15713                     newDate = this.moveMonth(this.date, dir);
15714                     newViewDate = this.moveMonth(this.viewDate, dir);
15715                 } else {
15716                     newDate = new Date(this.date);
15717                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15718                     newViewDate = new Date(this.viewDate);
15719                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15720                 }
15721                 if (this.dateWithinRange(newDate)){
15722                     this.date = newDate;
15723                     this.viewDate = newViewDate;
15724                     this.setValue(this.formatDate(this.date));
15725 //                    this.update();
15726                     e.preventDefault();
15727                     dateChanged = true;
15728                 }
15729                 break;
15730             case 13: // enter
15731                 this.setValue(this.formatDate(this.date));
15732                 this.hide();
15733                 e.preventDefault();
15734                 break;
15735             case 9: // tab
15736                 this.setValue(this.formatDate(this.date));
15737                 this.hide();
15738                 break;
15739             case 16: // shift
15740             case 17: // ctrl
15741             case 18: // alt
15742                 break;
15743             default :
15744                 this.hide();
15745                 
15746         }
15747     },
15748     
15749     
15750     onClick: function(e) 
15751     {
15752         e.stopPropagation();
15753         e.preventDefault();
15754         
15755         var target = e.getTarget();
15756         
15757         if(target.nodeName.toLowerCase() === 'i'){
15758             target = Roo.get(target).dom.parentNode;
15759         }
15760         
15761         var nodeName = target.nodeName;
15762         var className = target.className;
15763         var html = target.innerHTML;
15764         //Roo.log(nodeName);
15765         
15766         switch(nodeName.toLowerCase()) {
15767             case 'th':
15768                 switch(className) {
15769                     case 'switch':
15770                         this.showMode(1);
15771                         break;
15772                     case 'prev':
15773                     case 'next':
15774                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15775                         switch(this.viewMode){
15776                                 case 0:
15777                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15778                                         break;
15779                                 case 1:
15780                                 case 2:
15781                                         this.viewDate = this.moveYear(this.viewDate, dir);
15782                                         break;
15783                         }
15784                         this.fill();
15785                         break;
15786                     case 'today':
15787                         var date = new Date();
15788                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15789 //                        this.fill()
15790                         this.setValue(this.formatDate(this.date));
15791                         
15792                         this.hide();
15793                         break;
15794                 }
15795                 break;
15796             case 'span':
15797                 if (className.indexOf('disabled') < 0) {
15798                     this.viewDate.setUTCDate(1);
15799                     if (className.indexOf('month') > -1) {
15800                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15801                     } else {
15802                         var year = parseInt(html, 10) || 0;
15803                         this.viewDate.setUTCFullYear(year);
15804                         
15805                     }
15806                     
15807                     if(this.singleMode){
15808                         this.setValue(this.formatDate(this.viewDate));
15809                         this.hide();
15810                         return;
15811                     }
15812                     
15813                     this.showMode(-1);
15814                     this.fill();
15815                 }
15816                 break;
15817                 
15818             case 'td':
15819                 //Roo.log(className);
15820                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15821                     var day = parseInt(html, 10) || 1;
15822                     var year = this.viewDate.getUTCFullYear(),
15823                         month = this.viewDate.getUTCMonth();
15824
15825                     if (className.indexOf('old') > -1) {
15826                         if(month === 0 ){
15827                             month = 11;
15828                             year -= 1;
15829                         }else{
15830                             month -= 1;
15831                         }
15832                     } else if (className.indexOf('new') > -1) {
15833                         if (month == 11) {
15834                             month = 0;
15835                             year += 1;
15836                         } else {
15837                             month += 1;
15838                         }
15839                     }
15840                     //Roo.log([year,month,day]);
15841                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15842                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15843 //                    this.fill();
15844                     //Roo.log(this.formatDate(this.date));
15845                     this.setValue(this.formatDate(this.date));
15846                     this.hide();
15847                 }
15848                 break;
15849         }
15850     },
15851     
15852     setStartDate: function(startDate)
15853     {
15854         this.startDate = startDate || -Infinity;
15855         if (this.startDate !== -Infinity) {
15856             this.startDate = this.parseDate(this.startDate);
15857         }
15858         this.update();
15859         this.updateNavArrows();
15860     },
15861
15862     setEndDate: function(endDate)
15863     {
15864         this.endDate = endDate || Infinity;
15865         if (this.endDate !== Infinity) {
15866             this.endDate = this.parseDate(this.endDate);
15867         }
15868         this.update();
15869         this.updateNavArrows();
15870     },
15871     
15872     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15873     {
15874         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15875         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15876             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15877         }
15878         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15879             return parseInt(d, 10);
15880         });
15881         this.update();
15882         this.updateNavArrows();
15883     },
15884     
15885     updateNavArrows: function() 
15886     {
15887         if(this.singleMode){
15888             return;
15889         }
15890         
15891         var d = new Date(this.viewDate),
15892         year = d.getUTCFullYear(),
15893         month = d.getUTCMonth();
15894         
15895         Roo.each(this.picker().select('.prev', true).elements, function(v){
15896             v.show();
15897             switch (this.viewMode) {
15898                 case 0:
15899
15900                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15901                         v.hide();
15902                     }
15903                     break;
15904                 case 1:
15905                 case 2:
15906                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15907                         v.hide();
15908                     }
15909                     break;
15910             }
15911         });
15912         
15913         Roo.each(this.picker().select('.next', true).elements, function(v){
15914             v.show();
15915             switch (this.viewMode) {
15916                 case 0:
15917
15918                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15919                         v.hide();
15920                     }
15921                     break;
15922                 case 1:
15923                 case 2:
15924                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15925                         v.hide();
15926                     }
15927                     break;
15928             }
15929         })
15930     },
15931     
15932     moveMonth: function(date, dir)
15933     {
15934         if (!dir) return date;
15935         var new_date = new Date(date.valueOf()),
15936         day = new_date.getUTCDate(),
15937         month = new_date.getUTCMonth(),
15938         mag = Math.abs(dir),
15939         new_month, test;
15940         dir = dir > 0 ? 1 : -1;
15941         if (mag == 1){
15942             test = dir == -1
15943             // If going back one month, make sure month is not current month
15944             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15945             ? function(){
15946                 return new_date.getUTCMonth() == month;
15947             }
15948             // If going forward one month, make sure month is as expected
15949             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15950             : function(){
15951                 return new_date.getUTCMonth() != new_month;
15952             };
15953             new_month = month + dir;
15954             new_date.setUTCMonth(new_month);
15955             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15956             if (new_month < 0 || new_month > 11)
15957                 new_month = (new_month + 12) % 12;
15958         } else {
15959             // For magnitudes >1, move one month at a time...
15960             for (var i=0; i<mag; i++)
15961                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15962                 new_date = this.moveMonth(new_date, dir);
15963             // ...then reset the day, keeping it in the new month
15964             new_month = new_date.getUTCMonth();
15965             new_date.setUTCDate(day);
15966             test = function(){
15967                 return new_month != new_date.getUTCMonth();
15968             };
15969         }
15970         // Common date-resetting loop -- if date is beyond end of month, make it
15971         // end of month
15972         while (test()){
15973             new_date.setUTCDate(--day);
15974             new_date.setUTCMonth(new_month);
15975         }
15976         return new_date;
15977     },
15978
15979     moveYear: function(date, dir)
15980     {
15981         return this.moveMonth(date, dir*12);
15982     },
15983
15984     dateWithinRange: function(date)
15985     {
15986         return date >= this.startDate && date <= this.endDate;
15987     },
15988
15989     
15990     remove: function() 
15991     {
15992         this.picker().remove();
15993     }
15994    
15995 });
15996
15997 Roo.apply(Roo.bootstrap.DateField,  {
15998     
15999     head : {
16000         tag: 'thead',
16001         cn: [
16002         {
16003             tag: 'tr',
16004             cn: [
16005             {
16006                 tag: 'th',
16007                 cls: 'prev',
16008                 html: '<i class="fa fa-arrow-left"/>'
16009             },
16010             {
16011                 tag: 'th',
16012                 cls: 'switch',
16013                 colspan: '5'
16014             },
16015             {
16016                 tag: 'th',
16017                 cls: 'next',
16018                 html: '<i class="fa fa-arrow-right"/>'
16019             }
16020
16021             ]
16022         }
16023         ]
16024     },
16025     
16026     content : {
16027         tag: 'tbody',
16028         cn: [
16029         {
16030             tag: 'tr',
16031             cn: [
16032             {
16033                 tag: 'td',
16034                 colspan: '7'
16035             }
16036             ]
16037         }
16038         ]
16039     },
16040     
16041     footer : {
16042         tag: 'tfoot',
16043         cn: [
16044         {
16045             tag: 'tr',
16046             cn: [
16047             {
16048                 tag: 'th',
16049                 colspan: '7',
16050                 cls: 'today'
16051             }
16052                     
16053             ]
16054         }
16055         ]
16056     },
16057     
16058     dates:{
16059         en: {
16060             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
16061             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
16062             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
16063             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16064             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
16065             today: "Today"
16066         }
16067     },
16068     
16069     modes: [
16070     {
16071         clsName: 'days',
16072         navFnc: 'Month',
16073         navStep: 1
16074     },
16075     {
16076         clsName: 'months',
16077         navFnc: 'FullYear',
16078         navStep: 1
16079     },
16080     {
16081         clsName: 'years',
16082         navFnc: 'FullYear',
16083         navStep: 10
16084     }]
16085 });
16086
16087 Roo.apply(Roo.bootstrap.DateField,  {
16088   
16089     template : {
16090         tag: 'div',
16091         cls: 'datepicker dropdown-menu roo-dynamic',
16092         cn: [
16093         {
16094             tag: 'div',
16095             cls: 'datepicker-days',
16096             cn: [
16097             {
16098                 tag: 'table',
16099                 cls: 'table-condensed',
16100                 cn:[
16101                 Roo.bootstrap.DateField.head,
16102                 {
16103                     tag: 'tbody'
16104                 },
16105                 Roo.bootstrap.DateField.footer
16106                 ]
16107             }
16108             ]
16109         },
16110         {
16111             tag: 'div',
16112             cls: 'datepicker-months',
16113             cn: [
16114             {
16115                 tag: 'table',
16116                 cls: 'table-condensed',
16117                 cn:[
16118                 Roo.bootstrap.DateField.head,
16119                 Roo.bootstrap.DateField.content,
16120                 Roo.bootstrap.DateField.footer
16121                 ]
16122             }
16123             ]
16124         },
16125         {
16126             tag: 'div',
16127             cls: 'datepicker-years',
16128             cn: [
16129             {
16130                 tag: 'table',
16131                 cls: 'table-condensed',
16132                 cn:[
16133                 Roo.bootstrap.DateField.head,
16134                 Roo.bootstrap.DateField.content,
16135                 Roo.bootstrap.DateField.footer
16136                 ]
16137             }
16138             ]
16139         }
16140         ]
16141     }
16142 });
16143
16144  
16145
16146  /*
16147  * - LGPL
16148  *
16149  * TimeField
16150  * 
16151  */
16152
16153 /**
16154  * @class Roo.bootstrap.TimeField
16155  * @extends Roo.bootstrap.Input
16156  * Bootstrap DateField class
16157  * 
16158  * 
16159  * @constructor
16160  * Create a new TimeField
16161  * @param {Object} config The config object
16162  */
16163
16164 Roo.bootstrap.TimeField = function(config){
16165     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
16166     this.addEvents({
16167             /**
16168              * @event show
16169              * Fires when this field show.
16170              * @param {Roo.bootstrap.DateField} thisthis
16171              * @param {Mixed} date The date value
16172              */
16173             show : true,
16174             /**
16175              * @event show
16176              * Fires when this field hide.
16177              * @param {Roo.bootstrap.DateField} this
16178              * @param {Mixed} date The date value
16179              */
16180             hide : true,
16181             /**
16182              * @event select
16183              * Fires when select a date.
16184              * @param {Roo.bootstrap.DateField} this
16185              * @param {Mixed} date The date value
16186              */
16187             select : true
16188         });
16189 };
16190
16191 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
16192     
16193     /**
16194      * @cfg {String} format
16195      * The default time format string which can be overriden for localization support.  The format must be
16196      * valid according to {@link Date#parseDate} (defaults to 'H:i').
16197      */
16198     format : "H:i",
16199        
16200     onRender: function(ct, position)
16201     {
16202         
16203         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
16204                 
16205         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
16206         
16207         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16208         
16209         this.pop = this.picker().select('>.datepicker-time',true).first();
16210         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16211         
16212         this.picker().on('mousedown', this.onMousedown, this);
16213         this.picker().on('click', this.onClick, this);
16214         
16215         this.picker().addClass('datepicker-dropdown');
16216     
16217         this.fillTime();
16218         this.update();
16219             
16220         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
16221         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
16222         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
16223         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
16224         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
16225         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
16226
16227     },
16228     
16229     fireKey: function(e){
16230         if (!this.picker().isVisible()){
16231             if (e.keyCode == 27) { // allow escape to hide and re-show picker
16232                 this.show();
16233             }
16234             return;
16235         }
16236
16237         e.preventDefault();
16238         
16239         switch(e.keyCode){
16240             case 27: // escape
16241                 this.hide();
16242                 break;
16243             case 37: // left
16244             case 39: // right
16245                 this.onTogglePeriod();
16246                 break;
16247             case 38: // up
16248                 this.onIncrementMinutes();
16249                 break;
16250             case 40: // down
16251                 this.onDecrementMinutes();
16252                 break;
16253             case 13: // enter
16254             case 9: // tab
16255                 this.setTime();
16256                 break;
16257         }
16258     },
16259     
16260     onClick: function(e) {
16261         e.stopPropagation();
16262         e.preventDefault();
16263     },
16264     
16265     picker : function()
16266     {
16267         return this.el.select('.datepicker', true).first();
16268     },
16269     
16270     fillTime: function()
16271     {    
16272         var time = this.pop.select('tbody', true).first();
16273         
16274         time.dom.innerHTML = '';
16275         
16276         time.createChild({
16277             tag: 'tr',
16278             cn: [
16279                 {
16280                     tag: 'td',
16281                     cn: [
16282                         {
16283                             tag: 'a',
16284                             href: '#',
16285                             cls: 'btn',
16286                             cn: [
16287                                 {
16288                                     tag: 'span',
16289                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
16290                                 }
16291                             ]
16292                         } 
16293                     ]
16294                 },
16295                 {
16296                     tag: 'td',
16297                     cls: 'separator'
16298                 },
16299                 {
16300                     tag: 'td',
16301                     cn: [
16302                         {
16303                             tag: 'a',
16304                             href: '#',
16305                             cls: 'btn',
16306                             cn: [
16307                                 {
16308                                     tag: 'span',
16309                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
16310                                 }
16311                             ]
16312                         }
16313                     ]
16314                 },
16315                 {
16316                     tag: 'td',
16317                     cls: 'separator'
16318                 }
16319             ]
16320         });
16321         
16322         time.createChild({
16323             tag: 'tr',
16324             cn: [
16325                 {
16326                     tag: 'td',
16327                     cn: [
16328                         {
16329                             tag: 'span',
16330                             cls: 'timepicker-hour',
16331                             html: '00'
16332                         }  
16333                     ]
16334                 },
16335                 {
16336                     tag: 'td',
16337                     cls: 'separator',
16338                     html: ':'
16339                 },
16340                 {
16341                     tag: 'td',
16342                     cn: [
16343                         {
16344                             tag: 'span',
16345                             cls: 'timepicker-minute',
16346                             html: '00'
16347                         }  
16348                     ]
16349                 },
16350                 {
16351                     tag: 'td',
16352                     cls: 'separator'
16353                 },
16354                 {
16355                     tag: 'td',
16356                     cn: [
16357                         {
16358                             tag: 'button',
16359                             type: 'button',
16360                             cls: 'btn btn-primary period',
16361                             html: 'AM'
16362                             
16363                         }
16364                     ]
16365                 }
16366             ]
16367         });
16368         
16369         time.createChild({
16370             tag: 'tr',
16371             cn: [
16372                 {
16373                     tag: 'td',
16374                     cn: [
16375                         {
16376                             tag: 'a',
16377                             href: '#',
16378                             cls: 'btn',
16379                             cn: [
16380                                 {
16381                                     tag: 'span',
16382                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
16383                                 }
16384                             ]
16385                         }
16386                     ]
16387                 },
16388                 {
16389                     tag: 'td',
16390                     cls: 'separator'
16391                 },
16392                 {
16393                     tag: 'td',
16394                     cn: [
16395                         {
16396                             tag: 'a',
16397                             href: '#',
16398                             cls: 'btn',
16399                             cn: [
16400                                 {
16401                                     tag: 'span',
16402                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
16403                                 }
16404                             ]
16405                         }
16406                     ]
16407                 },
16408                 {
16409                     tag: 'td',
16410                     cls: 'separator'
16411                 }
16412             ]
16413         });
16414         
16415     },
16416     
16417     update: function()
16418     {
16419         
16420         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
16421         
16422         this.fill();
16423     },
16424     
16425     fill: function() 
16426     {
16427         var hours = this.time.getHours();
16428         var minutes = this.time.getMinutes();
16429         var period = 'AM';
16430         
16431         if(hours > 11){
16432             period = 'PM';
16433         }
16434         
16435         if(hours == 0){
16436             hours = 12;
16437         }
16438         
16439         
16440         if(hours > 12){
16441             hours = hours - 12;
16442         }
16443         
16444         if(hours < 10){
16445             hours = '0' + hours;
16446         }
16447         
16448         if(minutes < 10){
16449             minutes = '0' + minutes;
16450         }
16451         
16452         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
16453         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
16454         this.pop.select('button', true).first().dom.innerHTML = period;
16455         
16456     },
16457     
16458     place: function()
16459     {   
16460         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
16461         
16462         var cls = ['bottom'];
16463         
16464         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
16465             cls.pop();
16466             cls.push('top');
16467         }
16468         
16469         cls.push('right');
16470         
16471         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
16472             cls.pop();
16473             cls.push('left');
16474         }
16475         
16476         this.picker().addClass(cls.join('-'));
16477         
16478         var _this = this;
16479         
16480         Roo.each(cls, function(c){
16481             if(c == 'bottom'){
16482                 _this.picker().setTop(_this.inputEl().getHeight());
16483                 return;
16484             }
16485             if(c == 'top'){
16486                 _this.picker().setTop(0 - _this.picker().getHeight());
16487                 return;
16488             }
16489             
16490             if(c == 'left'){
16491                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
16492                 return;
16493             }
16494             if(c == 'right'){
16495                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
16496                 return;
16497             }
16498         });
16499         
16500     },
16501   
16502     onFocus : function()
16503     {
16504         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
16505         this.show();
16506     },
16507     
16508     onBlur : function()
16509     {
16510         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
16511         this.hide();
16512     },
16513     
16514     show : function()
16515     {
16516         this.picker().show();
16517         this.pop.show();
16518         this.update();
16519         this.place();
16520         
16521         this.fireEvent('show', this, this.date);
16522     },
16523     
16524     hide : function()
16525     {
16526         this.picker().hide();
16527         this.pop.hide();
16528         
16529         this.fireEvent('hide', this, this.date);
16530     },
16531     
16532     setTime : function()
16533     {
16534         this.hide();
16535         this.setValue(this.time.format(this.format));
16536         
16537         this.fireEvent('select', this, this.date);
16538         
16539         
16540     },
16541     
16542     onMousedown: function(e){
16543         e.stopPropagation();
16544         e.preventDefault();
16545     },
16546     
16547     onIncrementHours: function()
16548     {
16549         Roo.log('onIncrementHours');
16550         this.time = this.time.add(Date.HOUR, 1);
16551         this.update();
16552         
16553     },
16554     
16555     onDecrementHours: function()
16556     {
16557         Roo.log('onDecrementHours');
16558         this.time = this.time.add(Date.HOUR, -1);
16559         this.update();
16560     },
16561     
16562     onIncrementMinutes: function()
16563     {
16564         Roo.log('onIncrementMinutes');
16565         this.time = this.time.add(Date.MINUTE, 1);
16566         this.update();
16567     },
16568     
16569     onDecrementMinutes: function()
16570     {
16571         Roo.log('onDecrementMinutes');
16572         this.time = this.time.add(Date.MINUTE, -1);
16573         this.update();
16574     },
16575     
16576     onTogglePeriod: function()
16577     {
16578         Roo.log('onTogglePeriod');
16579         this.time = this.time.add(Date.HOUR, 12);
16580         this.update();
16581     }
16582     
16583    
16584 });
16585
16586 Roo.apply(Roo.bootstrap.TimeField,  {
16587     
16588     content : {
16589         tag: 'tbody',
16590         cn: [
16591             {
16592                 tag: 'tr',
16593                 cn: [
16594                 {
16595                     tag: 'td',
16596                     colspan: '7'
16597                 }
16598                 ]
16599             }
16600         ]
16601     },
16602     
16603     footer : {
16604         tag: 'tfoot',
16605         cn: [
16606             {
16607                 tag: 'tr',
16608                 cn: [
16609                 {
16610                     tag: 'th',
16611                     colspan: '7',
16612                     cls: '',
16613                     cn: [
16614                         {
16615                             tag: 'button',
16616                             cls: 'btn btn-info ok',
16617                             html: 'OK'
16618                         }
16619                     ]
16620                 }
16621
16622                 ]
16623             }
16624         ]
16625     }
16626 });
16627
16628 Roo.apply(Roo.bootstrap.TimeField,  {
16629   
16630     template : {
16631         tag: 'div',
16632         cls: 'datepicker dropdown-menu',
16633         cn: [
16634             {
16635                 tag: 'div',
16636                 cls: 'datepicker-time',
16637                 cn: [
16638                 {
16639                     tag: 'table',
16640                     cls: 'table-condensed',
16641                     cn:[
16642                     Roo.bootstrap.TimeField.content,
16643                     Roo.bootstrap.TimeField.footer
16644                     ]
16645                 }
16646                 ]
16647             }
16648         ]
16649     }
16650 });
16651
16652  
16653
16654  /*
16655  * - LGPL
16656  *
16657  * MonthField
16658  * 
16659  */
16660
16661 /**
16662  * @class Roo.bootstrap.MonthField
16663  * @extends Roo.bootstrap.Input
16664  * Bootstrap MonthField class
16665  * 
16666  * @cfg {String} language default en
16667  * 
16668  * @constructor
16669  * Create a new MonthField
16670  * @param {Object} config The config object
16671  */
16672
16673 Roo.bootstrap.MonthField = function(config){
16674     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
16675     
16676     this.addEvents({
16677         /**
16678          * @event show
16679          * Fires when this field show.
16680          * @param {Roo.bootstrap.MonthField} this
16681          * @param {Mixed} date The date value
16682          */
16683         show : true,
16684         /**
16685          * @event show
16686          * Fires when this field hide.
16687          * @param {Roo.bootstrap.MonthField} this
16688          * @param {Mixed} date The date value
16689          */
16690         hide : true,
16691         /**
16692          * @event select
16693          * Fires when select a date.
16694          * @param {Roo.bootstrap.MonthField} this
16695          * @param {String} oldvalue The old value
16696          * @param {String} newvalue The new value
16697          */
16698         select : true
16699     });
16700 };
16701
16702 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
16703     
16704     onRender: function(ct, position)
16705     {
16706         
16707         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
16708         
16709         this.language = this.language || 'en';
16710         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
16711         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
16712         
16713         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
16714         this.isInline = false;
16715         this.isInput = true;
16716         this.component = this.el.select('.add-on', true).first() || false;
16717         this.component = (this.component && this.component.length === 0) ? false : this.component;
16718         this.hasInput = this.component && this.inputEL().length;
16719         
16720         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
16721         
16722         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16723         
16724         this.picker().on('mousedown', this.onMousedown, this);
16725         this.picker().on('click', this.onClick, this);
16726         
16727         this.picker().addClass('datepicker-dropdown');
16728         
16729         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16730             v.setStyle('width', '189px');
16731         });
16732         
16733         this.fillMonths();
16734         
16735         this.update();
16736         
16737         if(this.isInline) {
16738             this.show();
16739         }
16740         
16741     },
16742     
16743     setValue: function(v, suppressEvent)
16744     {   
16745         var o = this.getValue();
16746         
16747         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
16748         
16749         this.update();
16750
16751         if(suppressEvent !== true){
16752             this.fireEvent('select', this, o, v);
16753         }
16754         
16755     },
16756     
16757     getValue: function()
16758     {
16759         return this.value;
16760     },
16761     
16762     onClick: function(e) 
16763     {
16764         e.stopPropagation();
16765         e.preventDefault();
16766         
16767         var target = e.getTarget();
16768         
16769         if(target.nodeName.toLowerCase() === 'i'){
16770             target = Roo.get(target).dom.parentNode;
16771         }
16772         
16773         var nodeName = target.nodeName;
16774         var className = target.className;
16775         var html = target.innerHTML;
16776         
16777         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
16778             return;
16779         }
16780         
16781         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
16782         
16783         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16784         
16785         this.hide();
16786                         
16787     },
16788     
16789     picker : function()
16790     {
16791         return this.pickerEl;
16792     },
16793     
16794     fillMonths: function()
16795     {    
16796         var i = 0;
16797         var months = this.picker().select('>.datepicker-months td', true).first();
16798         
16799         months.dom.innerHTML = '';
16800         
16801         while (i < 12) {
16802             var month = {
16803                 tag: 'span',
16804                 cls: 'month',
16805                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
16806             }
16807             
16808             months.createChild(month);
16809         }
16810         
16811     },
16812     
16813     update: function()
16814     {
16815         var _this = this;
16816         
16817         if(typeof(this.vIndex) == 'undefined' && this.value.length){
16818             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
16819         }
16820         
16821         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
16822             e.removeClass('active');
16823             
16824             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
16825                 e.addClass('active');
16826             }
16827         })
16828     },
16829     
16830     place: function()
16831     {
16832         if(this.isInline) return;
16833         
16834         this.picker().removeClass(['bottom', 'top']);
16835         
16836         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16837             /*
16838              * place to the top of element!
16839              *
16840              */
16841             
16842             this.picker().addClass('top');
16843             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16844             
16845             return;
16846         }
16847         
16848         this.picker().addClass('bottom');
16849         
16850         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16851     },
16852     
16853     onFocus : function()
16854     {
16855         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
16856         this.show();
16857     },
16858     
16859     onBlur : function()
16860     {
16861         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
16862         
16863         var d = this.inputEl().getValue();
16864         
16865         this.setValue(d);
16866                 
16867         this.hide();
16868     },
16869     
16870     show : function()
16871     {
16872         this.picker().show();
16873         this.picker().select('>.datepicker-months', true).first().show();
16874         this.update();
16875         this.place();
16876         
16877         this.fireEvent('show', this, this.date);
16878     },
16879     
16880     hide : function()
16881     {
16882         if(this.isInline) return;
16883         this.picker().hide();
16884         this.fireEvent('hide', this, this.date);
16885         
16886     },
16887     
16888     onMousedown: function(e)
16889     {
16890         e.stopPropagation();
16891         e.preventDefault();
16892     },
16893     
16894     keyup: function(e)
16895     {
16896         Roo.bootstrap.MonthField.superclass.keyup.call(this);
16897         this.update();
16898     },
16899
16900     fireKey: function(e)
16901     {
16902         if (!this.picker().isVisible()){
16903             if (e.keyCode == 27) // allow escape to hide and re-show picker
16904                 this.show();
16905             return;
16906         }
16907         
16908         var dir;
16909         
16910         switch(e.keyCode){
16911             case 27: // escape
16912                 this.hide();
16913                 e.preventDefault();
16914                 break;
16915             case 37: // left
16916             case 39: // right
16917                 dir = e.keyCode == 37 ? -1 : 1;
16918                 
16919                 this.vIndex = this.vIndex + dir;
16920                 
16921                 if(this.vIndex < 0){
16922                     this.vIndex = 0;
16923                 }
16924                 
16925                 if(this.vIndex > 11){
16926                     this.vIndex = 11;
16927                 }
16928                 
16929                 if(isNaN(this.vIndex)){
16930                     this.vIndex = 0;
16931                 }
16932                 
16933                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16934                 
16935                 break;
16936             case 38: // up
16937             case 40: // down
16938                 
16939                 dir = e.keyCode == 38 ? -1 : 1;
16940                 
16941                 this.vIndex = this.vIndex + dir * 4;
16942                 
16943                 if(this.vIndex < 0){
16944                     this.vIndex = 0;
16945                 }
16946                 
16947                 if(this.vIndex > 11){
16948                     this.vIndex = 11;
16949                 }
16950                 
16951                 if(isNaN(this.vIndex)){
16952                     this.vIndex = 0;
16953                 }
16954                 
16955                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16956                 break;
16957                 
16958             case 13: // enter
16959                 
16960                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16961                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16962                 }
16963                 
16964                 this.hide();
16965                 e.preventDefault();
16966                 break;
16967             case 9: // tab
16968                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16969                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16970                 }
16971                 this.hide();
16972                 break;
16973             case 16: // shift
16974             case 17: // ctrl
16975             case 18: // alt
16976                 break;
16977             default :
16978                 this.hide();
16979                 
16980         }
16981     },
16982     
16983     remove: function() 
16984     {
16985         this.picker().remove();
16986     }
16987    
16988 });
16989
16990 Roo.apply(Roo.bootstrap.MonthField,  {
16991     
16992     content : {
16993         tag: 'tbody',
16994         cn: [
16995         {
16996             tag: 'tr',
16997             cn: [
16998             {
16999                 tag: 'td',
17000                 colspan: '7'
17001             }
17002             ]
17003         }
17004         ]
17005     },
17006     
17007     dates:{
17008         en: {
17009             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17010             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
17011         }
17012     }
17013 });
17014
17015 Roo.apply(Roo.bootstrap.MonthField,  {
17016   
17017     template : {
17018         tag: 'div',
17019         cls: 'datepicker dropdown-menu roo-dynamic',
17020         cn: [
17021             {
17022                 tag: 'div',
17023                 cls: 'datepicker-months',
17024                 cn: [
17025                 {
17026                     tag: 'table',
17027                     cls: 'table-condensed',
17028                     cn:[
17029                         Roo.bootstrap.DateField.content
17030                     ]
17031                 }
17032                 ]
17033             }
17034         ]
17035     }
17036 });
17037
17038  
17039
17040  
17041  /*
17042  * - LGPL
17043  *
17044  * CheckBox
17045  * 
17046  */
17047
17048 /**
17049  * @class Roo.bootstrap.CheckBox
17050  * @extends Roo.bootstrap.Input
17051  * Bootstrap CheckBox class
17052  * 
17053  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17054  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17055  * @cfg {String} boxLabel The text that appears beside the checkbox
17056  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
17057  * @cfg {Boolean} checked initnal the element
17058  * @cfg {Boolean} inline inline the element (default false)
17059  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
17060  * 
17061  * @constructor
17062  * Create a new CheckBox
17063  * @param {Object} config The config object
17064  */
17065
17066 Roo.bootstrap.CheckBox = function(config){
17067     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
17068    
17069     this.addEvents({
17070         /**
17071         * @event check
17072         * Fires when the element is checked or unchecked.
17073         * @param {Roo.bootstrap.CheckBox} this This input
17074         * @param {Boolean} checked The new checked value
17075         */
17076        check : true
17077     });
17078     
17079 };
17080
17081 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
17082   
17083     inputType: 'checkbox',
17084     inputValue: 1,
17085     valueOff: 0,
17086     boxLabel: false,
17087     checked: false,
17088     weight : false,
17089     inline: false,
17090     
17091     getAutoCreate : function()
17092     {
17093         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17094         
17095         var id = Roo.id();
17096         
17097         var cfg = {};
17098         
17099         cfg.cls = 'form-group ' + this.inputType; //input-group
17100         
17101         if(this.inline){
17102             cfg.cls += ' ' + this.inputType + '-inline';
17103         }
17104         
17105         var input =  {
17106             tag: 'input',
17107             id : id,
17108             type : this.inputType,
17109             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
17110             cls : 'roo-' + this.inputType, //'form-box',
17111             placeholder : this.placeholder || ''
17112             
17113         };
17114         
17115         if (this.weight) { // Validity check?
17116             cfg.cls += " " + this.inputType + "-" + this.weight;
17117         }
17118         
17119         if (this.disabled) {
17120             input.disabled=true;
17121         }
17122         
17123         if(this.checked){
17124             input.checked = this.checked;
17125         }
17126         
17127         if (this.name) {
17128             input.name = this.name;
17129         }
17130         
17131         if (this.size) {
17132             input.cls += ' input-' + this.size;
17133         }
17134         
17135         var settings=this;
17136         
17137         ['xs','sm','md','lg'].map(function(size){
17138             if (settings[size]) {
17139                 cfg.cls += ' col-' + size + '-' + settings[size];
17140             }
17141         });
17142         
17143         var inputblock = input;
17144          
17145         if (this.before || this.after) {
17146             
17147             inputblock = {
17148                 cls : 'input-group',
17149                 cn :  [] 
17150             };
17151             
17152             if (this.before) {
17153                 inputblock.cn.push({
17154                     tag :'span',
17155                     cls : 'input-group-addon',
17156                     html : this.before
17157                 });
17158             }
17159             
17160             inputblock.cn.push(input);
17161             
17162             if (this.after) {
17163                 inputblock.cn.push({
17164                     tag :'span',
17165                     cls : 'input-group-addon',
17166                     html : this.after
17167                 });
17168             }
17169             
17170         }
17171         
17172         if (align ==='left' && this.fieldLabel.length) {
17173                 Roo.log("left and has label");
17174                 cfg.cn = [
17175                     
17176                     {
17177                         tag: 'label',
17178                         'for' :  id,
17179                         cls : 'control-label col-md-' + this.labelWidth,
17180                         html : this.fieldLabel
17181                         
17182                     },
17183                     {
17184                         cls : "col-md-" + (12 - this.labelWidth), 
17185                         cn: [
17186                             inputblock
17187                         ]
17188                     }
17189                     
17190                 ];
17191         } else if ( this.fieldLabel.length) {
17192                 Roo.log(" label");
17193                 cfg.cn = [
17194                    
17195                     {
17196                         tag: this.boxLabel ? 'span' : 'label',
17197                         'for': id,
17198                         cls: 'control-label box-input-label',
17199                         //cls : 'input-group-addon',
17200                         html : this.fieldLabel
17201                         
17202                     },
17203                     
17204                     inputblock
17205                     
17206                 ];
17207
17208         } else {
17209             
17210                 Roo.log(" no label && no align");
17211                 cfg.cn = [  inputblock ] ;
17212                 
17213                 
17214         }
17215         if(this.boxLabel){
17216              var boxLabelCfg = {
17217                 tag: 'label',
17218                 //'for': id, // box label is handled by onclick - so no for...
17219                 cls: 'box-label',
17220                 html: this.boxLabel
17221             }
17222             
17223             if(this.tooltip){
17224                 boxLabelCfg.tooltip = this.tooltip;
17225             }
17226              
17227             cfg.cn.push(boxLabelCfg);
17228         }
17229         
17230         
17231        
17232         return cfg;
17233         
17234     },
17235     
17236     /**
17237      * return the real input element.
17238      */
17239     inputEl: function ()
17240     {
17241         return this.el.select('input.roo-' + this.inputType,true).first();
17242     },
17243     
17244     labelEl: function()
17245     {
17246         return this.el.select('label.control-label',true).first();
17247     },
17248     /* depricated... */
17249     
17250     label: function()
17251     {
17252         return this.labelEl();
17253     },
17254     
17255     initEvents : function()
17256     {
17257 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17258         
17259         this.inputEl().on('click', this.onClick,  this);
17260         
17261         if (this.boxLabel) { 
17262             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
17263         }
17264         
17265         this.startValue = this.getValue();
17266         
17267         if(this.groupId){
17268             Roo.bootstrap.CheckBox.register(this);
17269         }
17270     },
17271     
17272     onClick : function()
17273     {   
17274         this.setChecked(!this.checked);
17275     },
17276     
17277     setChecked : function(state,suppressEvent)
17278     {
17279         this.startValue = this.getValue();
17280         
17281         if(this.inputType == 'radio'){
17282             
17283             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17284                 e.dom.checked = false;
17285             });
17286             
17287             this.inputEl().dom.checked = true;
17288             
17289             this.inputEl().dom.value = this.inputValue;
17290             
17291             if(suppressEvent !== true){
17292                 this.fireEvent('check', this, true);
17293             }
17294             
17295             this.validate();
17296             
17297             return;
17298         }
17299         
17300         this.checked = state;
17301         
17302         this.inputEl().dom.checked = state;
17303         
17304         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17305         
17306         if(suppressEvent !== true){
17307             this.fireEvent('check', this, state);
17308         }
17309         
17310         this.validate();
17311     },
17312     
17313     getValue : function()
17314     {
17315         if(this.inputType == 'radio'){
17316             return this.getGroupValue();
17317         }
17318         
17319         return this.inputEl().getValue();
17320         
17321     },
17322     
17323     getGroupValue : function()
17324     {
17325         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
17326             return '';
17327         }
17328         
17329         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
17330     },
17331     
17332     setValue : function(v,suppressEvent)
17333     {
17334         if(this.inputType == 'radio'){
17335             this.setGroupValue(v, suppressEvent);
17336             return;
17337         }
17338         
17339         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
17340         
17341         this.validate();
17342     },
17343     
17344     setGroupValue : function(v, suppressEvent)
17345     {
17346         this.startValue = this.getValue();
17347         
17348         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17349             e.dom.checked = false;
17350             
17351             if(e.dom.value == v){
17352                 e.dom.checked = true;
17353             }
17354         });
17355         
17356         if(suppressEvent !== true){
17357             this.fireEvent('check', this, true);
17358         }
17359
17360         this.validate();
17361         
17362         return;
17363     },
17364     
17365     validate : function()
17366     {
17367         if(
17368                 this.disabled || 
17369                 (this.inputType == 'radio' && this.validateRadio()) ||
17370                 (this.inputType == 'checkbox' && this.validateCheckbox())
17371         ){
17372             this.markValid();
17373             return true;
17374         }
17375         
17376         this.markInvalid();
17377         return false;
17378     },
17379     
17380     validateRadio : function()
17381     {
17382         var valid = false;
17383         
17384         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17385             if(!e.dom.checked){
17386                 return;
17387             }
17388             
17389             valid = true;
17390             
17391             return false;
17392         });
17393         
17394         return valid;
17395     },
17396     
17397     validateCheckbox : function()
17398     {
17399         if(!this.groupId){
17400             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
17401         }
17402         
17403         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17404         
17405         if(!group){
17406             return false;
17407         }
17408         
17409         var r = false;
17410         
17411         for(var i in group){
17412             if(r){
17413                 break;
17414             }
17415             
17416             r = (group[i].getValue() == group[i].inputValue) ? true : false;
17417         }
17418         
17419         return r;
17420     },
17421     
17422     /**
17423      * Mark this field as valid
17424      */
17425     markValid : function()
17426     {
17427         if(this.allowBlank){
17428             return;
17429         }
17430         
17431         var _this = this;
17432         
17433         this.fireEvent('valid', this);
17434         
17435         if(this.inputType == 'radio'){
17436             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17437                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17438                 e.findParent('.form-group', false, true).addClass(_this.validClass);
17439             });
17440             
17441             return;
17442         }
17443         
17444         if(!this.groupId){
17445             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17446             this.el.findParent('.form-group', false, true).addClass(this.validClass);
17447             return;
17448         }
17449         
17450         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17451             
17452         if(!group){
17453             return;
17454         }
17455         
17456         for(var i in group){
17457             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17458             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
17459         }
17460     },
17461     
17462      /**
17463      * Mark this field as invalid
17464      * @param {String} msg The validation message
17465      */
17466     markInvalid : function(msg)
17467     {
17468         if(this.allowBlank){
17469             return;
17470         }
17471         
17472         var _this = this;
17473         
17474         this.fireEvent('invalid', this, msg);
17475         
17476         if(this.inputType == 'radio'){
17477             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17478                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17479                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
17480             });
17481             
17482             return;
17483         }
17484         
17485         if(!this.groupId){
17486             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17487             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
17488             return;
17489         }
17490         
17491         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17492             
17493         if(!group){
17494             return;
17495         }
17496         
17497         for(var i in group){
17498             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17499             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
17500         }
17501         
17502     }
17503     
17504 });
17505
17506 Roo.apply(Roo.bootstrap.CheckBox, {
17507     
17508     groups: {},
17509     
17510      /**
17511     * register a CheckBox Group
17512     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
17513     */
17514     register : function(checkbox)
17515     {
17516         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
17517             this.groups[checkbox.groupId] = {};
17518         }
17519         
17520         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
17521             return;
17522         }
17523         
17524         this.groups[checkbox.groupId][checkbox.name] = checkbox;
17525         
17526     },
17527     /**
17528     * fetch a CheckBox Group based on the group ID
17529     * @param {string} the group ID
17530     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
17531     */
17532     get: function(groupId) {
17533         if (typeof(this.groups[groupId]) == 'undefined') {
17534             return false;
17535         }
17536         
17537         return this.groups[groupId] ;
17538     }
17539     
17540     
17541 });
17542 /*
17543  * - LGPL
17544  *
17545  * Radio
17546  *
17547  *
17548  * not inline
17549  *<div class="radio">
17550   <label>
17551     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
17552     Option one is this and that&mdash;be sure to include why it's great
17553   </label>
17554 </div>
17555  *
17556  *
17557  *inline
17558  *<span>
17559  *<label class="radio-inline">fieldLabel</label>
17560  *<label class="radio-inline">
17561   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
17562 </label>
17563 <span>
17564  * 
17565  * 
17566  */
17567
17568 /**
17569  * @class Roo.bootstrap.Radio
17570  * @extends Roo.bootstrap.CheckBox
17571  * Bootstrap Radio class
17572
17573  * @constructor
17574  * Create a new Radio
17575  * @param {Object} config The config object
17576  */
17577
17578 Roo.bootstrap.Radio = function(config){
17579     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
17580    
17581 };
17582
17583 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
17584     
17585     inputType: 'radio',
17586     inputValue: '',
17587     valueOff: '',
17588     
17589     getAutoCreate : function()
17590     {
17591         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17592         align = align || 'left'; // default...
17593         
17594         
17595         
17596         var id = Roo.id();
17597         
17598         var cfg = {
17599                 tag : this.inline ? 'span' : 'div',
17600                 cls : '',
17601                 cn : []
17602         };
17603         
17604         var inline = this.inline ? ' radio-inline' : '';
17605         
17606         var lbl = {
17607                 tag: 'label' ,
17608                 // does not need for, as we wrap the input with it..
17609                 'for' : id,
17610                 cls : 'control-label box-label' + inline,
17611                 cn : []
17612         };
17613         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
17614         
17615         var fieldLabel = {
17616             tag: 'label' ,
17617             //cls : 'control-label' + inline,
17618             html : this.fieldLabel,
17619             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
17620         };
17621         
17622  
17623         
17624         
17625         var input =  {
17626             tag: 'input',
17627             id : id,
17628             type : this.inputType,
17629             //value : (!this.checked) ? this.valueOff : this.inputValue,
17630             value : this.inputValue,
17631             cls : 'roo-radio',
17632             placeholder : this.placeholder || '' // ?? needed????
17633             
17634         };
17635         if (this.weight) { // Validity check?
17636             input.cls += " radio-" + this.weight;
17637         }
17638         if (this.disabled) {
17639             input.disabled=true;
17640         }
17641         
17642         if(this.checked){
17643             input.checked = this.checked;
17644         }
17645         
17646         if (this.name) {
17647             input.name = this.name;
17648         }
17649         
17650         if (this.size) {
17651             input.cls += ' input-' + this.size;
17652         }
17653         
17654         //?? can span's inline have a width??
17655         
17656         var settings=this;
17657         ['xs','sm','md','lg'].map(function(size){
17658             if (settings[size]) {
17659                 cfg.cls += ' col-' + size + '-' + settings[size];
17660             }
17661         });
17662         
17663         var inputblock = input;
17664         
17665         if (this.before || this.after) {
17666             
17667             inputblock = {
17668                 cls : 'input-group',
17669                 tag : 'span',
17670                 cn :  [] 
17671             };
17672             if (this.before) {
17673                 inputblock.cn.push({
17674                     tag :'span',
17675                     cls : 'input-group-addon',
17676                     html : this.before
17677                 });
17678             }
17679             inputblock.cn.push(input);
17680             if (this.after) {
17681                 inputblock.cn.push({
17682                     tag :'span',
17683                     cls : 'input-group-addon',
17684                     html : this.after
17685                 });
17686             }
17687             
17688         };
17689         
17690         
17691         if (this.fieldLabel && this.fieldLabel.length) {
17692             cfg.cn.push(fieldLabel);
17693         }
17694        
17695         // normal bootstrap puts the input inside the label.
17696         // however with our styled version - it has to go after the input.
17697        
17698         //lbl.cn.push(inputblock);
17699         
17700         var lblwrap =  {
17701             tag: 'span',
17702             cls: 'radio' + inline,
17703             cn: [
17704                 inputblock,
17705                 lbl
17706             ]
17707         };
17708         
17709         cfg.cn.push( lblwrap);
17710         
17711         if(this.boxLabel){
17712             lbl.cn.push({
17713                 tag: 'span',
17714                 html: this.boxLabel
17715             })
17716         }
17717          
17718         
17719         return cfg;
17720         
17721     },
17722     
17723     initEvents : function()
17724     {
17725 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17726         
17727         this.inputEl().on('click', this.onClick,  this);
17728         if (this.boxLabel) {
17729             Roo.log('find label')
17730             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
17731         }
17732         
17733     },
17734     
17735     inputEl: function ()
17736     {
17737         return this.el.select('input.roo-radio',true).first();
17738     },
17739     onClick : function()
17740     {   
17741         Roo.log("click");
17742         this.setChecked(true);
17743     },
17744     
17745     setChecked : function(state,suppressEvent)
17746     {
17747         if(state){
17748             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17749                 v.dom.checked = false;
17750             });
17751         }
17752         Roo.log(this.inputEl().dom);
17753         this.checked = state;
17754         this.inputEl().dom.checked = state;
17755         
17756         if(suppressEvent !== true){
17757             this.fireEvent('check', this, state);
17758         }
17759         
17760         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17761         
17762     },
17763     
17764     getGroupValue : function()
17765     {
17766         var value = '';
17767         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17768             if(v.dom.checked == true){
17769                 value = v.dom.value;
17770             }
17771         });
17772         
17773         return value;
17774     },
17775     
17776     /**
17777      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
17778      * @return {Mixed} value The field value
17779      */
17780     getValue : function(){
17781         return this.getGroupValue();
17782     }
17783     
17784 });
17785
17786  
17787 //<script type="text/javascript">
17788
17789 /*
17790  * Based  Ext JS Library 1.1.1
17791  * Copyright(c) 2006-2007, Ext JS, LLC.
17792  * LGPL
17793  *
17794  */
17795  
17796 /**
17797  * @class Roo.HtmlEditorCore
17798  * @extends Roo.Component
17799  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
17800  *
17801  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
17802  */
17803
17804 Roo.HtmlEditorCore = function(config){
17805     
17806     
17807     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
17808     
17809     
17810     this.addEvents({
17811         /**
17812          * @event initialize
17813          * Fires when the editor is fully initialized (including the iframe)
17814          * @param {Roo.HtmlEditorCore} this
17815          */
17816         initialize: true,
17817         /**
17818          * @event activate
17819          * Fires when the editor is first receives the focus. Any insertion must wait
17820          * until after this event.
17821          * @param {Roo.HtmlEditorCore} this
17822          */
17823         activate: true,
17824          /**
17825          * @event beforesync
17826          * Fires before the textarea is updated with content from the editor iframe. Return false
17827          * to cancel the sync.
17828          * @param {Roo.HtmlEditorCore} this
17829          * @param {String} html
17830          */
17831         beforesync: true,
17832          /**
17833          * @event beforepush
17834          * Fires before the iframe editor is updated with content from the textarea. Return false
17835          * to cancel the push.
17836          * @param {Roo.HtmlEditorCore} this
17837          * @param {String} html
17838          */
17839         beforepush: true,
17840          /**
17841          * @event sync
17842          * Fires when the textarea is updated with content from the editor iframe.
17843          * @param {Roo.HtmlEditorCore} this
17844          * @param {String} html
17845          */
17846         sync: true,
17847          /**
17848          * @event push
17849          * Fires when the iframe editor is updated with content from the textarea.
17850          * @param {Roo.HtmlEditorCore} this
17851          * @param {String} html
17852          */
17853         push: true,
17854         
17855         /**
17856          * @event editorevent
17857          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17858          * @param {Roo.HtmlEditorCore} this
17859          */
17860         editorevent: true
17861         
17862     });
17863     
17864     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
17865     
17866     // defaults : white / black...
17867     this.applyBlacklists();
17868     
17869     
17870     
17871 };
17872
17873
17874 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
17875
17876
17877      /**
17878      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
17879      */
17880     
17881     owner : false,
17882     
17883      /**
17884      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17885      *                        Roo.resizable.
17886      */
17887     resizable : false,
17888      /**
17889      * @cfg {Number} height (in pixels)
17890      */   
17891     height: 300,
17892    /**
17893      * @cfg {Number} width (in pixels)
17894      */   
17895     width: 500,
17896     
17897     /**
17898      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17899      * 
17900      */
17901     stylesheets: false,
17902     
17903     // id of frame..
17904     frameId: false,
17905     
17906     // private properties
17907     validationEvent : false,
17908     deferHeight: true,
17909     initialized : false,
17910     activated : false,
17911     sourceEditMode : false,
17912     onFocus : Roo.emptyFn,
17913     iframePad:3,
17914     hideMode:'offsets',
17915     
17916     clearUp: true,
17917     
17918     // blacklist + whitelisted elements..
17919     black: false,
17920     white: false,
17921      
17922     
17923
17924     /**
17925      * Protected method that will not generally be called directly. It
17926      * is called when the editor initializes the iframe with HTML contents. Override this method if you
17927      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
17928      */
17929     getDocMarkup : function(){
17930         // body styles..
17931         var st = '';
17932         
17933         // inherit styels from page...?? 
17934         if (this.stylesheets === false) {
17935             
17936             Roo.get(document.head).select('style').each(function(node) {
17937                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17938             });
17939             
17940             Roo.get(document.head).select('link').each(function(node) { 
17941                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17942             });
17943             
17944         } else if (!this.stylesheets.length) {
17945                 // simple..
17946                 st = '<style type="text/css">' +
17947                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17948                    '</style>';
17949         } else { 
17950             
17951         }
17952         
17953         st +=  '<style type="text/css">' +
17954             'IMG { cursor: pointer } ' +
17955         '</style>';
17956
17957         
17958         return '<html><head>' + st  +
17959             //<style type="text/css">' +
17960             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17961             //'</style>' +
17962             ' </head><body class="roo-htmleditor-body"></body></html>';
17963     },
17964
17965     // private
17966     onRender : function(ct, position)
17967     {
17968         var _t = this;
17969         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
17970         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
17971         
17972         
17973         this.el.dom.style.border = '0 none';
17974         this.el.dom.setAttribute('tabIndex', -1);
17975         this.el.addClass('x-hidden hide');
17976         
17977         
17978         
17979         if(Roo.isIE){ // fix IE 1px bogus margin
17980             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
17981         }
17982        
17983         
17984         this.frameId = Roo.id();
17985         
17986          
17987         
17988         var iframe = this.owner.wrap.createChild({
17989             tag: 'iframe',
17990             cls: 'form-control', // bootstrap..
17991             id: this.frameId,
17992             name: this.frameId,
17993             frameBorder : 'no',
17994             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
17995         }, this.el
17996         );
17997         
17998         
17999         this.iframe = iframe.dom;
18000
18001          this.assignDocWin();
18002         
18003         this.doc.designMode = 'on';
18004        
18005         this.doc.open();
18006         this.doc.write(this.getDocMarkup());
18007         this.doc.close();
18008
18009         
18010         var task = { // must defer to wait for browser to be ready
18011             run : function(){
18012                 //console.log("run task?" + this.doc.readyState);
18013                 this.assignDocWin();
18014                 if(this.doc.body || this.doc.readyState == 'complete'){
18015                     try {
18016                         this.doc.designMode="on";
18017                     } catch (e) {
18018                         return;
18019                     }
18020                     Roo.TaskMgr.stop(task);
18021                     this.initEditor.defer(10, this);
18022                 }
18023             },
18024             interval : 10,
18025             duration: 10000,
18026             scope: this
18027         };
18028         Roo.TaskMgr.start(task);
18029
18030     },
18031
18032     // private
18033     onResize : function(w, h)
18034     {
18035          Roo.log('resize: ' +w + ',' + h );
18036         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18037         if(!this.iframe){
18038             return;
18039         }
18040         if(typeof w == 'number'){
18041             
18042             this.iframe.style.width = w + 'px';
18043         }
18044         if(typeof h == 'number'){
18045             
18046             this.iframe.style.height = h + 'px';
18047             if(this.doc){
18048                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18049             }
18050         }
18051         
18052     },
18053
18054     /**
18055      * Toggles the editor between standard and source edit mode.
18056      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18057      */
18058     toggleSourceEdit : function(sourceEditMode){
18059         
18060         this.sourceEditMode = sourceEditMode === true;
18061         
18062         if(this.sourceEditMode){
18063  
18064             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
18065             
18066         }else{
18067             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
18068             //this.iframe.className = '';
18069             this.deferFocus();
18070         }
18071         //this.setSize(this.owner.wrap.getSize());
18072         //this.fireEvent('editmodechange', this, this.sourceEditMode);
18073     },
18074
18075     
18076   
18077
18078     /**
18079      * Protected method that will not generally be called directly. If you need/want
18080      * custom HTML cleanup, this is the method you should override.
18081      * @param {String} html The HTML to be cleaned
18082      * return {String} The cleaned HTML
18083      */
18084     cleanHtml : function(html){
18085         html = String(html);
18086         if(html.length > 5){
18087             if(Roo.isSafari){ // strip safari nonsense
18088                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
18089             }
18090         }
18091         if(html == '&nbsp;'){
18092             html = '';
18093         }
18094         return html;
18095     },
18096
18097     /**
18098      * HTML Editor -> Textarea
18099      * Protected method that will not generally be called directly. Syncs the contents
18100      * of the editor iframe with the textarea.
18101      */
18102     syncValue : function(){
18103         if(this.initialized){
18104             var bd = (this.doc.body || this.doc.documentElement);
18105             //this.cleanUpPaste(); -- this is done else where and causes havoc..
18106             var html = bd.innerHTML;
18107             if(Roo.isSafari){
18108                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
18109                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
18110                 if(m && m[1]){
18111                     html = '<div style="'+m[0]+'">' + html + '</div>';
18112                 }
18113             }
18114             html = this.cleanHtml(html);
18115             // fix up the special chars.. normaly like back quotes in word...
18116             // however we do not want to do this with chinese..
18117             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
18118                 var cc = b.charCodeAt();
18119                 if (
18120                     (cc >= 0x4E00 && cc < 0xA000 ) ||
18121                     (cc >= 0x3400 && cc < 0x4E00 ) ||
18122                     (cc >= 0xf900 && cc < 0xfb00 )
18123                 ) {
18124                         return b;
18125                 }
18126                 return "&#"+cc+";" 
18127             });
18128             if(this.owner.fireEvent('beforesync', this, html) !== false){
18129                 this.el.dom.value = html;
18130                 this.owner.fireEvent('sync', this, html);
18131             }
18132         }
18133     },
18134
18135     /**
18136      * Protected method that will not generally be called directly. Pushes the value of the textarea
18137      * into the iframe editor.
18138      */
18139     pushValue : function(){
18140         if(this.initialized){
18141             var v = this.el.dom.value.trim();
18142             
18143 //            if(v.length < 1){
18144 //                v = '&#160;';
18145 //            }
18146             
18147             if(this.owner.fireEvent('beforepush', this, v) !== false){
18148                 var d = (this.doc.body || this.doc.documentElement);
18149                 d.innerHTML = v;
18150                 this.cleanUpPaste();
18151                 this.el.dom.value = d.innerHTML;
18152                 this.owner.fireEvent('push', this, v);
18153             }
18154         }
18155     },
18156
18157     // private
18158     deferFocus : function(){
18159         this.focus.defer(10, this);
18160     },
18161
18162     // doc'ed in Field
18163     focus : function(){
18164         if(this.win && !this.sourceEditMode){
18165             this.win.focus();
18166         }else{
18167             this.el.focus();
18168         }
18169     },
18170     
18171     assignDocWin: function()
18172     {
18173         var iframe = this.iframe;
18174         
18175          if(Roo.isIE){
18176             this.doc = iframe.contentWindow.document;
18177             this.win = iframe.contentWindow;
18178         } else {
18179 //            if (!Roo.get(this.frameId)) {
18180 //                return;
18181 //            }
18182 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18183 //            this.win = Roo.get(this.frameId).dom.contentWindow;
18184             
18185             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
18186                 return;
18187             }
18188             
18189             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18190             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
18191         }
18192     },
18193     
18194     // private
18195     initEditor : function(){
18196         //console.log("INIT EDITOR");
18197         this.assignDocWin();
18198         
18199         
18200         
18201         this.doc.designMode="on";
18202         this.doc.open();
18203         this.doc.write(this.getDocMarkup());
18204         this.doc.close();
18205         
18206         var dbody = (this.doc.body || this.doc.documentElement);
18207         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
18208         // this copies styles from the containing element into thsi one..
18209         // not sure why we need all of this..
18210         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
18211         
18212         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
18213         //ss['background-attachment'] = 'fixed'; // w3c
18214         dbody.bgProperties = 'fixed'; // ie
18215         //Roo.DomHelper.applyStyles(dbody, ss);
18216         Roo.EventManager.on(this.doc, {
18217             //'mousedown': this.onEditorEvent,
18218             'mouseup': this.onEditorEvent,
18219             'dblclick': this.onEditorEvent,
18220             'click': this.onEditorEvent,
18221             'keyup': this.onEditorEvent,
18222             buffer:100,
18223             scope: this
18224         });
18225         if(Roo.isGecko){
18226             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
18227         }
18228         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
18229             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
18230         }
18231         this.initialized = true;
18232
18233         this.owner.fireEvent('initialize', this);
18234         this.pushValue();
18235     },
18236
18237     // private
18238     onDestroy : function(){
18239         
18240         
18241         
18242         if(this.rendered){
18243             
18244             //for (var i =0; i < this.toolbars.length;i++) {
18245             //    // fixme - ask toolbars for heights?
18246             //    this.toolbars[i].onDestroy();
18247            // }
18248             
18249             //this.wrap.dom.innerHTML = '';
18250             //this.wrap.remove();
18251         }
18252     },
18253
18254     // private
18255     onFirstFocus : function(){
18256         
18257         this.assignDocWin();
18258         
18259         
18260         this.activated = true;
18261          
18262     
18263         if(Roo.isGecko){ // prevent silly gecko errors
18264             this.win.focus();
18265             var s = this.win.getSelection();
18266             if(!s.focusNode || s.focusNode.nodeType != 3){
18267                 var r = s.getRangeAt(0);
18268                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
18269                 r.collapse(true);
18270                 this.deferFocus();
18271             }
18272             try{
18273                 this.execCmd('useCSS', true);
18274                 this.execCmd('styleWithCSS', false);
18275             }catch(e){}
18276         }
18277         this.owner.fireEvent('activate', this);
18278     },
18279
18280     // private
18281     adjustFont: function(btn){
18282         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
18283         //if(Roo.isSafari){ // safari
18284         //    adjust *= 2;
18285        // }
18286         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
18287         if(Roo.isSafari){ // safari
18288             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
18289             v =  (v < 10) ? 10 : v;
18290             v =  (v > 48) ? 48 : v;
18291             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
18292             
18293         }
18294         
18295         
18296         v = Math.max(1, v+adjust);
18297         
18298         this.execCmd('FontSize', v  );
18299     },
18300
18301     onEditorEvent : function(e){
18302         this.owner.fireEvent('editorevent', this, e);
18303       //  this.updateToolbar();
18304         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
18305     },
18306
18307     insertTag : function(tg)
18308     {
18309         // could be a bit smarter... -> wrap the current selected tRoo..
18310         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
18311             
18312             range = this.createRange(this.getSelection());
18313             var wrappingNode = this.doc.createElement(tg.toLowerCase());
18314             wrappingNode.appendChild(range.extractContents());
18315             range.insertNode(wrappingNode);
18316
18317             return;
18318             
18319             
18320             
18321         }
18322         this.execCmd("formatblock",   tg);
18323         
18324     },
18325     
18326     insertText : function(txt)
18327     {
18328         
18329         
18330         var range = this.createRange();
18331         range.deleteContents();
18332                //alert(Sender.getAttribute('label'));
18333                
18334         range.insertNode(this.doc.createTextNode(txt));
18335     } ,
18336     
18337      
18338
18339     /**
18340      * Executes a Midas editor command on the editor document and performs necessary focus and
18341      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
18342      * @param {String} cmd The Midas command
18343      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18344      */
18345     relayCmd : function(cmd, value){
18346         this.win.focus();
18347         this.execCmd(cmd, value);
18348         this.owner.fireEvent('editorevent', this);
18349         //this.updateToolbar();
18350         this.owner.deferFocus();
18351     },
18352
18353     /**
18354      * Executes a Midas editor command directly on the editor document.
18355      * For visual commands, you should use {@link #relayCmd} instead.
18356      * <b>This should only be called after the editor is initialized.</b>
18357      * @param {String} cmd The Midas command
18358      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18359      */
18360     execCmd : function(cmd, value){
18361         this.doc.execCommand(cmd, false, value === undefined ? null : value);
18362         this.syncValue();
18363     },
18364  
18365  
18366    
18367     /**
18368      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
18369      * to insert tRoo.
18370      * @param {String} text | dom node.. 
18371      */
18372     insertAtCursor : function(text)
18373     {
18374         
18375         
18376         
18377         if(!this.activated){
18378             return;
18379         }
18380         /*
18381         if(Roo.isIE){
18382             this.win.focus();
18383             var r = this.doc.selection.createRange();
18384             if(r){
18385                 r.collapse(true);
18386                 r.pasteHTML(text);
18387                 this.syncValue();
18388                 this.deferFocus();
18389             
18390             }
18391             return;
18392         }
18393         */
18394         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
18395             this.win.focus();
18396             
18397             
18398             // from jquery ui (MIT licenced)
18399             var range, node;
18400             var win = this.win;
18401             
18402             if (win.getSelection && win.getSelection().getRangeAt) {
18403                 range = win.getSelection().getRangeAt(0);
18404                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
18405                 range.insertNode(node);
18406             } else if (win.document.selection && win.document.selection.createRange) {
18407                 // no firefox support
18408                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18409                 win.document.selection.createRange().pasteHTML(txt);
18410             } else {
18411                 // no firefox support
18412                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18413                 this.execCmd('InsertHTML', txt);
18414             } 
18415             
18416             this.syncValue();
18417             
18418             this.deferFocus();
18419         }
18420     },
18421  // private
18422     mozKeyPress : function(e){
18423         if(e.ctrlKey){
18424             var c = e.getCharCode(), cmd;
18425           
18426             if(c > 0){
18427                 c = String.fromCharCode(c).toLowerCase();
18428                 switch(c){
18429                     case 'b':
18430                         cmd = 'bold';
18431                         break;
18432                     case 'i':
18433                         cmd = 'italic';
18434                         break;
18435                     
18436                     case 'u':
18437                         cmd = 'underline';
18438                         break;
18439                     
18440                     case 'v':
18441                         this.cleanUpPaste.defer(100, this);
18442                         return;
18443                         
18444                 }
18445                 if(cmd){
18446                     this.win.focus();
18447                     this.execCmd(cmd);
18448                     this.deferFocus();
18449                     e.preventDefault();
18450                 }
18451                 
18452             }
18453         }
18454     },
18455
18456     // private
18457     fixKeys : function(){ // load time branching for fastest keydown performance
18458         if(Roo.isIE){
18459             return function(e){
18460                 var k = e.getKey(), r;
18461                 if(k == e.TAB){
18462                     e.stopEvent();
18463                     r = this.doc.selection.createRange();
18464                     if(r){
18465                         r.collapse(true);
18466                         r.pasteHTML('&#160;&#160;&#160;&#160;');
18467                         this.deferFocus();
18468                     }
18469                     return;
18470                 }
18471                 
18472                 if(k == e.ENTER){
18473                     r = this.doc.selection.createRange();
18474                     if(r){
18475                         var target = r.parentElement();
18476                         if(!target || target.tagName.toLowerCase() != 'li'){
18477                             e.stopEvent();
18478                             r.pasteHTML('<br />');
18479                             r.collapse(false);
18480                             r.select();
18481                         }
18482                     }
18483                 }
18484                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18485                     this.cleanUpPaste.defer(100, this);
18486                     return;
18487                 }
18488                 
18489                 
18490             };
18491         }else if(Roo.isOpera){
18492             return function(e){
18493                 var k = e.getKey();
18494                 if(k == e.TAB){
18495                     e.stopEvent();
18496                     this.win.focus();
18497                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
18498                     this.deferFocus();
18499                 }
18500                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18501                     this.cleanUpPaste.defer(100, this);
18502                     return;
18503                 }
18504                 
18505             };
18506         }else if(Roo.isSafari){
18507             return function(e){
18508                 var k = e.getKey();
18509                 
18510                 if(k == e.TAB){
18511                     e.stopEvent();
18512                     this.execCmd('InsertText','\t');
18513                     this.deferFocus();
18514                     return;
18515                 }
18516                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18517                     this.cleanUpPaste.defer(100, this);
18518                     return;
18519                 }
18520                 
18521              };
18522         }
18523     }(),
18524     
18525     getAllAncestors: function()
18526     {
18527         var p = this.getSelectedNode();
18528         var a = [];
18529         if (!p) {
18530             a.push(p); // push blank onto stack..
18531             p = this.getParentElement();
18532         }
18533         
18534         
18535         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
18536             a.push(p);
18537             p = p.parentNode;
18538         }
18539         a.push(this.doc.body);
18540         return a;
18541     },
18542     lastSel : false,
18543     lastSelNode : false,
18544     
18545     
18546     getSelection : function() 
18547     {
18548         this.assignDocWin();
18549         return Roo.isIE ? this.doc.selection : this.win.getSelection();
18550     },
18551     
18552     getSelectedNode: function() 
18553     {
18554         // this may only work on Gecko!!!
18555         
18556         // should we cache this!!!!
18557         
18558         
18559         
18560          
18561         var range = this.createRange(this.getSelection()).cloneRange();
18562         
18563         if (Roo.isIE) {
18564             var parent = range.parentElement();
18565             while (true) {
18566                 var testRange = range.duplicate();
18567                 testRange.moveToElementText(parent);
18568                 if (testRange.inRange(range)) {
18569                     break;
18570                 }
18571                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
18572                     break;
18573                 }
18574                 parent = parent.parentElement;
18575             }
18576             return parent;
18577         }
18578         
18579         // is ancestor a text element.
18580         var ac =  range.commonAncestorContainer;
18581         if (ac.nodeType == 3) {
18582             ac = ac.parentNode;
18583         }
18584         
18585         var ar = ac.childNodes;
18586          
18587         var nodes = [];
18588         var other_nodes = [];
18589         var has_other_nodes = false;
18590         for (var i=0;i<ar.length;i++) {
18591             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
18592                 continue;
18593             }
18594             // fullly contained node.
18595             
18596             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
18597                 nodes.push(ar[i]);
18598                 continue;
18599             }
18600             
18601             // probably selected..
18602             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
18603                 other_nodes.push(ar[i]);
18604                 continue;
18605             }
18606             // outer..
18607             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
18608                 continue;
18609             }
18610             
18611             
18612             has_other_nodes = true;
18613         }
18614         if (!nodes.length && other_nodes.length) {
18615             nodes= other_nodes;
18616         }
18617         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
18618             return false;
18619         }
18620         
18621         return nodes[0];
18622     },
18623     createRange: function(sel)
18624     {
18625         // this has strange effects when using with 
18626         // top toolbar - not sure if it's a great idea.
18627         //this.editor.contentWindow.focus();
18628         if (typeof sel != "undefined") {
18629             try {
18630                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
18631             } catch(e) {
18632                 return this.doc.createRange();
18633             }
18634         } else {
18635             return this.doc.createRange();
18636         }
18637     },
18638     getParentElement: function()
18639     {
18640         
18641         this.assignDocWin();
18642         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
18643         
18644         var range = this.createRange(sel);
18645          
18646         try {
18647             var p = range.commonAncestorContainer;
18648             while (p.nodeType == 3) { // text node
18649                 p = p.parentNode;
18650             }
18651             return p;
18652         } catch (e) {
18653             return null;
18654         }
18655     
18656     },
18657     /***
18658      *
18659      * Range intersection.. the hard stuff...
18660      *  '-1' = before
18661      *  '0' = hits..
18662      *  '1' = after.
18663      *         [ -- selected range --- ]
18664      *   [fail]                        [fail]
18665      *
18666      *    basically..
18667      *      if end is before start or  hits it. fail.
18668      *      if start is after end or hits it fail.
18669      *
18670      *   if either hits (but other is outside. - then it's not 
18671      *   
18672      *    
18673      **/
18674     
18675     
18676     // @see http://www.thismuchiknow.co.uk/?p=64.
18677     rangeIntersectsNode : function(range, node)
18678     {
18679         var nodeRange = node.ownerDocument.createRange();
18680         try {
18681             nodeRange.selectNode(node);
18682         } catch (e) {
18683             nodeRange.selectNodeContents(node);
18684         }
18685     
18686         var rangeStartRange = range.cloneRange();
18687         rangeStartRange.collapse(true);
18688     
18689         var rangeEndRange = range.cloneRange();
18690         rangeEndRange.collapse(false);
18691     
18692         var nodeStartRange = nodeRange.cloneRange();
18693         nodeStartRange.collapse(true);
18694     
18695         var nodeEndRange = nodeRange.cloneRange();
18696         nodeEndRange.collapse(false);
18697     
18698         return rangeStartRange.compareBoundaryPoints(
18699                  Range.START_TO_START, nodeEndRange) == -1 &&
18700                rangeEndRange.compareBoundaryPoints(
18701                  Range.START_TO_START, nodeStartRange) == 1;
18702         
18703          
18704     },
18705     rangeCompareNode : function(range, node)
18706     {
18707         var nodeRange = node.ownerDocument.createRange();
18708         try {
18709             nodeRange.selectNode(node);
18710         } catch (e) {
18711             nodeRange.selectNodeContents(node);
18712         }
18713         
18714         
18715         range.collapse(true);
18716     
18717         nodeRange.collapse(true);
18718      
18719         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
18720         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
18721          
18722         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
18723         
18724         var nodeIsBefore   =  ss == 1;
18725         var nodeIsAfter    = ee == -1;
18726         
18727         if (nodeIsBefore && nodeIsAfter)
18728             return 0; // outer
18729         if (!nodeIsBefore && nodeIsAfter)
18730             return 1; //right trailed.
18731         
18732         if (nodeIsBefore && !nodeIsAfter)
18733             return 2;  // left trailed.
18734         // fully contined.
18735         return 3;
18736     },
18737
18738     // private? - in a new class?
18739     cleanUpPaste :  function()
18740     {
18741         // cleans up the whole document..
18742         Roo.log('cleanuppaste');
18743         
18744         this.cleanUpChildren(this.doc.body);
18745         var clean = this.cleanWordChars(this.doc.body.innerHTML);
18746         if (clean != this.doc.body.innerHTML) {
18747             this.doc.body.innerHTML = clean;
18748         }
18749         
18750     },
18751     
18752     cleanWordChars : function(input) {// change the chars to hex code
18753         var he = Roo.HtmlEditorCore;
18754         
18755         var output = input;
18756         Roo.each(he.swapCodes, function(sw) { 
18757             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
18758             
18759             output = output.replace(swapper, sw[1]);
18760         });
18761         
18762         return output;
18763     },
18764     
18765     
18766     cleanUpChildren : function (n)
18767     {
18768         if (!n.childNodes.length) {
18769             return;
18770         }
18771         for (var i = n.childNodes.length-1; i > -1 ; i--) {
18772            this.cleanUpChild(n.childNodes[i]);
18773         }
18774     },
18775     
18776     
18777         
18778     
18779     cleanUpChild : function (node)
18780     {
18781         var ed = this;
18782         //console.log(node);
18783         if (node.nodeName == "#text") {
18784             // clean up silly Windows -- stuff?
18785             return; 
18786         }
18787         if (node.nodeName == "#comment") {
18788             node.parentNode.removeChild(node);
18789             // clean up silly Windows -- stuff?
18790             return; 
18791         }
18792         var lcname = node.tagName.toLowerCase();
18793         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
18794         // whitelist of tags..
18795         
18796         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
18797             // remove node.
18798             node.parentNode.removeChild(node);
18799             return;
18800             
18801         }
18802         
18803         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
18804         
18805         // remove <a name=....> as rendering on yahoo mailer is borked with this.
18806         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
18807         
18808         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
18809         //    remove_keep_children = true;
18810         //}
18811         
18812         if (remove_keep_children) {
18813             this.cleanUpChildren(node);
18814             // inserts everything just before this node...
18815             while (node.childNodes.length) {
18816                 var cn = node.childNodes[0];
18817                 node.removeChild(cn);
18818                 node.parentNode.insertBefore(cn, node);
18819             }
18820             node.parentNode.removeChild(node);
18821             return;
18822         }
18823         
18824         if (!node.attributes || !node.attributes.length) {
18825             this.cleanUpChildren(node);
18826             return;
18827         }
18828         
18829         function cleanAttr(n,v)
18830         {
18831             
18832             if (v.match(/^\./) || v.match(/^\//)) {
18833                 return;
18834             }
18835             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
18836                 return;
18837             }
18838             if (v.match(/^#/)) {
18839                 return;
18840             }
18841 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
18842             node.removeAttribute(n);
18843             
18844         }
18845         
18846         var cwhite = this.cwhite;
18847         var cblack = this.cblack;
18848             
18849         function cleanStyle(n,v)
18850         {
18851             if (v.match(/expression/)) { //XSS?? should we even bother..
18852                 node.removeAttribute(n);
18853                 return;
18854             }
18855             
18856             var parts = v.split(/;/);
18857             var clean = [];
18858             
18859             Roo.each(parts, function(p) {
18860                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
18861                 if (!p.length) {
18862                     return true;
18863                 }
18864                 var l = p.split(':').shift().replace(/\s+/g,'');
18865                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
18866                 
18867                 if ( cwhite.length && cblack.indexOf(l) > -1) {
18868 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18869                     //node.removeAttribute(n);
18870                     return true;
18871                 }
18872                 //Roo.log()
18873                 // only allow 'c whitelisted system attributes'
18874                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
18875 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18876                     //node.removeAttribute(n);
18877                     return true;
18878                 }
18879                 
18880                 
18881                  
18882                 
18883                 clean.push(p);
18884                 return true;
18885             });
18886             if (clean.length) { 
18887                 node.setAttribute(n, clean.join(';'));
18888             } else {
18889                 node.removeAttribute(n);
18890             }
18891             
18892         }
18893         
18894         
18895         for (var i = node.attributes.length-1; i > -1 ; i--) {
18896             var a = node.attributes[i];
18897             //console.log(a);
18898             
18899             if (a.name.toLowerCase().substr(0,2)=='on')  {
18900                 node.removeAttribute(a.name);
18901                 continue;
18902             }
18903             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
18904                 node.removeAttribute(a.name);
18905                 continue;
18906             }
18907             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
18908                 cleanAttr(a.name,a.value); // fixme..
18909                 continue;
18910             }
18911             if (a.name == 'style') {
18912                 cleanStyle(a.name,a.value);
18913                 continue;
18914             }
18915             /// clean up MS crap..
18916             // tecnically this should be a list of valid class'es..
18917             
18918             
18919             if (a.name == 'class') {
18920                 if (a.value.match(/^Mso/)) {
18921                     node.className = '';
18922                 }
18923                 
18924                 if (a.value.match(/body/)) {
18925                     node.className = '';
18926                 }
18927                 continue;
18928             }
18929             
18930             // style cleanup!?
18931             // class cleanup?
18932             
18933         }
18934         
18935         
18936         this.cleanUpChildren(node);
18937         
18938         
18939     },
18940     /**
18941      * Clean up MS wordisms...
18942      */
18943     cleanWord : function(node)
18944     {
18945         var _t = this;
18946         var cleanWordChildren = function()
18947         {
18948             if (!node.childNodes.length) {
18949                 return;
18950             }
18951             for (var i = node.childNodes.length-1; i > -1 ; i--) {
18952                _t.cleanWord(node.childNodes[i]);
18953             }
18954         }
18955         
18956         
18957         if (!node) {
18958             this.cleanWord(this.doc.body);
18959             return;
18960         }
18961         if (node.nodeName == "#text") {
18962             // clean up silly Windows -- stuff?
18963             return; 
18964         }
18965         if (node.nodeName == "#comment") {
18966             node.parentNode.removeChild(node);
18967             // clean up silly Windows -- stuff?
18968             return; 
18969         }
18970         
18971         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
18972             node.parentNode.removeChild(node);
18973             return;
18974         }
18975         
18976         // remove - but keep children..
18977         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
18978             while (node.childNodes.length) {
18979                 var cn = node.childNodes[0];
18980                 node.removeChild(cn);
18981                 node.parentNode.insertBefore(cn, node);
18982             }
18983             node.parentNode.removeChild(node);
18984             cleanWordChildren();
18985             return;
18986         }
18987         // clean styles
18988         if (node.className.length) {
18989             
18990             var cn = node.className.split(/\W+/);
18991             var cna = [];
18992             Roo.each(cn, function(cls) {
18993                 if (cls.match(/Mso[a-zA-Z]+/)) {
18994                     return;
18995                 }
18996                 cna.push(cls);
18997             });
18998             node.className = cna.length ? cna.join(' ') : '';
18999             if (!cna.length) {
19000                 node.removeAttribute("class");
19001             }
19002         }
19003         
19004         if (node.hasAttribute("lang")) {
19005             node.removeAttribute("lang");
19006         }
19007         
19008         if (node.hasAttribute("style")) {
19009             
19010             var styles = node.getAttribute("style").split(";");
19011             var nstyle = [];
19012             Roo.each(styles, function(s) {
19013                 if (!s.match(/:/)) {
19014                     return;
19015                 }
19016                 var kv = s.split(":");
19017                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19018                     return;
19019                 }
19020                 // what ever is left... we allow.
19021                 nstyle.push(s);
19022             });
19023             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19024             if (!nstyle.length) {
19025                 node.removeAttribute('style');
19026             }
19027         }
19028         
19029         cleanWordChildren();
19030         
19031         
19032     },
19033     domToHTML : function(currentElement, depth, nopadtext) {
19034         
19035         depth = depth || 0;
19036         nopadtext = nopadtext || false;
19037     
19038         if (!currentElement) {
19039             return this.domToHTML(this.doc.body);
19040         }
19041         
19042         //Roo.log(currentElement);
19043         var j;
19044         var allText = false;
19045         var nodeName = currentElement.nodeName;
19046         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
19047         
19048         if  (nodeName == '#text') {
19049             
19050             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
19051         }
19052         
19053         
19054         var ret = '';
19055         if (nodeName != 'BODY') {
19056              
19057             var i = 0;
19058             // Prints the node tagName, such as <A>, <IMG>, etc
19059             if (tagName) {
19060                 var attr = [];
19061                 for(i = 0; i < currentElement.attributes.length;i++) {
19062                     // quoting?
19063                     var aname = currentElement.attributes.item(i).name;
19064                     if (!currentElement.attributes.item(i).value.length) {
19065                         continue;
19066                     }
19067                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
19068                 }
19069                 
19070                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
19071             } 
19072             else {
19073                 
19074                 // eack
19075             }
19076         } else {
19077             tagName = false;
19078         }
19079         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
19080             return ret;
19081         }
19082         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
19083             nopadtext = true;
19084         }
19085         
19086         
19087         // Traverse the tree
19088         i = 0;
19089         var currentElementChild = currentElement.childNodes.item(i);
19090         var allText = true;
19091         var innerHTML  = '';
19092         lastnode = '';
19093         while (currentElementChild) {
19094             // Formatting code (indent the tree so it looks nice on the screen)
19095             var nopad = nopadtext;
19096             if (lastnode == 'SPAN') {
19097                 nopad  = true;
19098             }
19099             // text
19100             if  (currentElementChild.nodeName == '#text') {
19101                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
19102                 toadd = nopadtext ? toadd : toadd.trim();
19103                 if (!nopad && toadd.length > 80) {
19104                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
19105                 }
19106                 innerHTML  += toadd;
19107                 
19108                 i++;
19109                 currentElementChild = currentElement.childNodes.item(i);
19110                 lastNode = '';
19111                 continue;
19112             }
19113             allText = false;
19114             
19115             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
19116                 
19117             // Recursively traverse the tree structure of the child node
19118             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
19119             lastnode = currentElementChild.nodeName;
19120             i++;
19121             currentElementChild=currentElement.childNodes.item(i);
19122         }
19123         
19124         ret += innerHTML;
19125         
19126         if (!allText) {
19127                 // The remaining code is mostly for formatting the tree
19128             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
19129         }
19130         
19131         
19132         if (tagName) {
19133             ret+= "</"+tagName+">";
19134         }
19135         return ret;
19136         
19137     },
19138         
19139     applyBlacklists : function()
19140     {
19141         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
19142         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
19143         
19144         this.white = [];
19145         this.black = [];
19146         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
19147             if (b.indexOf(tag) > -1) {
19148                 return;
19149             }
19150             this.white.push(tag);
19151             
19152         }, this);
19153         
19154         Roo.each(w, function(tag) {
19155             if (b.indexOf(tag) > -1) {
19156                 return;
19157             }
19158             if (this.white.indexOf(tag) > -1) {
19159                 return;
19160             }
19161             this.white.push(tag);
19162             
19163         }, this);
19164         
19165         
19166         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
19167             if (w.indexOf(tag) > -1) {
19168                 return;
19169             }
19170             this.black.push(tag);
19171             
19172         }, this);
19173         
19174         Roo.each(b, function(tag) {
19175             if (w.indexOf(tag) > -1) {
19176                 return;
19177             }
19178             if (this.black.indexOf(tag) > -1) {
19179                 return;
19180             }
19181             this.black.push(tag);
19182             
19183         }, this);
19184         
19185         
19186         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
19187         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
19188         
19189         this.cwhite = [];
19190         this.cblack = [];
19191         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
19192             if (b.indexOf(tag) > -1) {
19193                 return;
19194             }
19195             this.cwhite.push(tag);
19196             
19197         }, this);
19198         
19199         Roo.each(w, function(tag) {
19200             if (b.indexOf(tag) > -1) {
19201                 return;
19202             }
19203             if (this.cwhite.indexOf(tag) > -1) {
19204                 return;
19205             }
19206             this.cwhite.push(tag);
19207             
19208         }, this);
19209         
19210         
19211         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
19212             if (w.indexOf(tag) > -1) {
19213                 return;
19214             }
19215             this.cblack.push(tag);
19216             
19217         }, this);
19218         
19219         Roo.each(b, function(tag) {
19220             if (w.indexOf(tag) > -1) {
19221                 return;
19222             }
19223             if (this.cblack.indexOf(tag) > -1) {
19224                 return;
19225             }
19226             this.cblack.push(tag);
19227             
19228         }, this);
19229     },
19230     
19231     setStylesheets : function(stylesheets)
19232     {
19233         if(typeof(stylesheets) == 'string'){
19234             Roo.get(this.iframe.contentDocument.head).createChild({
19235                 tag : 'link',
19236                 rel : 'stylesheet',
19237                 type : 'text/css',
19238                 href : stylesheets
19239             });
19240             
19241             return;
19242         }
19243         var _this = this;
19244      
19245         Roo.each(stylesheets, function(s) {
19246             if(!s.length){
19247                 return;
19248             }
19249             
19250             Roo.get(_this.iframe.contentDocument.head).createChild({
19251                 tag : 'link',
19252                 rel : 'stylesheet',
19253                 type : 'text/css',
19254                 href : s
19255             });
19256         });
19257
19258         
19259     },
19260     
19261     removeStylesheets : function()
19262     {
19263         var _this = this;
19264         
19265         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
19266             s.remove();
19267         });
19268     }
19269     
19270     // hide stuff that is not compatible
19271     /**
19272      * @event blur
19273      * @hide
19274      */
19275     /**
19276      * @event change
19277      * @hide
19278      */
19279     /**
19280      * @event focus
19281      * @hide
19282      */
19283     /**
19284      * @event specialkey
19285      * @hide
19286      */
19287     /**
19288      * @cfg {String} fieldClass @hide
19289      */
19290     /**
19291      * @cfg {String} focusClass @hide
19292      */
19293     /**
19294      * @cfg {String} autoCreate @hide
19295      */
19296     /**
19297      * @cfg {String} inputType @hide
19298      */
19299     /**
19300      * @cfg {String} invalidClass @hide
19301      */
19302     /**
19303      * @cfg {String} invalidText @hide
19304      */
19305     /**
19306      * @cfg {String} msgFx @hide
19307      */
19308     /**
19309      * @cfg {String} validateOnBlur @hide
19310      */
19311 });
19312
19313 Roo.HtmlEditorCore.white = [
19314         'area', 'br', 'img', 'input', 'hr', 'wbr',
19315         
19316        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
19317        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
19318        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
19319        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
19320        'table',   'ul',         'xmp', 
19321        
19322        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
19323       'thead',   'tr', 
19324      
19325       'dir', 'menu', 'ol', 'ul', 'dl',
19326        
19327       'embed',  'object'
19328 ];
19329
19330
19331 Roo.HtmlEditorCore.black = [
19332     //    'embed',  'object', // enable - backend responsiblity to clean thiese
19333         'applet', // 
19334         'base',   'basefont', 'bgsound', 'blink',  'body', 
19335         'frame',  'frameset', 'head',    'html',   'ilayer', 
19336         'iframe', 'layer',  'link',     'meta',    'object',   
19337         'script', 'style' ,'title',  'xml' // clean later..
19338 ];
19339 Roo.HtmlEditorCore.clean = [
19340     'script', 'style', 'title', 'xml'
19341 ];
19342 Roo.HtmlEditorCore.remove = [
19343     'font'
19344 ];
19345 // attributes..
19346
19347 Roo.HtmlEditorCore.ablack = [
19348     'on'
19349 ];
19350     
19351 Roo.HtmlEditorCore.aclean = [ 
19352     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
19353 ];
19354
19355 // protocols..
19356 Roo.HtmlEditorCore.pwhite= [
19357         'http',  'https',  'mailto'
19358 ];
19359
19360 // white listed style attributes.
19361 Roo.HtmlEditorCore.cwhite= [
19362       //  'text-align', /// default is to allow most things..
19363       
19364          
19365 //        'font-size'//??
19366 ];
19367
19368 // black listed style attributes.
19369 Roo.HtmlEditorCore.cblack= [
19370       //  'font-size' -- this can be set by the project 
19371 ];
19372
19373
19374 Roo.HtmlEditorCore.swapCodes   =[ 
19375     [    8211, "--" ], 
19376     [    8212, "--" ], 
19377     [    8216,  "'" ],  
19378     [    8217, "'" ],  
19379     [    8220, '"' ],  
19380     [    8221, '"' ],  
19381     [    8226, "*" ],  
19382     [    8230, "..." ]
19383 ]; 
19384
19385     /*
19386  * - LGPL
19387  *
19388  * HtmlEditor
19389  * 
19390  */
19391
19392 /**
19393  * @class Roo.bootstrap.HtmlEditor
19394  * @extends Roo.bootstrap.TextArea
19395  * Bootstrap HtmlEditor class
19396
19397  * @constructor
19398  * Create a new HtmlEditor
19399  * @param {Object} config The config object
19400  */
19401
19402 Roo.bootstrap.HtmlEditor = function(config){
19403     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
19404     if (!this.toolbars) {
19405         this.toolbars = [];
19406     }
19407     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
19408     this.addEvents({
19409             /**
19410              * @event initialize
19411              * Fires when the editor is fully initialized (including the iframe)
19412              * @param {HtmlEditor} this
19413              */
19414             initialize: true,
19415             /**
19416              * @event activate
19417              * Fires when the editor is first receives the focus. Any insertion must wait
19418              * until after this event.
19419              * @param {HtmlEditor} this
19420              */
19421             activate: true,
19422              /**
19423              * @event beforesync
19424              * Fires before the textarea is updated with content from the editor iframe. Return false
19425              * to cancel the sync.
19426              * @param {HtmlEditor} this
19427              * @param {String} html
19428              */
19429             beforesync: true,
19430              /**
19431              * @event beforepush
19432              * Fires before the iframe editor is updated with content from the textarea. Return false
19433              * to cancel the push.
19434              * @param {HtmlEditor} this
19435              * @param {String} html
19436              */
19437             beforepush: true,
19438              /**
19439              * @event sync
19440              * Fires when the textarea is updated with content from the editor iframe.
19441              * @param {HtmlEditor} this
19442              * @param {String} html
19443              */
19444             sync: true,
19445              /**
19446              * @event push
19447              * Fires when the iframe editor is updated with content from the textarea.
19448              * @param {HtmlEditor} this
19449              * @param {String} html
19450              */
19451             push: true,
19452              /**
19453              * @event editmodechange
19454              * Fires when the editor switches edit modes
19455              * @param {HtmlEditor} this
19456              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
19457              */
19458             editmodechange: true,
19459             /**
19460              * @event editorevent
19461              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19462              * @param {HtmlEditor} this
19463              */
19464             editorevent: true,
19465             /**
19466              * @event firstfocus
19467              * Fires when on first focus - needed by toolbars..
19468              * @param {HtmlEditor} this
19469              */
19470             firstfocus: true,
19471             /**
19472              * @event autosave
19473              * Auto save the htmlEditor value as a file into Events
19474              * @param {HtmlEditor} this
19475              */
19476             autosave: true,
19477             /**
19478              * @event savedpreview
19479              * preview the saved version of htmlEditor
19480              * @param {HtmlEditor} this
19481              */
19482             savedpreview: true
19483         });
19484 };
19485
19486
19487 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
19488     
19489     
19490       /**
19491      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
19492      */
19493     toolbars : false,
19494    
19495      /**
19496      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19497      *                        Roo.resizable.
19498      */
19499     resizable : false,
19500      /**
19501      * @cfg {Number} height (in pixels)
19502      */   
19503     height: 300,
19504    /**
19505      * @cfg {Number} width (in pixels)
19506      */   
19507     width: false,
19508     
19509     /**
19510      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19511      * 
19512      */
19513     stylesheets: false,
19514     
19515     // id of frame..
19516     frameId: false,
19517     
19518     // private properties
19519     validationEvent : false,
19520     deferHeight: true,
19521     initialized : false,
19522     activated : false,
19523     
19524     onFocus : Roo.emptyFn,
19525     iframePad:3,
19526     hideMode:'offsets',
19527     
19528     
19529     tbContainer : false,
19530     
19531     toolbarContainer :function() {
19532         return this.wrap.select('.x-html-editor-tb',true).first();
19533     },
19534
19535     /**
19536      * Protected method that will not generally be called directly. It
19537      * is called when the editor creates its toolbar. Override this method if you need to
19538      * add custom toolbar buttons.
19539      * @param {HtmlEditor} editor
19540      */
19541     createToolbar : function(){
19542         
19543         Roo.log("create toolbars");
19544         
19545         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
19546         this.toolbars[0].render(this.toolbarContainer());
19547         
19548         return;
19549         
19550 //        if (!editor.toolbars || !editor.toolbars.length) {
19551 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
19552 //        }
19553 //        
19554 //        for (var i =0 ; i < editor.toolbars.length;i++) {
19555 //            editor.toolbars[i] = Roo.factory(
19556 //                    typeof(editor.toolbars[i]) == 'string' ?
19557 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
19558 //                Roo.bootstrap.HtmlEditor);
19559 //            editor.toolbars[i].init(editor);
19560 //        }
19561     },
19562
19563      
19564     // private
19565     onRender : function(ct, position)
19566     {
19567        // Roo.log("Call onRender: " + this.xtype);
19568         var _t = this;
19569         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
19570       
19571         this.wrap = this.inputEl().wrap({
19572             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
19573         });
19574         
19575         this.editorcore.onRender(ct, position);
19576          
19577         if (this.resizable) {
19578             this.resizeEl = new Roo.Resizable(this.wrap, {
19579                 pinned : true,
19580                 wrap: true,
19581                 dynamic : true,
19582                 minHeight : this.height,
19583                 height: this.height,
19584                 handles : this.resizable,
19585                 width: this.width,
19586                 listeners : {
19587                     resize : function(r, w, h) {
19588                         _t.onResize(w,h); // -something
19589                     }
19590                 }
19591             });
19592             
19593         }
19594         this.createToolbar(this);
19595        
19596         
19597         if(!this.width && this.resizable){
19598             this.setSize(this.wrap.getSize());
19599         }
19600         if (this.resizeEl) {
19601             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
19602             // should trigger onReize..
19603         }
19604         
19605     },
19606
19607     // private
19608     onResize : function(w, h)
19609     {
19610         Roo.log('resize: ' +w + ',' + h );
19611         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
19612         var ew = false;
19613         var eh = false;
19614         
19615         if(this.inputEl() ){
19616             if(typeof w == 'number'){
19617                 var aw = w - this.wrap.getFrameWidth('lr');
19618                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
19619                 ew = aw;
19620             }
19621             if(typeof h == 'number'){
19622                  var tbh = -11;  // fixme it needs to tool bar size!
19623                 for (var i =0; i < this.toolbars.length;i++) {
19624                     // fixme - ask toolbars for heights?
19625                     tbh += this.toolbars[i].el.getHeight();
19626                     //if (this.toolbars[i].footer) {
19627                     //    tbh += this.toolbars[i].footer.el.getHeight();
19628                     //}
19629                 }
19630               
19631                 
19632                 
19633                 
19634                 
19635                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
19636                 ah -= 5; // knock a few pixes off for look..
19637                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
19638                 var eh = ah;
19639             }
19640         }
19641         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
19642         this.editorcore.onResize(ew,eh);
19643         
19644     },
19645
19646     /**
19647      * Toggles the editor between standard and source edit mode.
19648      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19649      */
19650     toggleSourceEdit : function(sourceEditMode)
19651     {
19652         this.editorcore.toggleSourceEdit(sourceEditMode);
19653         
19654         if(this.editorcore.sourceEditMode){
19655             Roo.log('editor - showing textarea');
19656             
19657 //            Roo.log('in');
19658 //            Roo.log(this.syncValue());
19659             this.syncValue();
19660             this.inputEl().removeClass(['hide', 'x-hidden']);
19661             this.inputEl().dom.removeAttribute('tabIndex');
19662             this.inputEl().focus();
19663         }else{
19664             Roo.log('editor - hiding textarea');
19665 //            Roo.log('out')
19666 //            Roo.log(this.pushValue()); 
19667             this.pushValue();
19668             
19669             this.inputEl().addClass(['hide', 'x-hidden']);
19670             this.inputEl().dom.setAttribute('tabIndex', -1);
19671             //this.deferFocus();
19672         }
19673          
19674         if(this.resizable){
19675             this.setSize(this.wrap.getSize());
19676         }
19677         
19678         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
19679     },
19680  
19681     // private (for BoxComponent)
19682     adjustSize : Roo.BoxComponent.prototype.adjustSize,
19683
19684     // private (for BoxComponent)
19685     getResizeEl : function(){
19686         return this.wrap;
19687     },
19688
19689     // private (for BoxComponent)
19690     getPositionEl : function(){
19691         return this.wrap;
19692     },
19693
19694     // private
19695     initEvents : function(){
19696         this.originalValue = this.getValue();
19697     },
19698
19699 //    /**
19700 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19701 //     * @method
19702 //     */
19703 //    markInvalid : Roo.emptyFn,
19704 //    /**
19705 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19706 //     * @method
19707 //     */
19708 //    clearInvalid : Roo.emptyFn,
19709
19710     setValue : function(v){
19711         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
19712         this.editorcore.pushValue();
19713     },
19714
19715      
19716     // private
19717     deferFocus : function(){
19718         this.focus.defer(10, this);
19719     },
19720
19721     // doc'ed in Field
19722     focus : function(){
19723         this.editorcore.focus();
19724         
19725     },
19726       
19727
19728     // private
19729     onDestroy : function(){
19730         
19731         
19732         
19733         if(this.rendered){
19734             
19735             for (var i =0; i < this.toolbars.length;i++) {
19736                 // fixme - ask toolbars for heights?
19737                 this.toolbars[i].onDestroy();
19738             }
19739             
19740             this.wrap.dom.innerHTML = '';
19741             this.wrap.remove();
19742         }
19743     },
19744
19745     // private
19746     onFirstFocus : function(){
19747         //Roo.log("onFirstFocus");
19748         this.editorcore.onFirstFocus();
19749          for (var i =0; i < this.toolbars.length;i++) {
19750             this.toolbars[i].onFirstFocus();
19751         }
19752         
19753     },
19754     
19755     // private
19756     syncValue : function()
19757     {   
19758         this.editorcore.syncValue();
19759     },
19760     
19761     pushValue : function()
19762     {   
19763         this.editorcore.pushValue();
19764     }
19765      
19766     
19767     // hide stuff that is not compatible
19768     /**
19769      * @event blur
19770      * @hide
19771      */
19772     /**
19773      * @event change
19774      * @hide
19775      */
19776     /**
19777      * @event focus
19778      * @hide
19779      */
19780     /**
19781      * @event specialkey
19782      * @hide
19783      */
19784     /**
19785      * @cfg {String} fieldClass @hide
19786      */
19787     /**
19788      * @cfg {String} focusClass @hide
19789      */
19790     /**
19791      * @cfg {String} autoCreate @hide
19792      */
19793     /**
19794      * @cfg {String} inputType @hide
19795      */
19796     /**
19797      * @cfg {String} invalidClass @hide
19798      */
19799     /**
19800      * @cfg {String} invalidText @hide
19801      */
19802     /**
19803      * @cfg {String} msgFx @hide
19804      */
19805     /**
19806      * @cfg {String} validateOnBlur @hide
19807      */
19808 });
19809  
19810     
19811    
19812    
19813    
19814       
19815 Roo.namespace('Roo.bootstrap.htmleditor');
19816 /**
19817  * @class Roo.bootstrap.HtmlEditorToolbar1
19818  * Basic Toolbar
19819  * 
19820  * Usage:
19821  *
19822  new Roo.bootstrap.HtmlEditor({
19823     ....
19824     toolbars : [
19825         new Roo.bootstrap.HtmlEditorToolbar1({
19826             disable : { fonts: 1 , format: 1, ..., ... , ...],
19827             btns : [ .... ]
19828         })
19829     }
19830      
19831  * 
19832  * @cfg {Object} disable List of elements to disable..
19833  * @cfg {Array} btns List of additional buttons.
19834  * 
19835  * 
19836  * NEEDS Extra CSS? 
19837  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
19838  */
19839  
19840 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
19841 {
19842     
19843     Roo.apply(this, config);
19844     
19845     // default disabled, based on 'good practice'..
19846     this.disable = this.disable || {};
19847     Roo.applyIf(this.disable, {
19848         fontSize : true,
19849         colors : true,
19850         specialElements : true
19851     });
19852     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
19853     
19854     this.editor = config.editor;
19855     this.editorcore = config.editor.editorcore;
19856     
19857     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
19858     
19859     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
19860     // dont call parent... till later.
19861 }
19862 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
19863      
19864     bar : true,
19865     
19866     editor : false,
19867     editorcore : false,
19868     
19869     
19870     formats : [
19871         "p" ,  
19872         "h1","h2","h3","h4","h5","h6", 
19873         "pre", "code", 
19874         "abbr", "acronym", "address", "cite", "samp", "var",
19875         'div','span'
19876     ],
19877     
19878     onRender : function(ct, position)
19879     {
19880        // Roo.log("Call onRender: " + this.xtype);
19881         
19882        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
19883        Roo.log(this.el);
19884        this.el.dom.style.marginBottom = '0';
19885        var _this = this;
19886        var editorcore = this.editorcore;
19887        var editor= this.editor;
19888        
19889        var children = [];
19890        var btn = function(id,cmd , toggle, handler){
19891        
19892             var  event = toggle ? 'toggle' : 'click';
19893        
19894             var a = {
19895                 size : 'sm',
19896                 xtype: 'Button',
19897                 xns: Roo.bootstrap,
19898                 glyphicon : id,
19899                 cmd : id || cmd,
19900                 enableToggle:toggle !== false,
19901                 //html : 'submit'
19902                 pressed : toggle ? false : null,
19903                 listeners : {}
19904             }
19905             a.listeners[toggle ? 'toggle' : 'click'] = function() {
19906                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
19907             }
19908             children.push(a);
19909             return a;
19910        }
19911         
19912         var style = {
19913                 xtype: 'Button',
19914                 size : 'sm',
19915                 xns: Roo.bootstrap,
19916                 glyphicon : 'font',
19917                 //html : 'submit'
19918                 menu : {
19919                     xtype: 'Menu',
19920                     xns: Roo.bootstrap,
19921                     items:  []
19922                 }
19923         };
19924         Roo.each(this.formats, function(f) {
19925             style.menu.items.push({
19926                 xtype :'MenuItem',
19927                 xns: Roo.bootstrap,
19928                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
19929                 tagname : f,
19930                 listeners : {
19931                     click : function()
19932                     {
19933                         editorcore.insertTag(this.tagname);
19934                         editor.focus();
19935                     }
19936                 }
19937                 
19938             });
19939         });
19940          children.push(style);   
19941             
19942             
19943         btn('bold',false,true);
19944         btn('italic',false,true);
19945         btn('align-left', 'justifyleft',true);
19946         btn('align-center', 'justifycenter',true);
19947         btn('align-right' , 'justifyright',true);
19948         btn('link', false, false, function(btn) {
19949             //Roo.log("create link?");
19950             var url = prompt(this.createLinkText, this.defaultLinkValue);
19951             if(url && url != 'http:/'+'/'){
19952                 this.editorcore.relayCmd('createlink', url);
19953             }
19954         }),
19955         btn('list','insertunorderedlist',true);
19956         btn('pencil', false,true, function(btn){
19957                 Roo.log(this);
19958                 
19959                 this.toggleSourceEdit(btn.pressed);
19960         });
19961         /*
19962         var cog = {
19963                 xtype: 'Button',
19964                 size : 'sm',
19965                 xns: Roo.bootstrap,
19966                 glyphicon : 'cog',
19967                 //html : 'submit'
19968                 menu : {
19969                     xtype: 'Menu',
19970                     xns: Roo.bootstrap,
19971                     items:  []
19972                 }
19973         };
19974         
19975         cog.menu.items.push({
19976             xtype :'MenuItem',
19977             xns: Roo.bootstrap,
19978             html : Clean styles,
19979             tagname : f,
19980             listeners : {
19981                 click : function()
19982                 {
19983                     editorcore.insertTag(this.tagname);
19984                     editor.focus();
19985                 }
19986             }
19987             
19988         });
19989        */
19990         
19991          
19992        this.xtype = 'NavSimplebar';
19993         
19994         for(var i=0;i< children.length;i++) {
19995             
19996             this.buttons.add(this.addxtypeChild(children[i]));
19997             
19998         }
19999         
20000         editor.on('editorevent', this.updateToolbar, this);
20001     },
20002     onBtnClick : function(id)
20003     {
20004        this.editorcore.relayCmd(id);
20005        this.editorcore.focus();
20006     },
20007     
20008     /**
20009      * Protected method that will not generally be called directly. It triggers
20010      * a toolbar update by reading the markup state of the current selection in the editor.
20011      */
20012     updateToolbar: function(){
20013
20014         if(!this.editorcore.activated){
20015             this.editor.onFirstFocus(); // is this neeed?
20016             return;
20017         }
20018
20019         var btns = this.buttons; 
20020         var doc = this.editorcore.doc;
20021         btns.get('bold').setActive(doc.queryCommandState('bold'));
20022         btns.get('italic').setActive(doc.queryCommandState('italic'));
20023         //btns.get('underline').setActive(doc.queryCommandState('underline'));
20024         
20025         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
20026         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
20027         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
20028         
20029         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
20030         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
20031          /*
20032         
20033         var ans = this.editorcore.getAllAncestors();
20034         if (this.formatCombo) {
20035             
20036             
20037             var store = this.formatCombo.store;
20038             this.formatCombo.setValue("");
20039             for (var i =0; i < ans.length;i++) {
20040                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
20041                     // select it..
20042                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
20043                     break;
20044                 }
20045             }
20046         }
20047         
20048         
20049         
20050         // hides menus... - so this cant be on a menu...
20051         Roo.bootstrap.MenuMgr.hideAll();
20052         */
20053         Roo.bootstrap.MenuMgr.hideAll();
20054         //this.editorsyncValue();
20055     },
20056     onFirstFocus: function() {
20057         this.buttons.each(function(item){
20058            item.enable();
20059         });
20060     },
20061     toggleSourceEdit : function(sourceEditMode){
20062         
20063           
20064         if(sourceEditMode){
20065             Roo.log("disabling buttons");
20066            this.buttons.each( function(item){
20067                 if(item.cmd != 'pencil'){
20068                     item.disable();
20069                 }
20070             });
20071           
20072         }else{
20073             Roo.log("enabling buttons");
20074             if(this.editorcore.initialized){
20075                 this.buttons.each( function(item){
20076                     item.enable();
20077                 });
20078             }
20079             
20080         }
20081         Roo.log("calling toggole on editor");
20082         // tell the editor that it's been pressed..
20083         this.editor.toggleSourceEdit(sourceEditMode);
20084        
20085     }
20086 });
20087
20088
20089
20090
20091
20092 /**
20093  * @class Roo.bootstrap.Table.AbstractSelectionModel
20094  * @extends Roo.util.Observable
20095  * Abstract base class for grid SelectionModels.  It provides the interface that should be
20096  * implemented by descendant classes.  This class should not be directly instantiated.
20097  * @constructor
20098  */
20099 Roo.bootstrap.Table.AbstractSelectionModel = function(){
20100     this.locked = false;
20101     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
20102 };
20103
20104
20105 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
20106     /** @ignore Called by the grid automatically. Do not call directly. */
20107     init : function(grid){
20108         this.grid = grid;
20109         this.initEvents();
20110     },
20111
20112     /**
20113      * Locks the selections.
20114      */
20115     lock : function(){
20116         this.locked = true;
20117     },
20118
20119     /**
20120      * Unlocks the selections.
20121      */
20122     unlock : function(){
20123         this.locked = false;
20124     },
20125
20126     /**
20127      * Returns true if the selections are locked.
20128      * @return {Boolean}
20129      */
20130     isLocked : function(){
20131         return this.locked;
20132     }
20133 });
20134 /**
20135  * @extends Roo.bootstrap.Table.AbstractSelectionModel
20136  * @class Roo.bootstrap.Table.RowSelectionModel
20137  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
20138  * It supports multiple selections and keyboard selection/navigation. 
20139  * @constructor
20140  * @param {Object} config
20141  */
20142
20143 Roo.bootstrap.Table.RowSelectionModel = function(config){
20144     Roo.apply(this, config);
20145     this.selections = new Roo.util.MixedCollection(false, function(o){
20146         return o.id;
20147     });
20148
20149     this.last = false;
20150     this.lastActive = false;
20151
20152     this.addEvents({
20153         /**
20154              * @event selectionchange
20155              * Fires when the selection changes
20156              * @param {SelectionModel} this
20157              */
20158             "selectionchange" : true,
20159         /**
20160              * @event afterselectionchange
20161              * Fires after the selection changes (eg. by key press or clicking)
20162              * @param {SelectionModel} this
20163              */
20164             "afterselectionchange" : true,
20165         /**
20166              * @event beforerowselect
20167              * Fires when a row is selected being selected, return false to cancel.
20168              * @param {SelectionModel} this
20169              * @param {Number} rowIndex The selected index
20170              * @param {Boolean} keepExisting False if other selections will be cleared
20171              */
20172             "beforerowselect" : true,
20173         /**
20174              * @event rowselect
20175              * Fires when a row is selected.
20176              * @param {SelectionModel} this
20177              * @param {Number} rowIndex The selected index
20178              * @param {Roo.data.Record} r The record
20179              */
20180             "rowselect" : true,
20181         /**
20182              * @event rowdeselect
20183              * Fires when a row is deselected.
20184              * @param {SelectionModel} this
20185              * @param {Number} rowIndex The selected index
20186              */
20187         "rowdeselect" : true
20188     });
20189     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
20190     this.locked = false;
20191 };
20192
20193 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
20194     /**
20195      * @cfg {Boolean} singleSelect
20196      * True to allow selection of only one row at a time (defaults to false)
20197      */
20198     singleSelect : false,
20199
20200     // private
20201     initEvents : function(){
20202
20203         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
20204             this.grid.on("mousedown", this.handleMouseDown, this);
20205         }else{ // allow click to work like normal
20206             this.grid.on("rowclick", this.handleDragableRowClick, this);
20207         }
20208
20209         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
20210             "up" : function(e){
20211                 if(!e.shiftKey){
20212                     this.selectPrevious(e.shiftKey);
20213                 }else if(this.last !== false && this.lastActive !== false){
20214                     var last = this.last;
20215                     this.selectRange(this.last,  this.lastActive-1);
20216                     this.grid.getView().focusRow(this.lastActive);
20217                     if(last !== false){
20218                         this.last = last;
20219                     }
20220                 }else{
20221                     this.selectFirstRow();
20222                 }
20223                 this.fireEvent("afterselectionchange", this);
20224             },
20225             "down" : function(e){
20226                 if(!e.shiftKey){
20227                     this.selectNext(e.shiftKey);
20228                 }else if(this.last !== false && this.lastActive !== false){
20229                     var last = this.last;
20230                     this.selectRange(this.last,  this.lastActive+1);
20231                     this.grid.getView().focusRow(this.lastActive);
20232                     if(last !== false){
20233                         this.last = last;
20234                     }
20235                 }else{
20236                     this.selectFirstRow();
20237                 }
20238                 this.fireEvent("afterselectionchange", this);
20239             },
20240             scope: this
20241         });
20242
20243         var view = this.grid.view;
20244         view.on("refresh", this.onRefresh, this);
20245         view.on("rowupdated", this.onRowUpdated, this);
20246         view.on("rowremoved", this.onRemove, this);
20247     },
20248
20249     // private
20250     onRefresh : function(){
20251         var ds = this.grid.dataSource, i, v = this.grid.view;
20252         var s = this.selections;
20253         s.each(function(r){
20254             if((i = ds.indexOfId(r.id)) != -1){
20255                 v.onRowSelect(i);
20256             }else{
20257                 s.remove(r);
20258             }
20259         });
20260     },
20261
20262     // private
20263     onRemove : function(v, index, r){
20264         this.selections.remove(r);
20265     },
20266
20267     // private
20268     onRowUpdated : function(v, index, r){
20269         if(this.isSelected(r)){
20270             v.onRowSelect(index);
20271         }
20272     },
20273
20274     /**
20275      * Select records.
20276      * @param {Array} records The records to select
20277      * @param {Boolean} keepExisting (optional) True to keep existing selections
20278      */
20279     selectRecords : function(records, keepExisting){
20280         if(!keepExisting){
20281             this.clearSelections();
20282         }
20283         var ds = this.grid.dataSource;
20284         for(var i = 0, len = records.length; i < len; i++){
20285             this.selectRow(ds.indexOf(records[i]), true);
20286         }
20287     },
20288
20289     /**
20290      * Gets the number of selected rows.
20291      * @return {Number}
20292      */
20293     getCount : function(){
20294         return this.selections.length;
20295     },
20296
20297     /**
20298      * Selects the first row in the grid.
20299      */
20300     selectFirstRow : function(){
20301         this.selectRow(0);
20302     },
20303
20304     /**
20305      * Select the last row.
20306      * @param {Boolean} keepExisting (optional) True to keep existing selections
20307      */
20308     selectLastRow : function(keepExisting){
20309         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
20310     },
20311
20312     /**
20313      * Selects the row immediately following the last selected row.
20314      * @param {Boolean} keepExisting (optional) True to keep existing selections
20315      */
20316     selectNext : function(keepExisting){
20317         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
20318             this.selectRow(this.last+1, keepExisting);
20319             this.grid.getView().focusRow(this.last);
20320         }
20321     },
20322
20323     /**
20324      * Selects the row that precedes the last selected row.
20325      * @param {Boolean} keepExisting (optional) True to keep existing selections
20326      */
20327     selectPrevious : function(keepExisting){
20328         if(this.last){
20329             this.selectRow(this.last-1, keepExisting);
20330             this.grid.getView().focusRow(this.last);
20331         }
20332     },
20333
20334     /**
20335      * Returns the selected records
20336      * @return {Array} Array of selected records
20337      */
20338     getSelections : function(){
20339         return [].concat(this.selections.items);
20340     },
20341
20342     /**
20343      * Returns the first selected record.
20344      * @return {Record}
20345      */
20346     getSelected : function(){
20347         return this.selections.itemAt(0);
20348     },
20349
20350
20351     /**
20352      * Clears all selections.
20353      */
20354     clearSelections : function(fast){
20355         if(this.locked) return;
20356         if(fast !== true){
20357             var ds = this.grid.dataSource;
20358             var s = this.selections;
20359             s.each(function(r){
20360                 this.deselectRow(ds.indexOfId(r.id));
20361             }, this);
20362             s.clear();
20363         }else{
20364             this.selections.clear();
20365         }
20366         this.last = false;
20367     },
20368
20369
20370     /**
20371      * Selects all rows.
20372      */
20373     selectAll : function(){
20374         if(this.locked) return;
20375         this.selections.clear();
20376         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
20377             this.selectRow(i, true);
20378         }
20379     },
20380
20381     /**
20382      * Returns True if there is a selection.
20383      * @return {Boolean}
20384      */
20385     hasSelection : function(){
20386         return this.selections.length > 0;
20387     },
20388
20389     /**
20390      * Returns True if the specified row is selected.
20391      * @param {Number/Record} record The record or index of the record to check
20392      * @return {Boolean}
20393      */
20394     isSelected : function(index){
20395         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
20396         return (r && this.selections.key(r.id) ? true : false);
20397     },
20398
20399     /**
20400      * Returns True if the specified record id is selected.
20401      * @param {String} id The id of record to check
20402      * @return {Boolean}
20403      */
20404     isIdSelected : function(id){
20405         return (this.selections.key(id) ? true : false);
20406     },
20407
20408     // private
20409     handleMouseDown : function(e, t){
20410         var view = this.grid.getView(), rowIndex;
20411         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
20412             return;
20413         };
20414         if(e.shiftKey && this.last !== false){
20415             var last = this.last;
20416             this.selectRange(last, rowIndex, e.ctrlKey);
20417             this.last = last; // reset the last
20418             view.focusRow(rowIndex);
20419         }else{
20420             var isSelected = this.isSelected(rowIndex);
20421             if(e.button !== 0 && isSelected){
20422                 view.focusRow(rowIndex);
20423             }else if(e.ctrlKey && isSelected){
20424                 this.deselectRow(rowIndex);
20425             }else if(!isSelected){
20426                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
20427                 view.focusRow(rowIndex);
20428             }
20429         }
20430         this.fireEvent("afterselectionchange", this);
20431     },
20432     // private
20433     handleDragableRowClick :  function(grid, rowIndex, e) 
20434     {
20435         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
20436             this.selectRow(rowIndex, false);
20437             grid.view.focusRow(rowIndex);
20438              this.fireEvent("afterselectionchange", this);
20439         }
20440     },
20441     
20442     /**
20443      * Selects multiple rows.
20444      * @param {Array} rows Array of the indexes of the row to select
20445      * @param {Boolean} keepExisting (optional) True to keep existing selections
20446      */
20447     selectRows : function(rows, keepExisting){
20448         if(!keepExisting){
20449             this.clearSelections();
20450         }
20451         for(var i = 0, len = rows.length; i < len; i++){
20452             this.selectRow(rows[i], true);
20453         }
20454     },
20455
20456     /**
20457      * Selects a range of rows. All rows in between startRow and endRow are also selected.
20458      * @param {Number} startRow The index of the first row in the range
20459      * @param {Number} endRow The index of the last row in the range
20460      * @param {Boolean} keepExisting (optional) True to retain existing selections
20461      */
20462     selectRange : function(startRow, endRow, keepExisting){
20463         if(this.locked) return;
20464         if(!keepExisting){
20465             this.clearSelections();
20466         }
20467         if(startRow <= endRow){
20468             for(var i = startRow; i <= endRow; i++){
20469                 this.selectRow(i, true);
20470             }
20471         }else{
20472             for(var i = startRow; i >= endRow; i--){
20473                 this.selectRow(i, true);
20474             }
20475         }
20476     },
20477
20478     /**
20479      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
20480      * @param {Number} startRow The index of the first row in the range
20481      * @param {Number} endRow The index of the last row in the range
20482      */
20483     deselectRange : function(startRow, endRow, preventViewNotify){
20484         if(this.locked) return;
20485         for(var i = startRow; i <= endRow; i++){
20486             this.deselectRow(i, preventViewNotify);
20487         }
20488     },
20489
20490     /**
20491      * Selects a row.
20492      * @param {Number} row The index of the row to select
20493      * @param {Boolean} keepExisting (optional) True to keep existing selections
20494      */
20495     selectRow : function(index, keepExisting, preventViewNotify){
20496         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
20497         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
20498             if(!keepExisting || this.singleSelect){
20499                 this.clearSelections();
20500             }
20501             var r = this.grid.dataSource.getAt(index);
20502             this.selections.add(r);
20503             this.last = this.lastActive = index;
20504             if(!preventViewNotify){
20505                 this.grid.getView().onRowSelect(index);
20506             }
20507             this.fireEvent("rowselect", this, index, r);
20508             this.fireEvent("selectionchange", this);
20509         }
20510     },
20511
20512     /**
20513      * Deselects a row.
20514      * @param {Number} row The index of the row to deselect
20515      */
20516     deselectRow : function(index, preventViewNotify){
20517         if(this.locked) return;
20518         if(this.last == index){
20519             this.last = false;
20520         }
20521         if(this.lastActive == index){
20522             this.lastActive = false;
20523         }
20524         var r = this.grid.dataSource.getAt(index);
20525         this.selections.remove(r);
20526         if(!preventViewNotify){
20527             this.grid.getView().onRowDeselect(index);
20528         }
20529         this.fireEvent("rowdeselect", this, index);
20530         this.fireEvent("selectionchange", this);
20531     },
20532
20533     // private
20534     restoreLast : function(){
20535         if(this._last){
20536             this.last = this._last;
20537         }
20538     },
20539
20540     // private
20541     acceptsNav : function(row, col, cm){
20542         return !cm.isHidden(col) && cm.isCellEditable(col, row);
20543     },
20544
20545     // private
20546     onEditorKey : function(field, e){
20547         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
20548         if(k == e.TAB){
20549             e.stopEvent();
20550             ed.completeEdit();
20551             if(e.shiftKey){
20552                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
20553             }else{
20554                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
20555             }
20556         }else if(k == e.ENTER && !e.ctrlKey){
20557             e.stopEvent();
20558             ed.completeEdit();
20559             if(e.shiftKey){
20560                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
20561             }else{
20562                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
20563             }
20564         }else if(k == e.ESC){
20565             ed.cancelEdit();
20566         }
20567         if(newCell){
20568             g.startEditing(newCell[0], newCell[1]);
20569         }
20570     }
20571 });/*
20572  * Based on:
20573  * Ext JS Library 1.1.1
20574  * Copyright(c) 2006-2007, Ext JS, LLC.
20575  *
20576  * Originally Released Under LGPL - original licence link has changed is not relivant.
20577  *
20578  * Fork - LGPL
20579  * <script type="text/javascript">
20580  */
20581  
20582 /**
20583  * @class Roo.bootstrap.PagingToolbar
20584  * @extends Roo.Row
20585  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
20586  * @constructor
20587  * Create a new PagingToolbar
20588  * @param {Object} config The config object
20589  */
20590 Roo.bootstrap.PagingToolbar = function(config)
20591 {
20592     // old args format still supported... - xtype is prefered..
20593         // created from xtype...
20594     var ds = config.dataSource;
20595     this.toolbarItems = [];
20596     if (config.items) {
20597         this.toolbarItems = config.items;
20598 //        config.items = [];
20599     }
20600     
20601     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
20602     this.ds = ds;
20603     this.cursor = 0;
20604     if (ds) { 
20605         this.bind(ds);
20606     }
20607     
20608     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
20609     
20610 };
20611
20612 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
20613     /**
20614      * @cfg {Roo.data.Store} dataSource
20615      * The underlying data store providing the paged data
20616      */
20617     /**
20618      * @cfg {String/HTMLElement/Element} container
20619      * container The id or element that will contain the toolbar
20620      */
20621     /**
20622      * @cfg {Boolean} displayInfo
20623      * True to display the displayMsg (defaults to false)
20624      */
20625     /**
20626      * @cfg {Number} pageSize
20627      * The number of records to display per page (defaults to 20)
20628      */
20629     pageSize: 20,
20630     /**
20631      * @cfg {String} displayMsg
20632      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
20633      */
20634     displayMsg : 'Displaying {0} - {1} of {2}',
20635     /**
20636      * @cfg {String} emptyMsg
20637      * The message to display when no records are found (defaults to "No data to display")
20638      */
20639     emptyMsg : 'No data to display',
20640     /**
20641      * Customizable piece of the default paging text (defaults to "Page")
20642      * @type String
20643      */
20644     beforePageText : "Page",
20645     /**
20646      * Customizable piece of the default paging text (defaults to "of %0")
20647      * @type String
20648      */
20649     afterPageText : "of {0}",
20650     /**
20651      * Customizable piece of the default paging text (defaults to "First Page")
20652      * @type String
20653      */
20654     firstText : "First Page",
20655     /**
20656      * Customizable piece of the default paging text (defaults to "Previous Page")
20657      * @type String
20658      */
20659     prevText : "Previous Page",
20660     /**
20661      * Customizable piece of the default paging text (defaults to "Next Page")
20662      * @type String
20663      */
20664     nextText : "Next Page",
20665     /**
20666      * Customizable piece of the default paging text (defaults to "Last Page")
20667      * @type String
20668      */
20669     lastText : "Last Page",
20670     /**
20671      * Customizable piece of the default paging text (defaults to "Refresh")
20672      * @type String
20673      */
20674     refreshText : "Refresh",
20675
20676     buttons : false,
20677     // private
20678     onRender : function(ct, position) 
20679     {
20680         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
20681         this.navgroup.parentId = this.id;
20682         this.navgroup.onRender(this.el, null);
20683         // add the buttons to the navgroup
20684         
20685         if(this.displayInfo){
20686             Roo.log(this.el.select('ul.navbar-nav',true).first());
20687             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
20688             this.displayEl = this.el.select('.x-paging-info', true).first();
20689 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
20690 //            this.displayEl = navel.el.select('span',true).first();
20691         }
20692         
20693         var _this = this;
20694         
20695         if(this.buttons){
20696             Roo.each(_this.buttons, function(e){
20697                Roo.factory(e).onRender(_this.el, null);
20698             });
20699         }
20700             
20701         Roo.each(_this.toolbarItems, function(e) {
20702             _this.navgroup.addItem(e);
20703         });
20704         
20705         
20706         this.first = this.navgroup.addItem({
20707             tooltip: this.firstText,
20708             cls: "prev",
20709             icon : 'fa fa-backward',
20710             disabled: true,
20711             preventDefault: true,
20712             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
20713         });
20714         
20715         this.prev =  this.navgroup.addItem({
20716             tooltip: this.prevText,
20717             cls: "prev",
20718             icon : 'fa fa-step-backward',
20719             disabled: true,
20720             preventDefault: true,
20721             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
20722         });
20723     //this.addSeparator();
20724         
20725         
20726         var field = this.navgroup.addItem( {
20727             tagtype : 'span',
20728             cls : 'x-paging-position',
20729             
20730             html : this.beforePageText  +
20731                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
20732                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
20733          } ); //?? escaped?
20734         
20735         this.field = field.el.select('input', true).first();
20736         this.field.on("keydown", this.onPagingKeydown, this);
20737         this.field.on("focus", function(){this.dom.select();});
20738     
20739     
20740         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
20741         //this.field.setHeight(18);
20742         //this.addSeparator();
20743         this.next = this.navgroup.addItem({
20744             tooltip: this.nextText,
20745             cls: "next",
20746             html : ' <i class="fa fa-step-forward">',
20747             disabled: true,
20748             preventDefault: true,
20749             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
20750         });
20751         this.last = this.navgroup.addItem({
20752             tooltip: this.lastText,
20753             icon : 'fa fa-forward',
20754             cls: "next",
20755             disabled: true,
20756             preventDefault: true,
20757             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
20758         });
20759     //this.addSeparator();
20760         this.loading = this.navgroup.addItem({
20761             tooltip: this.refreshText,
20762             icon: 'fa fa-refresh',
20763             preventDefault: true,
20764             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
20765         });
20766
20767     },
20768
20769     // private
20770     updateInfo : function(){
20771         if(this.displayEl){
20772             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
20773             var msg = count == 0 ?
20774                 this.emptyMsg :
20775                 String.format(
20776                     this.displayMsg,
20777                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
20778                 );
20779             this.displayEl.update(msg);
20780         }
20781     },
20782
20783     // private
20784     onLoad : function(ds, r, o){
20785        this.cursor = o.params ? o.params.start : 0;
20786        var d = this.getPageData(),
20787             ap = d.activePage,
20788             ps = d.pages;
20789         
20790        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
20791        this.field.dom.value = ap;
20792        this.first.setDisabled(ap == 1);
20793        this.prev.setDisabled(ap == 1);
20794        this.next.setDisabled(ap == ps);
20795        this.last.setDisabled(ap == ps);
20796        this.loading.enable();
20797        this.updateInfo();
20798     },
20799
20800     // private
20801     getPageData : function(){
20802         var total = this.ds.getTotalCount();
20803         return {
20804             total : total,
20805             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
20806             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
20807         };
20808     },
20809
20810     // private
20811     onLoadError : function(){
20812         this.loading.enable();
20813     },
20814
20815     // private
20816     onPagingKeydown : function(e){
20817         var k = e.getKey();
20818         var d = this.getPageData();
20819         if(k == e.RETURN){
20820             var v = this.field.dom.value, pageNum;
20821             if(!v || isNaN(pageNum = parseInt(v, 10))){
20822                 this.field.dom.value = d.activePage;
20823                 return;
20824             }
20825             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
20826             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20827             e.stopEvent();
20828         }
20829         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))
20830         {
20831           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
20832           this.field.dom.value = pageNum;
20833           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
20834           e.stopEvent();
20835         }
20836         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20837         {
20838           var v = this.field.dom.value, pageNum; 
20839           var increment = (e.shiftKey) ? 10 : 1;
20840           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20841             increment *= -1;
20842           if(!v || isNaN(pageNum = parseInt(v, 10))) {
20843             this.field.dom.value = d.activePage;
20844             return;
20845           }
20846           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
20847           {
20848             this.field.dom.value = parseInt(v, 10) + increment;
20849             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
20850             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20851           }
20852           e.stopEvent();
20853         }
20854     },
20855
20856     // private
20857     beforeLoad : function(){
20858         if(this.loading){
20859             this.loading.disable();
20860         }
20861     },
20862
20863     // private
20864     onClick : function(which){
20865         
20866         var ds = this.ds;
20867         if (!ds) {
20868             return;
20869         }
20870         
20871         switch(which){
20872             case "first":
20873                 ds.load({params:{start: 0, limit: this.pageSize}});
20874             break;
20875             case "prev":
20876                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
20877             break;
20878             case "next":
20879                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
20880             break;
20881             case "last":
20882                 var total = ds.getTotalCount();
20883                 var extra = total % this.pageSize;
20884                 var lastStart = extra ? (total - extra) : total-this.pageSize;
20885                 ds.load({params:{start: lastStart, limit: this.pageSize}});
20886             break;
20887             case "refresh":
20888                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
20889             break;
20890         }
20891     },
20892
20893     /**
20894      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
20895      * @param {Roo.data.Store} store The data store to unbind
20896      */
20897     unbind : function(ds){
20898         ds.un("beforeload", this.beforeLoad, this);
20899         ds.un("load", this.onLoad, this);
20900         ds.un("loadexception", this.onLoadError, this);
20901         ds.un("remove", this.updateInfo, this);
20902         ds.un("add", this.updateInfo, this);
20903         this.ds = undefined;
20904     },
20905
20906     /**
20907      * Binds the paging toolbar to the specified {@link Roo.data.Store}
20908      * @param {Roo.data.Store} store The data store to bind
20909      */
20910     bind : function(ds){
20911         ds.on("beforeload", this.beforeLoad, this);
20912         ds.on("load", this.onLoad, this);
20913         ds.on("loadexception", this.onLoadError, this);
20914         ds.on("remove", this.updateInfo, this);
20915         ds.on("add", this.updateInfo, this);
20916         this.ds = ds;
20917     }
20918 });/*
20919  * - LGPL
20920  *
20921  * element
20922  * 
20923  */
20924
20925 /**
20926  * @class Roo.bootstrap.MessageBar
20927  * @extends Roo.bootstrap.Component
20928  * Bootstrap MessageBar class
20929  * @cfg {String} html contents of the MessageBar
20930  * @cfg {String} weight (info | success | warning | danger) default info
20931  * @cfg {String} beforeClass insert the bar before the given class
20932  * @cfg {Boolean} closable (true | false) default false
20933  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
20934  * 
20935  * @constructor
20936  * Create a new Element
20937  * @param {Object} config The config object
20938  */
20939
20940 Roo.bootstrap.MessageBar = function(config){
20941     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
20942 };
20943
20944 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
20945     
20946     html: '',
20947     weight: 'info',
20948     closable: false,
20949     fixed: false,
20950     beforeClass: 'bootstrap-sticky-wrap',
20951     
20952     getAutoCreate : function(){
20953         
20954         var cfg = {
20955             tag: 'div',
20956             cls: 'alert alert-dismissable alert-' + this.weight,
20957             cn: [
20958                 {
20959                     tag: 'span',
20960                     cls: 'message',
20961                     html: this.html || ''
20962                 }
20963             ]
20964         }
20965         
20966         if(this.fixed){
20967             cfg.cls += ' alert-messages-fixed';
20968         }
20969         
20970         if(this.closable){
20971             cfg.cn.push({
20972                 tag: 'button',
20973                 cls: 'close',
20974                 html: 'x'
20975             });
20976         }
20977         
20978         return cfg;
20979     },
20980     
20981     onRender : function(ct, position)
20982     {
20983         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20984         
20985         if(!this.el){
20986             var cfg = Roo.apply({},  this.getAutoCreate());
20987             cfg.id = Roo.id();
20988             
20989             if (this.cls) {
20990                 cfg.cls += ' ' + this.cls;
20991             }
20992             if (this.style) {
20993                 cfg.style = this.style;
20994             }
20995             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
20996             
20997             this.el.setVisibilityMode(Roo.Element.DISPLAY);
20998         }
20999         
21000         this.el.select('>button.close').on('click', this.hide, this);
21001         
21002     },
21003     
21004     show : function()
21005     {
21006         if (!this.rendered) {
21007             this.render();
21008         }
21009         
21010         this.el.show();
21011         
21012         this.fireEvent('show', this);
21013         
21014     },
21015     
21016     hide : function()
21017     {
21018         if (!this.rendered) {
21019             this.render();
21020         }
21021         
21022         this.el.hide();
21023         
21024         this.fireEvent('hide', this);
21025     },
21026     
21027     update : function()
21028     {
21029 //        var e = this.el.dom.firstChild;
21030 //        
21031 //        if(this.closable){
21032 //            e = e.nextSibling;
21033 //        }
21034 //        
21035 //        e.data = this.html || '';
21036
21037         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
21038     }
21039    
21040 });
21041
21042  
21043
21044      /*
21045  * - LGPL
21046  *
21047  * Graph
21048  * 
21049  */
21050
21051
21052 /**
21053  * @class Roo.bootstrap.Graph
21054  * @extends Roo.bootstrap.Component
21055  * Bootstrap Graph class
21056 > Prameters
21057  -sm {number} sm 4
21058  -md {number} md 5
21059  @cfg {String} graphtype  bar | vbar | pie
21060  @cfg {number} g_x coodinator | centre x (pie)
21061  @cfg {number} g_y coodinator | centre y (pie)
21062  @cfg {number} g_r radius (pie)
21063  @cfg {number} g_height height of the chart (respected by all elements in the set)
21064  @cfg {number} g_width width of the chart (respected by all elements in the set)
21065  @cfg {Object} title The title of the chart
21066     
21067  -{Array}  values
21068  -opts (object) options for the chart 
21069      o {
21070      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
21071      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
21072      o vgutter (number)
21073      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.
21074      o stacked (boolean) whether or not to tread values as in a stacked bar chart
21075      o to
21076      o stretch (boolean)
21077      o }
21078  -opts (object) options for the pie
21079      o{
21080      o cut
21081      o startAngle (number)
21082      o endAngle (number)
21083      } 
21084  *
21085  * @constructor
21086  * Create a new Input
21087  * @param {Object} config The config object
21088  */
21089
21090 Roo.bootstrap.Graph = function(config){
21091     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
21092     
21093     this.addEvents({
21094         // img events
21095         /**
21096          * @event click
21097          * The img click event for the img.
21098          * @param {Roo.EventObject} e
21099          */
21100         "click" : true
21101     });
21102 };
21103
21104 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
21105     
21106     sm: 4,
21107     md: 5,
21108     graphtype: 'bar',
21109     g_height: 250,
21110     g_width: 400,
21111     g_x: 50,
21112     g_y: 50,
21113     g_r: 30,
21114     opts:{
21115         //g_colors: this.colors,
21116         g_type: 'soft',
21117         g_gutter: '20%'
21118
21119     },
21120     title : false,
21121
21122     getAutoCreate : function(){
21123         
21124         var cfg = {
21125             tag: 'div',
21126             html : null
21127         }
21128         
21129         
21130         return  cfg;
21131     },
21132
21133     onRender : function(ct,position){
21134         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
21135         this.raphael = Raphael(this.el.dom);
21136         
21137                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21138                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21139                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21140                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
21141                 /*
21142                 r.text(160, 10, "Single Series Chart").attr(txtattr);
21143                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
21144                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
21145                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
21146                 
21147                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
21148                 r.barchart(330, 10, 300, 220, data1);
21149                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
21150                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
21151                 */
21152                 
21153                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21154                 // r.barchart(30, 30, 560, 250,  xdata, {
21155                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
21156                 //     axis : "0 0 1 1",
21157                 //     axisxlabels :  xdata
21158                 //     //yvalues : cols,
21159                    
21160                 // });
21161 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21162 //        
21163 //        this.load(null,xdata,{
21164 //                axis : "0 0 1 1",
21165 //                axisxlabels :  xdata
21166 //                });
21167
21168     },
21169
21170     load : function(graphtype,xdata,opts){
21171         this.raphael.clear();
21172         if(!graphtype) {
21173             graphtype = this.graphtype;
21174         }
21175         if(!opts){
21176             opts = this.opts;
21177         }
21178         var r = this.raphael,
21179             fin = function () {
21180                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
21181             },
21182             fout = function () {
21183                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
21184             },
21185             pfin = function() {
21186                 this.sector.stop();
21187                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
21188
21189                 if (this.label) {
21190                     this.label[0].stop();
21191                     this.label[0].attr({ r: 7.5 });
21192                     this.label[1].attr({ "font-weight": 800 });
21193                 }
21194             },
21195             pfout = function() {
21196                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
21197
21198                 if (this.label) {
21199                     this.label[0].animate({ r: 5 }, 500, "bounce");
21200                     this.label[1].attr({ "font-weight": 400 });
21201                 }
21202             };
21203
21204         switch(graphtype){
21205             case 'bar':
21206                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21207                 break;
21208             case 'hbar':
21209                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21210                 break;
21211             case 'pie':
21212 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
21213 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
21214 //            
21215                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
21216                 
21217                 break;
21218
21219         }
21220         
21221         if(this.title){
21222             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
21223         }
21224         
21225     },
21226     
21227     setTitle: function(o)
21228     {
21229         this.title = o;
21230     },
21231     
21232     initEvents: function() {
21233         
21234         if(!this.href){
21235             this.el.on('click', this.onClick, this);
21236         }
21237     },
21238     
21239     onClick : function(e)
21240     {
21241         Roo.log('img onclick');
21242         this.fireEvent('click', this, e);
21243     }
21244    
21245 });
21246
21247  
21248 /*
21249  * - LGPL
21250  *
21251  * numberBox
21252  * 
21253  */
21254 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21255
21256 /**
21257  * @class Roo.bootstrap.dash.NumberBox
21258  * @extends Roo.bootstrap.Component
21259  * Bootstrap NumberBox class
21260  * @cfg {String} headline Box headline
21261  * @cfg {String} content Box content
21262  * @cfg {String} icon Box icon
21263  * @cfg {String} footer Footer text
21264  * @cfg {String} fhref Footer href
21265  * 
21266  * @constructor
21267  * Create a new NumberBox
21268  * @param {Object} config The config object
21269  */
21270
21271
21272 Roo.bootstrap.dash.NumberBox = function(config){
21273     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
21274     
21275 };
21276
21277 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
21278     
21279     headline : '',
21280     content : '',
21281     icon : '',
21282     footer : '',
21283     fhref : '',
21284     ficon : '',
21285     
21286     getAutoCreate : function(){
21287         
21288         var cfg = {
21289             tag : 'div',
21290             cls : 'small-box ',
21291             cn : [
21292                 {
21293                     tag : 'div',
21294                     cls : 'inner',
21295                     cn :[
21296                         {
21297                             tag : 'h3',
21298                             cls : 'roo-headline',
21299                             html : this.headline
21300                         },
21301                         {
21302                             tag : 'p',
21303                             cls : 'roo-content',
21304                             html : this.content
21305                         }
21306                     ]
21307                 }
21308             ]
21309         }
21310         
21311         if(this.icon){
21312             cfg.cn.push({
21313                 tag : 'div',
21314                 cls : 'icon',
21315                 cn :[
21316                     {
21317                         tag : 'i',
21318                         cls : 'ion ' + this.icon
21319                     }
21320                 ]
21321             });
21322         }
21323         
21324         if(this.footer){
21325             var footer = {
21326                 tag : 'a',
21327                 cls : 'small-box-footer',
21328                 href : this.fhref || '#',
21329                 html : this.footer
21330             };
21331             
21332             cfg.cn.push(footer);
21333             
21334         }
21335         
21336         return  cfg;
21337     },
21338
21339     onRender : function(ct,position){
21340         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
21341
21342
21343        
21344                 
21345     },
21346
21347     setHeadline: function (value)
21348     {
21349         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
21350     },
21351     
21352     setFooter: function (value, href)
21353     {
21354         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
21355         
21356         if(href){
21357             this.el.select('a.small-box-footer',true).first().attr('href', href);
21358         }
21359         
21360     },
21361
21362     setContent: function (value)
21363     {
21364         this.el.select('.roo-content',true).first().dom.innerHTML = value;
21365     },
21366
21367     initEvents: function() 
21368     {   
21369         
21370     }
21371     
21372 });
21373
21374  
21375 /*
21376  * - LGPL
21377  *
21378  * TabBox
21379  * 
21380  */
21381 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21382
21383 /**
21384  * @class Roo.bootstrap.dash.TabBox
21385  * @extends Roo.bootstrap.Component
21386  * Bootstrap TabBox class
21387  * @cfg {String} title Title of the TabBox
21388  * @cfg {String} icon Icon of the TabBox
21389  * @cfg {Boolean} showtabs (true|false) show the tabs default true
21390  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
21391  * 
21392  * @constructor
21393  * Create a new TabBox
21394  * @param {Object} config The config object
21395  */
21396
21397
21398 Roo.bootstrap.dash.TabBox = function(config){
21399     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
21400     this.addEvents({
21401         // raw events
21402         /**
21403          * @event addpane
21404          * When a pane is added
21405          * @param {Roo.bootstrap.dash.TabPane} pane
21406          */
21407         "addpane" : true,
21408         /**
21409          * @event activatepane
21410          * When a pane is activated
21411          * @param {Roo.bootstrap.dash.TabPane} pane
21412          */
21413         "activatepane" : true
21414         
21415          
21416     });
21417     
21418     this.panes = [];
21419 };
21420
21421 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
21422
21423     title : '',
21424     icon : false,
21425     showtabs : true,
21426     tabScrollable : false,
21427     
21428     getChildContainer : function()
21429     {
21430         return this.el.select('.tab-content', true).first();
21431     },
21432     
21433     getAutoCreate : function(){
21434         
21435         var header = {
21436             tag: 'li',
21437             cls: 'pull-left header',
21438             html: this.title,
21439             cn : []
21440         };
21441         
21442         if(this.icon){
21443             header.cn.push({
21444                 tag: 'i',
21445                 cls: 'fa ' + this.icon
21446             });
21447         }
21448         
21449         var h = {
21450             tag: 'ul',
21451             cls: 'nav nav-tabs pull-right',
21452             cn: [
21453                 header
21454             ]
21455         };
21456         
21457         if(this.tabScrollable){
21458             h = {
21459                 tag: 'div',
21460                 cls: 'tab-header',
21461                 cn: [
21462                     {
21463                         tag: 'ul',
21464                         cls: 'nav nav-tabs pull-right',
21465                         cn: [
21466                             header
21467                         ]
21468                     }
21469                 ]
21470             }
21471         }
21472         
21473         var cfg = {
21474             tag: 'div',
21475             cls: 'nav-tabs-custom',
21476             cn: [
21477                 h,
21478                 {
21479                     tag: 'div',
21480                     cls: 'tab-content no-padding',
21481                     cn: []
21482                 }
21483             ]
21484         }
21485
21486         return  cfg;
21487     },
21488     initEvents : function()
21489     {
21490         //Roo.log('add add pane handler');
21491         this.on('addpane', this.onAddPane, this);
21492     },
21493      /**
21494      * Updates the box title
21495      * @param {String} html to set the title to.
21496      */
21497     setTitle : function(value)
21498     {
21499         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
21500     },
21501     onAddPane : function(pane)
21502     {
21503         this.panes.push(pane);
21504         //Roo.log('addpane');
21505         //Roo.log(pane);
21506         // tabs are rendere left to right..
21507         if(!this.showtabs){
21508             return;
21509         }
21510         
21511         var ctr = this.el.select('.nav-tabs', true).first();
21512          
21513          
21514         var existing = ctr.select('.nav-tab',true);
21515         var qty = existing.getCount();;
21516         
21517         
21518         var tab = ctr.createChild({
21519             tag : 'li',
21520             cls : 'nav-tab' + (qty ? '' : ' active'),
21521             cn : [
21522                 {
21523                     tag : 'a',
21524                     href:'#',
21525                     html : pane.title
21526                 }
21527             ]
21528         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
21529         pane.tab = tab;
21530         
21531         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
21532         if (!qty) {
21533             pane.el.addClass('active');
21534         }
21535         
21536                 
21537     },
21538     onTabClick : function(ev,un,ob,pane)
21539     {
21540         //Roo.log('tab - prev default');
21541         ev.preventDefault();
21542         
21543         
21544         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
21545         pane.tab.addClass('active');
21546         //Roo.log(pane.title);
21547         this.getChildContainer().select('.tab-pane',true).removeClass('active');
21548         // technically we should have a deactivate event.. but maybe add later.
21549         // and it should not de-activate the selected tab...
21550         this.fireEvent('activatepane', pane);
21551         pane.el.addClass('active');
21552         pane.fireEvent('activate');
21553         
21554         
21555     },
21556     
21557     getActivePane : function()
21558     {
21559         var r = false;
21560         Roo.each(this.panes, function(p) {
21561             if(p.el.hasClass('active')){
21562                 r = p;
21563                 return false;
21564             }
21565             
21566             return;
21567         });
21568         
21569         return r;
21570     }
21571     
21572     
21573 });
21574
21575  
21576 /*
21577  * - LGPL
21578  *
21579  * Tab pane
21580  * 
21581  */
21582 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21583 /**
21584  * @class Roo.bootstrap.TabPane
21585  * @extends Roo.bootstrap.Component
21586  * Bootstrap TabPane class
21587  * @cfg {Boolean} active (false | true) Default false
21588  * @cfg {String} title title of panel
21589
21590  * 
21591  * @constructor
21592  * Create a new TabPane
21593  * @param {Object} config The config object
21594  */
21595
21596 Roo.bootstrap.dash.TabPane = function(config){
21597     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
21598     
21599     this.addEvents({
21600         // raw events
21601         /**
21602          * @event activate
21603          * When a pane is activated
21604          * @param {Roo.bootstrap.dash.TabPane} pane
21605          */
21606         "activate" : true
21607          
21608     });
21609 };
21610
21611 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
21612     
21613     active : false,
21614     title : '',
21615     
21616     // the tabBox that this is attached to.
21617     tab : false,
21618      
21619     getAutoCreate : function() 
21620     {
21621         var cfg = {
21622             tag: 'div',
21623             cls: 'tab-pane'
21624         }
21625         
21626         if(this.active){
21627             cfg.cls += ' active';
21628         }
21629         
21630         return cfg;
21631     },
21632     initEvents  : function()
21633     {
21634         //Roo.log('trigger add pane handler');
21635         this.parent().fireEvent('addpane', this)
21636     },
21637     
21638      /**
21639      * Updates the tab title 
21640      * @param {String} html to set the title to.
21641      */
21642     setTitle: function(str)
21643     {
21644         if (!this.tab) {
21645             return;
21646         }
21647         this.title = str;
21648         this.tab.select('a', true).first().dom.innerHTML = str;
21649         
21650     }
21651     
21652     
21653     
21654 });
21655
21656  
21657
21658
21659  /*
21660  * - LGPL
21661  *
21662  * menu
21663  * 
21664  */
21665 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21666
21667 /**
21668  * @class Roo.bootstrap.menu.Menu
21669  * @extends Roo.bootstrap.Component
21670  * Bootstrap Menu class - container for Menu
21671  * @cfg {String} html Text of the menu
21672  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
21673  * @cfg {String} icon Font awesome icon
21674  * @cfg {String} pos Menu align to (top | bottom) default bottom
21675  * 
21676  * 
21677  * @constructor
21678  * Create a new Menu
21679  * @param {Object} config The config object
21680  */
21681
21682
21683 Roo.bootstrap.menu.Menu = function(config){
21684     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
21685     
21686     this.addEvents({
21687         /**
21688          * @event beforeshow
21689          * Fires before this menu is displayed
21690          * @param {Roo.bootstrap.menu.Menu} this
21691          */
21692         beforeshow : true,
21693         /**
21694          * @event beforehide
21695          * Fires before this menu is hidden
21696          * @param {Roo.bootstrap.menu.Menu} this
21697          */
21698         beforehide : true,
21699         /**
21700          * @event show
21701          * Fires after this menu is displayed
21702          * @param {Roo.bootstrap.menu.Menu} this
21703          */
21704         show : true,
21705         /**
21706          * @event hide
21707          * Fires after this menu is hidden
21708          * @param {Roo.bootstrap.menu.Menu} this
21709          */
21710         hide : true,
21711         /**
21712          * @event click
21713          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
21714          * @param {Roo.bootstrap.menu.Menu} this
21715          * @param {Roo.EventObject} e
21716          */
21717         click : true
21718     });
21719     
21720 };
21721
21722 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
21723     
21724     submenu : false,
21725     html : '',
21726     weight : 'default',
21727     icon : false,
21728     pos : 'bottom',
21729     
21730     
21731     getChildContainer : function() {
21732         if(this.isSubMenu){
21733             return this.el;
21734         }
21735         
21736         return this.el.select('ul.dropdown-menu', true).first();  
21737     },
21738     
21739     getAutoCreate : function()
21740     {
21741         var text = [
21742             {
21743                 tag : 'span',
21744                 cls : 'roo-menu-text',
21745                 html : this.html
21746             }
21747         ];
21748         
21749         if(this.icon){
21750             text.unshift({
21751                 tag : 'i',
21752                 cls : 'fa ' + this.icon
21753             })
21754         }
21755         
21756         
21757         var cfg = {
21758             tag : 'div',
21759             cls : 'btn-group',
21760             cn : [
21761                 {
21762                     tag : 'button',
21763                     cls : 'dropdown-button btn btn-' + this.weight,
21764                     cn : text
21765                 },
21766                 {
21767                     tag : 'button',
21768                     cls : 'dropdown-toggle btn btn-' + this.weight,
21769                     cn : [
21770                         {
21771                             tag : 'span',
21772                             cls : 'caret'
21773                         }
21774                     ]
21775                 },
21776                 {
21777                     tag : 'ul',
21778                     cls : 'dropdown-menu'
21779                 }
21780             ]
21781             
21782         };
21783         
21784         if(this.pos == 'top'){
21785             cfg.cls += ' dropup';
21786         }
21787         
21788         if(this.isSubMenu){
21789             cfg = {
21790                 tag : 'ul',
21791                 cls : 'dropdown-menu'
21792             }
21793         }
21794         
21795         return cfg;
21796     },
21797     
21798     onRender : function(ct, position)
21799     {
21800         this.isSubMenu = ct.hasClass('dropdown-submenu');
21801         
21802         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
21803     },
21804     
21805     initEvents : function() 
21806     {
21807         if(this.isSubMenu){
21808             return;
21809         }
21810         
21811         this.hidden = true;
21812         
21813         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
21814         this.triggerEl.on('click', this.onTriggerPress, this);
21815         
21816         this.buttonEl = this.el.select('button.dropdown-button', true).first();
21817         this.buttonEl.on('click', this.onClick, this);
21818         
21819     },
21820     
21821     list : function()
21822     {
21823         if(this.isSubMenu){
21824             return this.el;
21825         }
21826         
21827         return this.el.select('ul.dropdown-menu', true).first();
21828     },
21829     
21830     onClick : function(e)
21831     {
21832         this.fireEvent("click", this, e);
21833     },
21834     
21835     onTriggerPress  : function(e)
21836     {   
21837         if (this.isVisible()) {
21838             this.hide();
21839         } else {
21840             this.show();
21841         }
21842     },
21843     
21844     isVisible : function(){
21845         return !this.hidden;
21846     },
21847     
21848     show : function()
21849     {
21850         this.fireEvent("beforeshow", this);
21851         
21852         this.hidden = false;
21853         this.el.addClass('open');
21854         
21855         Roo.get(document).on("mouseup", this.onMouseUp, this);
21856         
21857         this.fireEvent("show", this);
21858         
21859         
21860     },
21861     
21862     hide : function()
21863     {
21864         this.fireEvent("beforehide", this);
21865         
21866         this.hidden = true;
21867         this.el.removeClass('open');
21868         
21869         Roo.get(document).un("mouseup", this.onMouseUp);
21870         
21871         this.fireEvent("hide", this);
21872     },
21873     
21874     onMouseUp : function()
21875     {
21876         this.hide();
21877     }
21878     
21879 });
21880
21881  
21882  /*
21883  * - LGPL
21884  *
21885  * menu item
21886  * 
21887  */
21888 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21889
21890 /**
21891  * @class Roo.bootstrap.menu.Item
21892  * @extends Roo.bootstrap.Component
21893  * Bootstrap MenuItem class
21894  * @cfg {Boolean} submenu (true | false) default false
21895  * @cfg {String} html text of the item
21896  * @cfg {String} href the link
21897  * @cfg {Boolean} disable (true | false) default false
21898  * @cfg {Boolean} preventDefault (true | false) default true
21899  * @cfg {String} icon Font awesome icon
21900  * @cfg {String} pos Submenu align to (left | right) default right 
21901  * 
21902  * 
21903  * @constructor
21904  * Create a new Item
21905  * @param {Object} config The config object
21906  */
21907
21908
21909 Roo.bootstrap.menu.Item = function(config){
21910     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
21911     this.addEvents({
21912         /**
21913          * @event mouseover
21914          * Fires when the mouse is hovering over this menu
21915          * @param {Roo.bootstrap.menu.Item} this
21916          * @param {Roo.EventObject} e
21917          */
21918         mouseover : true,
21919         /**
21920          * @event mouseout
21921          * Fires when the mouse exits this menu
21922          * @param {Roo.bootstrap.menu.Item} this
21923          * @param {Roo.EventObject} e
21924          */
21925         mouseout : true,
21926         // raw events
21927         /**
21928          * @event click
21929          * The raw click event for the entire grid.
21930          * @param {Roo.EventObject} e
21931          */
21932         click : true
21933     });
21934 };
21935
21936 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
21937     
21938     submenu : false,
21939     href : '',
21940     html : '',
21941     preventDefault: true,
21942     disable : false,
21943     icon : false,
21944     pos : 'right',
21945     
21946     getAutoCreate : function()
21947     {
21948         var text = [
21949             {
21950                 tag : 'span',
21951                 cls : 'roo-menu-item-text',
21952                 html : this.html
21953             }
21954         ];
21955         
21956         if(this.icon){
21957             text.unshift({
21958                 tag : 'i',
21959                 cls : 'fa ' + this.icon
21960             })
21961         }
21962         
21963         var cfg = {
21964             tag : 'li',
21965             cn : [
21966                 {
21967                     tag : 'a',
21968                     href : this.href || '#',
21969                     cn : text
21970                 }
21971             ]
21972         };
21973         
21974         if(this.disable){
21975             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
21976         }
21977         
21978         if(this.submenu){
21979             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
21980             
21981             if(this.pos == 'left'){
21982                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
21983             }
21984         }
21985         
21986         return cfg;
21987     },
21988     
21989     initEvents : function() 
21990     {
21991         this.el.on('mouseover', this.onMouseOver, this);
21992         this.el.on('mouseout', this.onMouseOut, this);
21993         
21994         this.el.select('a', true).first().on('click', this.onClick, this);
21995         
21996     },
21997     
21998     onClick : function(e)
21999     {
22000         if(this.preventDefault){
22001             e.preventDefault();
22002         }
22003         
22004         this.fireEvent("click", this, e);
22005     },
22006     
22007     onMouseOver : function(e)
22008     {
22009         if(this.submenu && this.pos == 'left'){
22010             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
22011         }
22012         
22013         this.fireEvent("mouseover", this, e);
22014     },
22015     
22016     onMouseOut : function(e)
22017     {
22018         this.fireEvent("mouseout", this, e);
22019     }
22020 });
22021
22022  
22023
22024  /*
22025  * - LGPL
22026  *
22027  * menu separator
22028  * 
22029  */
22030 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22031
22032 /**
22033  * @class Roo.bootstrap.menu.Separator
22034  * @extends Roo.bootstrap.Component
22035  * Bootstrap Separator class
22036  * 
22037  * @constructor
22038  * Create a new Separator
22039  * @param {Object} config The config object
22040  */
22041
22042
22043 Roo.bootstrap.menu.Separator = function(config){
22044     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
22045 };
22046
22047 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
22048     
22049     getAutoCreate : function(){
22050         var cfg = {
22051             tag : 'li',
22052             cls: 'divider'
22053         };
22054         
22055         return cfg;
22056     }
22057    
22058 });
22059
22060  
22061
22062  /*
22063  * - LGPL
22064  *
22065  * Tooltip
22066  * 
22067  */
22068
22069 /**
22070  * @class Roo.bootstrap.Tooltip
22071  * Bootstrap Tooltip class
22072  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
22073  * to determine which dom element triggers the tooltip.
22074  * 
22075  * It needs to add support for additional attributes like tooltip-position
22076  * 
22077  * @constructor
22078  * Create a new Toolti
22079  * @param {Object} config The config object
22080  */
22081
22082 Roo.bootstrap.Tooltip = function(config){
22083     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
22084 };
22085
22086 Roo.apply(Roo.bootstrap.Tooltip, {
22087     /**
22088      * @function init initialize tooltip monitoring.
22089      * @static
22090      */
22091     currentEl : false,
22092     currentTip : false,
22093     currentRegion : false,
22094     
22095     //  init : delay?
22096     
22097     init : function()
22098     {
22099         Roo.get(document).on('mouseover', this.enter ,this);
22100         Roo.get(document).on('mouseout', this.leave, this);
22101          
22102         
22103         this.currentTip = new Roo.bootstrap.Tooltip();
22104     },
22105     
22106     enter : function(ev)
22107     {
22108         var dom = ev.getTarget();
22109         
22110         //Roo.log(['enter',dom]);
22111         var el = Roo.fly(dom);
22112         if (this.currentEl) {
22113             //Roo.log(dom);
22114             //Roo.log(this.currentEl);
22115             //Roo.log(this.currentEl.contains(dom));
22116             if (this.currentEl == el) {
22117                 return;
22118             }
22119             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
22120                 return;
22121             }
22122
22123         }
22124         
22125         
22126         
22127         if (this.currentTip.el) {
22128             this.currentTip.el.hide(); // force hiding...
22129         }    
22130         //Roo.log(ev);
22131         var bindEl = el;
22132         
22133         // you can not look for children, as if el is the body.. then everythign is the child..
22134         if (!el.attr('tooltip')) { //
22135             if (!el.select("[tooltip]").elements.length) {
22136                 return;
22137             }
22138             // is the mouse over this child...?
22139             bindEl = el.select("[tooltip]").first();
22140             var xy = ev.getXY();
22141             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
22142                 //Roo.log("not in region.");
22143                 return;
22144             }
22145             //Roo.log("child element over..");
22146             
22147         }
22148         this.currentEl = bindEl;
22149         this.currentTip.bind(bindEl);
22150         this.currentRegion = Roo.lib.Region.getRegion(dom);
22151         this.currentTip.enter();
22152         
22153     },
22154     leave : function(ev)
22155     {
22156         var dom = ev.getTarget();
22157         //Roo.log(['leave',dom]);
22158         if (!this.currentEl) {
22159             return;
22160         }
22161         
22162         
22163         if (dom != this.currentEl.dom) {
22164             return;
22165         }
22166         var xy = ev.getXY();
22167         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
22168             return;
22169         }
22170         // only activate leave if mouse cursor is outside... bounding box..
22171         
22172         
22173         
22174         
22175         if (this.currentTip) {
22176             this.currentTip.leave();
22177         }
22178         //Roo.log('clear currentEl');
22179         this.currentEl = false;
22180         
22181         
22182     },
22183     alignment : {
22184         'left' : ['r-l', [-2,0], 'right'],
22185         'right' : ['l-r', [2,0], 'left'],
22186         'bottom' : ['t-b', [0,2], 'top'],
22187         'top' : [ 'b-t', [0,-2], 'bottom']
22188     }
22189     
22190 });
22191
22192
22193 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
22194     
22195     
22196     bindEl : false,
22197     
22198     delay : null, // can be { show : 300 , hide: 500}
22199     
22200     timeout : null,
22201     
22202     hoverState : null, //???
22203     
22204     placement : 'bottom', 
22205     
22206     getAutoCreate : function(){
22207     
22208         var cfg = {
22209            cls : 'tooltip',
22210            role : 'tooltip',
22211            cn : [
22212                 {
22213                     cls : 'tooltip-arrow'
22214                 },
22215                 {
22216                     cls : 'tooltip-inner'
22217                 }
22218            ]
22219         };
22220         
22221         return cfg;
22222     },
22223     bind : function(el)
22224     {
22225         this.bindEl = el;
22226     },
22227       
22228     
22229     enter : function () {
22230        
22231         if (this.timeout != null) {
22232             clearTimeout(this.timeout);
22233         }
22234         
22235         this.hoverState = 'in';
22236          //Roo.log("enter - show");
22237         if (!this.delay || !this.delay.show) {
22238             this.show();
22239             return;
22240         }
22241         var _t = this;
22242         this.timeout = setTimeout(function () {
22243             if (_t.hoverState == 'in') {
22244                 _t.show();
22245             }
22246         }, this.delay.show);
22247     },
22248     leave : function()
22249     {
22250         clearTimeout(this.timeout);
22251     
22252         this.hoverState = 'out';
22253          if (!this.delay || !this.delay.hide) {
22254             this.hide();
22255             return;
22256         }
22257        
22258         var _t = this;
22259         this.timeout = setTimeout(function () {
22260             //Roo.log("leave - timeout");
22261             
22262             if (_t.hoverState == 'out') {
22263                 _t.hide();
22264                 Roo.bootstrap.Tooltip.currentEl = false;
22265             }
22266         }, delay);
22267     },
22268     
22269     show : function ()
22270     {
22271         if (!this.el) {
22272             this.render(document.body);
22273         }
22274         // set content.
22275         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
22276         
22277         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
22278         
22279         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
22280         
22281         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
22282         
22283         var placement = typeof this.placement == 'function' ?
22284             this.placement.call(this, this.el, on_el) :
22285             this.placement;
22286             
22287         var autoToken = /\s?auto?\s?/i;
22288         var autoPlace = autoToken.test(placement);
22289         if (autoPlace) {
22290             placement = placement.replace(autoToken, '') || 'top';
22291         }
22292         
22293         //this.el.detach()
22294         //this.el.setXY([0,0]);
22295         this.el.show();
22296         //this.el.dom.style.display='block';
22297         this.el.addClass(placement);
22298         
22299         //this.el.appendTo(on_el);
22300         
22301         var p = this.getPosition();
22302         var box = this.el.getBox();
22303         
22304         if (autoPlace) {
22305             // fixme..
22306         }
22307         var align = Roo.bootstrap.Tooltip.alignment[placement];
22308         this.el.alignTo(this.bindEl, align[0],align[1]);
22309         //var arrow = this.el.select('.arrow',true).first();
22310         //arrow.set(align[2], 
22311         
22312         this.el.addClass('in fade');
22313         this.hoverState = null;
22314         
22315         if (this.el.hasClass('fade')) {
22316             // fade it?
22317         }
22318         
22319     },
22320     hide : function()
22321     {
22322          
22323         if (!this.el) {
22324             return;
22325         }
22326         //this.el.setXY([0,0]);
22327         this.el.removeClass('in');
22328         //this.el.hide();
22329         
22330     }
22331     
22332 });
22333  
22334
22335  /*
22336  * - LGPL
22337  *
22338  * Location Picker
22339  * 
22340  */
22341
22342 /**
22343  * @class Roo.bootstrap.LocationPicker
22344  * @extends Roo.bootstrap.Component
22345  * Bootstrap LocationPicker class
22346  * @cfg {Number} latitude Position when init default 0
22347  * @cfg {Number} longitude Position when init default 0
22348  * @cfg {Number} zoom default 15
22349  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
22350  * @cfg {Boolean} mapTypeControl default false
22351  * @cfg {Boolean} disableDoubleClickZoom default false
22352  * @cfg {Boolean} scrollwheel default true
22353  * @cfg {Boolean} streetViewControl default false
22354  * @cfg {Number} radius default 0
22355  * @cfg {String} locationName
22356  * @cfg {Boolean} draggable default true
22357  * @cfg {Boolean} enableAutocomplete default false
22358  * @cfg {Boolean} enableReverseGeocode default true
22359  * @cfg {String} markerTitle
22360  * 
22361  * @constructor
22362  * Create a new LocationPicker
22363  * @param {Object} config The config object
22364  */
22365
22366
22367 Roo.bootstrap.LocationPicker = function(config){
22368     
22369     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
22370     
22371     this.addEvents({
22372         /**
22373          * @event initial
22374          * Fires when the picker initialized.
22375          * @param {Roo.bootstrap.LocationPicker} this
22376          * @param {Google Location} location
22377          */
22378         initial : true,
22379         /**
22380          * @event positionchanged
22381          * Fires when the picker position changed.
22382          * @param {Roo.bootstrap.LocationPicker} this
22383          * @param {Google Location} location
22384          */
22385         positionchanged : true,
22386         /**
22387          * @event resize
22388          * Fires when the map resize.
22389          * @param {Roo.bootstrap.LocationPicker} this
22390          */
22391         resize : true,
22392         /**
22393          * @event show
22394          * Fires when the map show.
22395          * @param {Roo.bootstrap.LocationPicker} this
22396          */
22397         show : true,
22398         /**
22399          * @event hide
22400          * Fires when the map hide.
22401          * @param {Roo.bootstrap.LocationPicker} this
22402          */
22403         hide : true,
22404         /**
22405          * @event mapClick
22406          * Fires when click the map.
22407          * @param {Roo.bootstrap.LocationPicker} this
22408          * @param {Map event} e
22409          */
22410         mapClick : true,
22411         /**
22412          * @event mapRightClick
22413          * Fires when right click the map.
22414          * @param {Roo.bootstrap.LocationPicker} this
22415          * @param {Map event} e
22416          */
22417         mapRightClick : true,
22418         /**
22419          * @event markerClick
22420          * Fires when click the marker.
22421          * @param {Roo.bootstrap.LocationPicker} this
22422          * @param {Map event} e
22423          */
22424         markerClick : true,
22425         /**
22426          * @event markerRightClick
22427          * Fires when right click the marker.
22428          * @param {Roo.bootstrap.LocationPicker} this
22429          * @param {Map event} e
22430          */
22431         markerRightClick : true,
22432         /**
22433          * @event OverlayViewDraw
22434          * Fires when OverlayView Draw
22435          * @param {Roo.bootstrap.LocationPicker} this
22436          */
22437         OverlayViewDraw : true,
22438         /**
22439          * @event OverlayViewOnAdd
22440          * Fires when OverlayView Draw
22441          * @param {Roo.bootstrap.LocationPicker} this
22442          */
22443         OverlayViewOnAdd : true,
22444         /**
22445          * @event OverlayViewOnRemove
22446          * Fires when OverlayView Draw
22447          * @param {Roo.bootstrap.LocationPicker} this
22448          */
22449         OverlayViewOnRemove : true,
22450         /**
22451          * @event OverlayViewShow
22452          * Fires when OverlayView Draw
22453          * @param {Roo.bootstrap.LocationPicker} this
22454          * @param {Pixel} cpx
22455          */
22456         OverlayViewShow : true,
22457         /**
22458          * @event OverlayViewHide
22459          * Fires when OverlayView Draw
22460          * @param {Roo.bootstrap.LocationPicker} this
22461          */
22462         OverlayViewHide : true
22463     });
22464         
22465 };
22466
22467 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
22468     
22469     gMapContext: false,
22470     
22471     latitude: 0,
22472     longitude: 0,
22473     zoom: 15,
22474     mapTypeId: false,
22475     mapTypeControl: false,
22476     disableDoubleClickZoom: false,
22477     scrollwheel: true,
22478     streetViewControl: false,
22479     radius: 0,
22480     locationName: '',
22481     draggable: true,
22482     enableAutocomplete: false,
22483     enableReverseGeocode: true,
22484     markerTitle: '',
22485     
22486     getAutoCreate: function()
22487     {
22488
22489         var cfg = {
22490             tag: 'div',
22491             cls: 'roo-location-picker'
22492         };
22493         
22494         return cfg
22495     },
22496     
22497     initEvents: function(ct, position)
22498     {       
22499         if(!this.el.getWidth() || this.isApplied()){
22500             return;
22501         }
22502         
22503         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22504         
22505         this.initial();
22506     },
22507     
22508     initial: function()
22509     {
22510         if(!this.mapTypeId){
22511             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
22512         }
22513         
22514         this.gMapContext = this.GMapContext();
22515         
22516         this.initOverlayView();
22517         
22518         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
22519         
22520         var _this = this;
22521                 
22522         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
22523             _this.setPosition(_this.gMapContext.marker.position);
22524         });
22525         
22526         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
22527             _this.fireEvent('mapClick', this, event);
22528             
22529         });
22530
22531         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
22532             _this.fireEvent('mapRightClick', this, event);
22533             
22534         });
22535         
22536         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
22537             _this.fireEvent('markerClick', this, event);
22538             
22539         });
22540
22541         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
22542             _this.fireEvent('markerRightClick', this, event);
22543             
22544         });
22545         
22546         this.setPosition(this.gMapContext.location);
22547         
22548         this.fireEvent('initial', this, this.gMapContext.location);
22549     },
22550     
22551     initOverlayView: function()
22552     {
22553         var _this = this;
22554         
22555         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
22556             
22557             draw: function()
22558             {
22559                 _this.fireEvent('OverlayViewDraw', _this);
22560             },
22561             
22562             onAdd: function()
22563             {
22564                 _this.fireEvent('OverlayViewOnAdd', _this);
22565             },
22566             
22567             onRemove: function()
22568             {
22569                 _this.fireEvent('OverlayViewOnRemove', _this);
22570             },
22571             
22572             show: function(cpx)
22573             {
22574                 _this.fireEvent('OverlayViewShow', _this, cpx);
22575             },
22576             
22577             hide: function()
22578             {
22579                 _this.fireEvent('OverlayViewHide', _this);
22580             }
22581             
22582         });
22583     },
22584     
22585     fromLatLngToContainerPixel: function(event)
22586     {
22587         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
22588     },
22589     
22590     isApplied: function() 
22591     {
22592         return this.getGmapContext() == false ? false : true;
22593     },
22594     
22595     getGmapContext: function() 
22596     {
22597         return this.gMapContext
22598     },
22599     
22600     GMapContext: function() 
22601     {
22602         var position = new google.maps.LatLng(this.latitude, this.longitude);
22603         
22604         var _map = new google.maps.Map(this.el.dom, {
22605             center: position,
22606             zoom: this.zoom,
22607             mapTypeId: this.mapTypeId,
22608             mapTypeControl: this.mapTypeControl,
22609             disableDoubleClickZoom: this.disableDoubleClickZoom,
22610             scrollwheel: this.scrollwheel,
22611             streetViewControl: this.streetViewControl,
22612             locationName: this.locationName,
22613             draggable: this.draggable,
22614             enableAutocomplete: this.enableAutocomplete,
22615             enableReverseGeocode: this.enableReverseGeocode
22616         });
22617         
22618         var _marker = new google.maps.Marker({
22619             position: position,
22620             map: _map,
22621             title: this.markerTitle,
22622             draggable: this.draggable
22623         });
22624         
22625         return {
22626             map: _map,
22627             marker: _marker,
22628             circle: null,
22629             location: position,
22630             radius: this.radius,
22631             locationName: this.locationName,
22632             addressComponents: {
22633                 formatted_address: null,
22634                 addressLine1: null,
22635                 addressLine2: null,
22636                 streetName: null,
22637                 streetNumber: null,
22638                 city: null,
22639                 district: null,
22640                 state: null,
22641                 stateOrProvince: null
22642             },
22643             settings: this,
22644             domContainer: this.el.dom,
22645             geodecoder: new google.maps.Geocoder()
22646         };
22647     },
22648     
22649     drawCircle: function(center, radius, options) 
22650     {
22651         if (this.gMapContext.circle != null) {
22652             this.gMapContext.circle.setMap(null);
22653         }
22654         if (radius > 0) {
22655             radius *= 1;
22656             options = Roo.apply({}, options, {
22657                 strokeColor: "#0000FF",
22658                 strokeOpacity: .35,
22659                 strokeWeight: 2,
22660                 fillColor: "#0000FF",
22661                 fillOpacity: .2
22662             });
22663             
22664             options.map = this.gMapContext.map;
22665             options.radius = radius;
22666             options.center = center;
22667             this.gMapContext.circle = new google.maps.Circle(options);
22668             return this.gMapContext.circle;
22669         }
22670         
22671         return null;
22672     },
22673     
22674     setPosition: function(location) 
22675     {
22676         this.gMapContext.location = location;
22677         this.gMapContext.marker.setPosition(location);
22678         this.gMapContext.map.panTo(location);
22679         this.drawCircle(location, this.gMapContext.radius, {});
22680         
22681         var _this = this;
22682         
22683         if (this.gMapContext.settings.enableReverseGeocode) {
22684             this.gMapContext.geodecoder.geocode({
22685                 latLng: this.gMapContext.location
22686             }, function(results, status) {
22687                 
22688                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
22689                     _this.gMapContext.locationName = results[0].formatted_address;
22690                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
22691                     
22692                     _this.fireEvent('positionchanged', this, location);
22693                 }
22694             });
22695             
22696             return;
22697         }
22698         
22699         this.fireEvent('positionchanged', this, location);
22700     },
22701     
22702     resize: function()
22703     {
22704         google.maps.event.trigger(this.gMapContext.map, "resize");
22705         
22706         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
22707         
22708         this.fireEvent('resize', this);
22709     },
22710     
22711     setPositionByLatLng: function(latitude, longitude)
22712     {
22713         this.setPosition(new google.maps.LatLng(latitude, longitude));
22714     },
22715     
22716     getCurrentPosition: function() 
22717     {
22718         return {
22719             latitude: this.gMapContext.location.lat(),
22720             longitude: this.gMapContext.location.lng()
22721         };
22722     },
22723     
22724     getAddressName: function() 
22725     {
22726         return this.gMapContext.locationName;
22727     },
22728     
22729     getAddressComponents: function() 
22730     {
22731         return this.gMapContext.addressComponents;
22732     },
22733     
22734     address_component_from_google_geocode: function(address_components) 
22735     {
22736         var result = {};
22737         
22738         for (var i = 0; i < address_components.length; i++) {
22739             var component = address_components[i];
22740             if (component.types.indexOf("postal_code") >= 0) {
22741                 result.postalCode = component.short_name;
22742             } else if (component.types.indexOf("street_number") >= 0) {
22743                 result.streetNumber = component.short_name;
22744             } else if (component.types.indexOf("route") >= 0) {
22745                 result.streetName = component.short_name;
22746             } else if (component.types.indexOf("neighborhood") >= 0) {
22747                 result.city = component.short_name;
22748             } else if (component.types.indexOf("locality") >= 0) {
22749                 result.city = component.short_name;
22750             } else if (component.types.indexOf("sublocality") >= 0) {
22751                 result.district = component.short_name;
22752             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
22753                 result.stateOrProvince = component.short_name;
22754             } else if (component.types.indexOf("country") >= 0) {
22755                 result.country = component.short_name;
22756             }
22757         }
22758         
22759         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
22760         result.addressLine2 = "";
22761         return result;
22762     },
22763     
22764     setZoomLevel: function(zoom)
22765     {
22766         this.gMapContext.map.setZoom(zoom);
22767     },
22768     
22769     show: function()
22770     {
22771         if(!this.el){
22772             return;
22773         }
22774         
22775         this.el.show();
22776         
22777         this.resize();
22778         
22779         this.fireEvent('show', this);
22780     },
22781     
22782     hide: function()
22783     {
22784         if(!this.el){
22785             return;
22786         }
22787         
22788         this.el.hide();
22789         
22790         this.fireEvent('hide', this);
22791     }
22792     
22793 });
22794
22795 Roo.apply(Roo.bootstrap.LocationPicker, {
22796     
22797     OverlayView : function(map, options)
22798     {
22799         options = options || {};
22800         
22801         this.setMap(map);
22802     }
22803     
22804     
22805 });/*
22806  * - LGPL
22807  *
22808  * Alert
22809  * 
22810  */
22811
22812 /**
22813  * @class Roo.bootstrap.Alert
22814  * @extends Roo.bootstrap.Component
22815  * Bootstrap Alert class
22816  * @cfg {String} title The title of alert
22817  * @cfg {String} html The content of alert
22818  * @cfg {String} weight (  success | info | warning | danger )
22819  * @cfg {String} faicon font-awesomeicon
22820  * 
22821  * @constructor
22822  * Create a new alert
22823  * @param {Object} config The config object
22824  */
22825
22826
22827 Roo.bootstrap.Alert = function(config){
22828     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
22829     
22830 };
22831
22832 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
22833     
22834     title: '',
22835     html: '',
22836     weight: false,
22837     faicon: false,
22838     
22839     getAutoCreate : function()
22840     {
22841         
22842         var cfg = {
22843             tag : 'div',
22844             cls : 'alert',
22845             cn : [
22846                 {
22847                     tag : 'i',
22848                     cls : 'roo-alert-icon'
22849                     
22850                 },
22851                 {
22852                     tag : 'b',
22853                     cls : 'roo-alert-title',
22854                     html : this.title
22855                 },
22856                 {
22857                     tag : 'span',
22858                     cls : 'roo-alert-text',
22859                     html : this.html
22860                 }
22861             ]
22862         };
22863         
22864         if(this.faicon){
22865             cfg.cn[0].cls += ' fa ' + this.faicon;
22866         }
22867         
22868         if(this.weight){
22869             cfg.cls += ' alert-' + this.weight;
22870         }
22871         
22872         return cfg;
22873     },
22874     
22875     initEvents: function() 
22876     {
22877         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22878     },
22879     
22880     setTitle : function(str)
22881     {
22882         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
22883     },
22884     
22885     setText : function(str)
22886     {
22887         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
22888     },
22889     
22890     setWeight : function(weight)
22891     {
22892         if(this.weight){
22893             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
22894         }
22895         
22896         this.weight = weight;
22897         
22898         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
22899     },
22900     
22901     setIcon : function(icon)
22902     {
22903         if(this.faicon){
22904             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
22905         }
22906         
22907         this.faicon = icon
22908         
22909         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
22910     },
22911     
22912     hide: function() 
22913     {
22914         this.el.hide();   
22915     },
22916     
22917     show: function() 
22918     {  
22919         this.el.show();   
22920     }
22921     
22922 });
22923
22924