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) {
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                 for (var i = 0; i < this.bullets; i++){
14678                     bullets.cn.push({
14679                         cls : 'bullet bullet-' + i
14680                     });
14681                 }
14682                 
14683                 bullets.cn.push({
14684                     cls : 'clear'
14685                 });
14686                 
14687                 cfg.cn[0].cn = bullets;
14688             }
14689         }
14690         
14691         return cfg;
14692     },
14693     
14694     initEvents:  function()
14695     {
14696         Roo.log('-------- init events on tab group ---------');
14697         
14698         var _this = this;
14699         
14700         if(this.bullets > 0){
14701             
14702             for (var i = 0; i < this.bullets; i++){
14703                 var bullet = this.el.select('.bullet-' + i, true).first();
14704                 
14705                 if(!bullet){
14706                     continue;
14707                 }
14708                 
14709                 bullet.on('click', (function(e, el, o, ii, t){
14710                     
14711                     e.preventDefault();
14712                     
14713                     _this.showPanel(ii);
14714                     
14715                     if(_this.autoslide && _this.slideFn){
14716                         clearInterval(_this.slideFn);
14717                         _this.slideFn = window.setInterval(function() {
14718                             _this.showPanelNext();
14719                         }, _this.timer);
14720                     }
14721                     
14722                 }).createDelegate(this, [i, bullet], true));
14723             }
14724         }
14725         
14726         if(this.autoslide){
14727             this.slideFn = window.setInterval(function() {
14728                 _this.showPanelNext();
14729             }, this.timer);
14730         }
14731     },
14732     
14733     getChildContainer : function()
14734     {
14735         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14736     },
14737     
14738     /**
14739     * register a Navigation item
14740     * @param {Roo.bootstrap.NavItem} the navitem to add
14741     */
14742     register : function(item)
14743     {
14744         this.tabs.push( item);
14745         item.navId = this.navId; // not really needed..
14746     
14747     },
14748     
14749     getActivePanel : function()
14750     {
14751         var r = false;
14752         Roo.each(this.tabs, function(t) {
14753             if (t.active) {
14754                 r = t;
14755                 return false;
14756             }
14757             return null;
14758         });
14759         return r;
14760         
14761     },
14762     getPanelByName : function(n)
14763     {
14764         var r = false;
14765         Roo.each(this.tabs, function(t) {
14766             if (t.tabId == n) {
14767                 r = t;
14768                 return false;
14769             }
14770             return null;
14771         });
14772         return r;
14773     },
14774     indexOfPanel : function(p)
14775     {
14776         var r = false;
14777         Roo.each(this.tabs, function(t,i) {
14778             if (t.tabId == p.tabId) {
14779                 r = i;
14780                 return false;
14781             }
14782             return null;
14783         });
14784         return r;
14785     },
14786     /**
14787      * show a specific panel
14788      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14789      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14790      */
14791     showPanel : function (pan)
14792     {
14793         if(this.transition){
14794             Roo.log("waiting for the transitionend");
14795             return;
14796         }
14797         
14798         if (typeof(pan) == 'number') {
14799             pan = this.tabs[pan];
14800         }
14801         if (typeof(pan) == 'string') {
14802             pan = this.getPanelByName(pan);
14803         }
14804         if (pan.tabId == this.getActivePanel().tabId) {
14805             return true;
14806         }
14807         var cur = this.getActivePanel();
14808         
14809         if (false === cur.fireEvent('beforedeactivate')) {
14810             return false;
14811         }
14812         
14813         if(this.bullets > 0){
14814             this.setActiveBullet(this.indexOfPanel(pan));
14815         }
14816         
14817         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
14818             
14819             this.transition = true;
14820             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14821             var lr = dir == 'next' ? 'left' : 'right';
14822             pan.el.addClass(dir); // or prev
14823             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14824             cur.el.addClass(lr); // or right
14825             pan.el.addClass(lr);
14826             
14827             var _this = this;
14828             cur.el.on('transitionend', function() {
14829                 Roo.log("trans end?");
14830                 
14831                 pan.el.removeClass([lr,dir]);
14832                 pan.setActive(true);
14833                 
14834                 cur.el.removeClass([lr]);
14835                 cur.setActive(false);
14836                 
14837                 _this.transition = false;
14838                 
14839             }, this, { single:  true } );
14840             
14841             return true;
14842         }
14843         
14844         cur.setActive(false);
14845         pan.setActive(true);
14846         
14847         return true;
14848         
14849     },
14850     showPanelNext : function()
14851     {
14852         var i = this.indexOfPanel(this.getActivePanel());
14853         
14854         if (i >= this.tabs.length - 1 && !this.autoslide) {
14855             return;
14856         }
14857         
14858         if (i >= this.tabs.length - 1 && this.autoslide) {
14859             i = -1;
14860         }
14861         
14862         this.showPanel(this.tabs[i+1]);
14863     },
14864     
14865     showPanelPrev : function()
14866     {
14867         var i = this.indexOfPanel(this.getActivePanel());
14868         
14869         if (i  < 1 && !this.autoslide) {
14870             return;
14871         }
14872         
14873         if (i < 1 && this.autoslide) {
14874             i = this.tabs.length;
14875         }
14876         
14877         this.showPanel(this.tabs[i-1]);
14878     },
14879     
14880     setActiveBullet : function(i)
14881     {
14882         Roo.each(this.el.select('.bullet', true).elements, function(el){
14883             el.removeClass('selected');
14884         });
14885
14886         var bullet = this.el.select('.bullet-' + i, true).first();
14887         
14888         if(!bullet){
14889             return;
14890         }
14891         
14892         bullet.addClass('selected');
14893     }
14894     
14895     
14896   
14897 });
14898
14899  
14900
14901  
14902  
14903 Roo.apply(Roo.bootstrap.TabGroup, {
14904     
14905     groups: {},
14906      /**
14907     * register a Navigation Group
14908     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14909     */
14910     register : function(navgrp)
14911     {
14912         this.groups[navgrp.navId] = navgrp;
14913         
14914     },
14915     /**
14916     * fetch a Navigation Group based on the navigation ID
14917     * if one does not exist , it will get created.
14918     * @param {string} the navgroup to add
14919     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14920     */
14921     get: function(navId) {
14922         if (typeof(this.groups[navId]) == 'undefined') {
14923             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14924         }
14925         return this.groups[navId] ;
14926     }
14927     
14928     
14929     
14930 });
14931
14932  /*
14933  * - LGPL
14934  *
14935  * TabPanel
14936  * 
14937  */
14938
14939 /**
14940  * @class Roo.bootstrap.TabPanel
14941  * @extends Roo.bootstrap.Component
14942  * Bootstrap TabPanel class
14943  * @cfg {Boolean} active panel active
14944  * @cfg {String} html panel content
14945  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14946  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14947  * 
14948  * 
14949  * @constructor
14950  * Create a new TabPanel
14951  * @param {Object} config The config object
14952  */
14953
14954 Roo.bootstrap.TabPanel = function(config){
14955     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14956     this.addEvents({
14957         /**
14958              * @event changed
14959              * Fires when the active status changes
14960              * @param {Roo.bootstrap.TabPanel} this
14961              * @param {Boolean} state the new state
14962             
14963          */
14964         'changed': true,
14965         /**
14966              * @event beforedeactivate
14967              * Fires before a tab is de-activated - can be used to do validation on a form.
14968              * @param {Roo.bootstrap.TabPanel} this
14969              * @return {Boolean} false if there is an error
14970             
14971          */
14972         'beforedeactivate': true
14973      });
14974     
14975     this.tabId = this.tabId || Roo.id();
14976   
14977 };
14978
14979 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14980     
14981     active: false,
14982     html: false,
14983     tabId: false,
14984     navId : false,
14985     
14986     getAutoCreate : function(){
14987         var cfg = {
14988             tag: 'div',
14989             // item is needed for carousel - not sure if it has any effect otherwise
14990             cls: 'tab-pane item',
14991             html: this.html || ''
14992         };
14993         
14994         if(this.active){
14995             cfg.cls += ' active';
14996         }
14997         
14998         if(this.tabId){
14999             cfg.tabId = this.tabId;
15000         }
15001         
15002         
15003         return cfg;
15004     },
15005     
15006     initEvents:  function()
15007     {
15008         Roo.log('-------- init events on tab panel ---------');
15009         
15010         var p = this.parent();
15011         this.navId = this.navId || p.navId;
15012         
15013         if (typeof(this.navId) != 'undefined') {
15014             // not really needed.. but just in case.. parent should be a NavGroup.
15015             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15016             Roo.log(['register', tg, this]);
15017             tg.register(this);
15018             
15019             var i = tg.tabs.length - 1;
15020             
15021             if(this.active && tg.bullets > 0 && i < tg.bullets){
15022                 tg.setActiveBullet(i);
15023             }
15024         }
15025         
15026     },
15027     
15028     
15029     onRender : function(ct, position)
15030     {
15031        // Roo.log("Call onRender: " + this.xtype);
15032         
15033         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
15034         
15035         
15036         
15037         
15038         
15039     },
15040     
15041     setActive: function(state)
15042     {
15043         Roo.log("panel - set active " + this.tabId + "=" + state);
15044         
15045         this.active = state;
15046         if (!state) {
15047             this.el.removeClass('active');
15048             
15049         } else  if (!this.el.hasClass('active')) {
15050             this.el.addClass('active');
15051         }
15052         
15053         this.fireEvent('changed', this, state);
15054     }
15055     
15056     
15057 });
15058  
15059
15060  
15061
15062  /*
15063  * - LGPL
15064  *
15065  * DateField
15066  * 
15067  */
15068
15069 /**
15070  * @class Roo.bootstrap.DateField
15071  * @extends Roo.bootstrap.Input
15072  * Bootstrap DateField class
15073  * @cfg {Number} weekStart default 0
15074  * @cfg {String} viewMode default empty, (months|years)
15075  * @cfg {String} minViewMode default empty, (months|years)
15076  * @cfg {Number} startDate default -Infinity
15077  * @cfg {Number} endDate default Infinity
15078  * @cfg {Boolean} todayHighlight default false
15079  * @cfg {Boolean} todayBtn default false
15080  * @cfg {Boolean} calendarWeeks default false
15081  * @cfg {Object} daysOfWeekDisabled default empty
15082  * @cfg {Boolean} singleMode default false (true | false)
15083  * 
15084  * @cfg {Boolean} keyboardNavigation default true
15085  * @cfg {String} language default en
15086  * 
15087  * @constructor
15088  * Create a new DateField
15089  * @param {Object} config The config object
15090  */
15091
15092 Roo.bootstrap.DateField = function(config){
15093     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
15094      this.addEvents({
15095             /**
15096              * @event show
15097              * Fires when this field show.
15098              * @param {Roo.bootstrap.DateField} this
15099              * @param {Mixed} date The date value
15100              */
15101             show : true,
15102             /**
15103              * @event show
15104              * Fires when this field hide.
15105              * @param {Roo.bootstrap.DateField} this
15106              * @param {Mixed} date The date value
15107              */
15108             hide : true,
15109             /**
15110              * @event select
15111              * Fires when select a date.
15112              * @param {Roo.bootstrap.DateField} this
15113              * @param {Mixed} date The date value
15114              */
15115             select : true
15116         });
15117 };
15118
15119 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
15120     
15121     /**
15122      * @cfg {String} format
15123      * The default date format string which can be overriden for localization support.  The format must be
15124      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
15125      */
15126     format : "m/d/y",
15127     /**
15128      * @cfg {String} altFormats
15129      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
15130      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
15131      */
15132     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
15133     
15134     weekStart : 0,
15135     
15136     viewMode : '',
15137     
15138     minViewMode : '',
15139     
15140     todayHighlight : false,
15141     
15142     todayBtn: false,
15143     
15144     language: 'en',
15145     
15146     keyboardNavigation: true,
15147     
15148     calendarWeeks: false,
15149     
15150     startDate: -Infinity,
15151     
15152     endDate: Infinity,
15153     
15154     daysOfWeekDisabled: [],
15155     
15156     _events: [],
15157     
15158     singleMode : false,
15159     
15160     UTCDate: function()
15161     {
15162         return new Date(Date.UTC.apply(Date, arguments));
15163     },
15164     
15165     UTCToday: function()
15166     {
15167         var today = new Date();
15168         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
15169     },
15170     
15171     getDate: function() {
15172             var d = this.getUTCDate();
15173             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
15174     },
15175     
15176     getUTCDate: function() {
15177             return this.date;
15178     },
15179     
15180     setDate: function(d) {
15181             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
15182     },
15183     
15184     setUTCDate: function(d) {
15185             this.date = d;
15186             this.setValue(this.formatDate(this.date));
15187     },
15188         
15189     onRender: function(ct, position)
15190     {
15191         
15192         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
15193         
15194         this.language = this.language || 'en';
15195         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
15196         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
15197         
15198         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
15199         this.format = this.format || 'm/d/y';
15200         this.isInline = false;
15201         this.isInput = true;
15202         this.component = this.el.select('.add-on', true).first() || false;
15203         this.component = (this.component && this.component.length === 0) ? false : this.component;
15204         this.hasInput = this.component && this.inputEL().length;
15205         
15206         if (typeof(this.minViewMode === 'string')) {
15207             switch (this.minViewMode) {
15208                 case 'months':
15209                     this.minViewMode = 1;
15210                     break;
15211                 case 'years':
15212                     this.minViewMode = 2;
15213                     break;
15214                 default:
15215                     this.minViewMode = 0;
15216                     break;
15217             }
15218         }
15219         
15220         if (typeof(this.viewMode === 'string')) {
15221             switch (this.viewMode) {
15222                 case 'months':
15223                     this.viewMode = 1;
15224                     break;
15225                 case 'years':
15226                     this.viewMode = 2;
15227                     break;
15228                 default:
15229                     this.viewMode = 0;
15230                     break;
15231             }
15232         }
15233                 
15234         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
15235         
15236 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
15237         
15238         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15239         
15240         this.picker().on('mousedown', this.onMousedown, this);
15241         this.picker().on('click', this.onClick, this);
15242         
15243         this.picker().addClass('datepicker-dropdown');
15244         
15245         this.startViewMode = this.viewMode;
15246         
15247         if(this.singleMode){
15248             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
15249                 v.setVisibilityMode(Roo.Element.DISPLAY)
15250                 v.hide();
15251             });
15252             
15253             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
15254                 v.setStyle('width', '189px');
15255             });
15256         }
15257         
15258         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
15259             if(!this.calendarWeeks){
15260                 v.remove();
15261                 return;
15262             }
15263             
15264             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15265             v.attr('colspan', function(i, val){
15266                 return parseInt(val) + 1;
15267             });
15268         })
15269                         
15270         
15271         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
15272         
15273         this.setStartDate(this.startDate);
15274         this.setEndDate(this.endDate);
15275         
15276         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
15277         
15278         this.fillDow();
15279         this.fillMonths();
15280         this.update();
15281         this.showMode();
15282         
15283         if(this.isInline) {
15284             this.show();
15285         }
15286     },
15287     
15288     picker : function()
15289     {
15290         return this.pickerEl;
15291 //        return this.el.select('.datepicker', true).first();
15292     },
15293     
15294     fillDow: function()
15295     {
15296         var dowCnt = this.weekStart;
15297         
15298         var dow = {
15299             tag: 'tr',
15300             cn: [
15301                 
15302             ]
15303         };
15304         
15305         if(this.calendarWeeks){
15306             dow.cn.push({
15307                 tag: 'th',
15308                 cls: 'cw',
15309                 html: '&nbsp;'
15310             })
15311         }
15312         
15313         while (dowCnt < this.weekStart + 7) {
15314             dow.cn.push({
15315                 tag: 'th',
15316                 cls: 'dow',
15317                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
15318             });
15319         }
15320         
15321         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
15322     },
15323     
15324     fillMonths: function()
15325     {    
15326         var i = 0;
15327         var months = this.picker().select('>.datepicker-months td', true).first();
15328         
15329         months.dom.innerHTML = '';
15330         
15331         while (i < 12) {
15332             var month = {
15333                 tag: 'span',
15334                 cls: 'month',
15335                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
15336             }
15337             
15338             months.createChild(month);
15339         }
15340         
15341     },
15342     
15343     update: function()
15344     {
15345         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;
15346         
15347         if (this.date < this.startDate) {
15348             this.viewDate = new Date(this.startDate);
15349         } else if (this.date > this.endDate) {
15350             this.viewDate = new Date(this.endDate);
15351         } else {
15352             this.viewDate = new Date(this.date);
15353         }
15354         
15355         this.fill();
15356     },
15357     
15358     fill: function() 
15359     {
15360         var d = new Date(this.viewDate),
15361                 year = d.getUTCFullYear(),
15362                 month = d.getUTCMonth(),
15363                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
15364                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
15365                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
15366                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
15367                 currentDate = this.date && this.date.valueOf(),
15368                 today = this.UTCToday();
15369         
15370         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
15371         
15372 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15373         
15374 //        this.picker.select('>tfoot th.today').
15375 //                                              .text(dates[this.language].today)
15376 //                                              .toggle(this.todayBtn !== false);
15377     
15378         this.updateNavArrows();
15379         this.fillMonths();
15380                                                 
15381         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
15382         
15383         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
15384          
15385         prevMonth.setUTCDate(day);
15386         
15387         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
15388         
15389         var nextMonth = new Date(prevMonth);
15390         
15391         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
15392         
15393         nextMonth = nextMonth.valueOf();
15394         
15395         var fillMonths = false;
15396         
15397         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
15398         
15399         while(prevMonth.valueOf() < nextMonth) {
15400             var clsName = '';
15401             
15402             if (prevMonth.getUTCDay() === this.weekStart) {
15403                 if(fillMonths){
15404                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
15405                 }
15406                     
15407                 fillMonths = {
15408                     tag: 'tr',
15409                     cn: []
15410                 };
15411                 
15412                 if(this.calendarWeeks){
15413                     // ISO 8601: First week contains first thursday.
15414                     // ISO also states week starts on Monday, but we can be more abstract here.
15415                     var
15416                     // Start of current week: based on weekstart/current date
15417                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
15418                     // Thursday of this week
15419                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
15420                     // First Thursday of year, year from thursday
15421                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
15422                     // Calendar week: ms between thursdays, div ms per day, div 7 days
15423                     calWeek =  (th - yth) / 864e5 / 7 + 1;
15424                     
15425                     fillMonths.cn.push({
15426                         tag: 'td',
15427                         cls: 'cw',
15428                         html: calWeek
15429                     });
15430                 }
15431             }
15432             
15433             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
15434                 clsName += ' old';
15435             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
15436                 clsName += ' new';
15437             }
15438             if (this.todayHighlight &&
15439                 prevMonth.getUTCFullYear() == today.getFullYear() &&
15440                 prevMonth.getUTCMonth() == today.getMonth() &&
15441                 prevMonth.getUTCDate() == today.getDate()) {
15442                 clsName += ' today';
15443             }
15444             
15445             if (currentDate && prevMonth.valueOf() === currentDate) {
15446                 clsName += ' active';
15447             }
15448             
15449             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
15450                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
15451                     clsName += ' disabled';
15452             }
15453             
15454             fillMonths.cn.push({
15455                 tag: 'td',
15456                 cls: 'day ' + clsName,
15457                 html: prevMonth.getDate()
15458             })
15459             
15460             prevMonth.setDate(prevMonth.getDate()+1);
15461         }
15462           
15463         var currentYear = this.date && this.date.getUTCFullYear();
15464         var currentMonth = this.date && this.date.getUTCMonth();
15465         
15466         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
15467         
15468         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
15469             v.removeClass('active');
15470             
15471             if(currentYear === year && k === currentMonth){
15472                 v.addClass('active');
15473             }
15474             
15475             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
15476                 v.addClass('disabled');
15477             }
15478             
15479         });
15480         
15481         
15482         year = parseInt(year/10, 10) * 10;
15483         
15484         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
15485         
15486         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
15487         
15488         year -= 1;
15489         for (var i = -1; i < 11; i++) {
15490             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
15491                 tag: 'span',
15492                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
15493                 html: year
15494             })
15495             
15496             year += 1;
15497         }
15498     },
15499     
15500     showMode: function(dir) 
15501     {
15502         if (dir) {
15503             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15504         }
15505         
15506         Roo.each(this.picker().select('>div',true).elements, function(v){
15507             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15508             v.hide();
15509         });
15510         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15511     },
15512     
15513     place: function()
15514     {
15515         if(this.isInline) return;
15516         
15517         this.picker().removeClass(['bottom', 'top']);
15518         
15519         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15520             /*
15521              * place to the top of element!
15522              *
15523              */
15524             
15525             this.picker().addClass('top');
15526             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15527             
15528             return;
15529         }
15530         
15531         this.picker().addClass('bottom');
15532         
15533         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15534     },
15535     
15536     parseDate : function(value)
15537     {
15538         if(!value || value instanceof Date){
15539             return value;
15540         }
15541         var v = Date.parseDate(value, this.format);
15542         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15543             v = Date.parseDate(value, 'Y-m-d');
15544         }
15545         if(!v && this.altFormats){
15546             if(!this.altFormatsArray){
15547                 this.altFormatsArray = this.altFormats.split("|");
15548             }
15549             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15550                 v = Date.parseDate(value, this.altFormatsArray[i]);
15551             }
15552         }
15553         return v;
15554     },
15555     
15556     formatDate : function(date, fmt)
15557     {   
15558         return (!date || !(date instanceof Date)) ?
15559         date : date.dateFormat(fmt || this.format);
15560     },
15561     
15562     onFocus : function()
15563     {
15564         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15565         this.show();
15566     },
15567     
15568     onBlur : function()
15569     {
15570         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15571         
15572         var d = this.inputEl().getValue();
15573         
15574         this.setValue(d);
15575                 
15576         this.hide();
15577     },
15578     
15579     show : function()
15580     {
15581         this.picker().show();
15582         this.update();
15583         this.place();
15584         
15585         this.fireEvent('show', this, this.date);
15586     },
15587     
15588     hide : function()
15589     {
15590         if(this.isInline) return;
15591         this.picker().hide();
15592         this.viewMode = this.startViewMode;
15593         this.showMode();
15594         
15595         this.fireEvent('hide', this, this.date);
15596         
15597     },
15598     
15599     onMousedown: function(e)
15600     {
15601         e.stopPropagation();
15602         e.preventDefault();
15603     },
15604     
15605     keyup: function(e)
15606     {
15607         Roo.bootstrap.DateField.superclass.keyup.call(this);
15608         this.update();
15609     },
15610
15611     setValue: function(v)
15612     {
15613         
15614         // v can be a string or a date..
15615         
15616         
15617         var d = new Date(this.parseDate(v) ).clearTime();
15618         
15619         if(isNaN(d.getTime())){
15620             this.date = this.viewDate = '';
15621             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15622             return;
15623         }
15624         
15625         v = this.formatDate(d);
15626         
15627         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15628         
15629         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15630      
15631         this.update();
15632
15633         this.fireEvent('select', this, this.date);
15634         
15635     },
15636     
15637     getValue: function()
15638     {
15639         return this.formatDate(this.date);
15640     },
15641     
15642     fireKey: function(e)
15643     {
15644         if (!this.picker().isVisible()){
15645             if (e.keyCode == 27) // allow escape to hide and re-show picker
15646                 this.show();
15647             return;
15648         }
15649         
15650         var dateChanged = false,
15651         dir, day, month,
15652         newDate, newViewDate;
15653         
15654         switch(e.keyCode){
15655             case 27: // escape
15656                 this.hide();
15657                 e.preventDefault();
15658                 break;
15659             case 37: // left
15660             case 39: // right
15661                 if (!this.keyboardNavigation) break;
15662                 dir = e.keyCode == 37 ? -1 : 1;
15663                 
15664                 if (e.ctrlKey){
15665                     newDate = this.moveYear(this.date, dir);
15666                     newViewDate = this.moveYear(this.viewDate, dir);
15667                 } else if (e.shiftKey){
15668                     newDate = this.moveMonth(this.date, dir);
15669                     newViewDate = this.moveMonth(this.viewDate, dir);
15670                 } else {
15671                     newDate = new Date(this.date);
15672                     newDate.setUTCDate(this.date.getUTCDate() + dir);
15673                     newViewDate = new Date(this.viewDate);
15674                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15675                 }
15676                 if (this.dateWithinRange(newDate)){
15677                     this.date = newDate;
15678                     this.viewDate = newViewDate;
15679                     this.setValue(this.formatDate(this.date));
15680 //                    this.update();
15681                     e.preventDefault();
15682                     dateChanged = true;
15683                 }
15684                 break;
15685             case 38: // up
15686             case 40: // down
15687                 if (!this.keyboardNavigation) break;
15688                 dir = e.keyCode == 38 ? -1 : 1;
15689                 if (e.ctrlKey){
15690                     newDate = this.moveYear(this.date, dir);
15691                     newViewDate = this.moveYear(this.viewDate, dir);
15692                 } else if (e.shiftKey){
15693                     newDate = this.moveMonth(this.date, dir);
15694                     newViewDate = this.moveMonth(this.viewDate, dir);
15695                 } else {
15696                     newDate = new Date(this.date);
15697                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15698                     newViewDate = new Date(this.viewDate);
15699                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15700                 }
15701                 if (this.dateWithinRange(newDate)){
15702                     this.date = newDate;
15703                     this.viewDate = newViewDate;
15704                     this.setValue(this.formatDate(this.date));
15705 //                    this.update();
15706                     e.preventDefault();
15707                     dateChanged = true;
15708                 }
15709                 break;
15710             case 13: // enter
15711                 this.setValue(this.formatDate(this.date));
15712                 this.hide();
15713                 e.preventDefault();
15714                 break;
15715             case 9: // tab
15716                 this.setValue(this.formatDate(this.date));
15717                 this.hide();
15718                 break;
15719             case 16: // shift
15720             case 17: // ctrl
15721             case 18: // alt
15722                 break;
15723             default :
15724                 this.hide();
15725                 
15726         }
15727     },
15728     
15729     
15730     onClick: function(e) 
15731     {
15732         e.stopPropagation();
15733         e.preventDefault();
15734         
15735         var target = e.getTarget();
15736         
15737         if(target.nodeName.toLowerCase() === 'i'){
15738             target = Roo.get(target).dom.parentNode;
15739         }
15740         
15741         var nodeName = target.nodeName;
15742         var className = target.className;
15743         var html = target.innerHTML;
15744         //Roo.log(nodeName);
15745         
15746         switch(nodeName.toLowerCase()) {
15747             case 'th':
15748                 switch(className) {
15749                     case 'switch':
15750                         this.showMode(1);
15751                         break;
15752                     case 'prev':
15753                     case 'next':
15754                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15755                         switch(this.viewMode){
15756                                 case 0:
15757                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15758                                         break;
15759                                 case 1:
15760                                 case 2:
15761                                         this.viewDate = this.moveYear(this.viewDate, dir);
15762                                         break;
15763                         }
15764                         this.fill();
15765                         break;
15766                     case 'today':
15767                         var date = new Date();
15768                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15769 //                        this.fill()
15770                         this.setValue(this.formatDate(this.date));
15771                         
15772                         this.hide();
15773                         break;
15774                 }
15775                 break;
15776             case 'span':
15777                 if (className.indexOf('disabled') < 0) {
15778                     this.viewDate.setUTCDate(1);
15779                     if (className.indexOf('month') > -1) {
15780                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15781                     } else {
15782                         var year = parseInt(html, 10) || 0;
15783                         this.viewDate.setUTCFullYear(year);
15784                         
15785                     }
15786                     
15787                     if(this.singleMode){
15788                         this.setValue(this.formatDate(this.viewDate));
15789                         this.hide();
15790                         return;
15791                     }
15792                     
15793                     this.showMode(-1);
15794                     this.fill();
15795                 }
15796                 break;
15797                 
15798             case 'td':
15799                 //Roo.log(className);
15800                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15801                     var day = parseInt(html, 10) || 1;
15802                     var year = this.viewDate.getUTCFullYear(),
15803                         month = this.viewDate.getUTCMonth();
15804
15805                     if (className.indexOf('old') > -1) {
15806                         if(month === 0 ){
15807                             month = 11;
15808                             year -= 1;
15809                         }else{
15810                             month -= 1;
15811                         }
15812                     } else if (className.indexOf('new') > -1) {
15813                         if (month == 11) {
15814                             month = 0;
15815                             year += 1;
15816                         } else {
15817                             month += 1;
15818                         }
15819                     }
15820                     //Roo.log([year,month,day]);
15821                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15822                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15823 //                    this.fill();
15824                     //Roo.log(this.formatDate(this.date));
15825                     this.setValue(this.formatDate(this.date));
15826                     this.hide();
15827                 }
15828                 break;
15829         }
15830     },
15831     
15832     setStartDate: function(startDate)
15833     {
15834         this.startDate = startDate || -Infinity;
15835         if (this.startDate !== -Infinity) {
15836             this.startDate = this.parseDate(this.startDate);
15837         }
15838         this.update();
15839         this.updateNavArrows();
15840     },
15841
15842     setEndDate: function(endDate)
15843     {
15844         this.endDate = endDate || Infinity;
15845         if (this.endDate !== Infinity) {
15846             this.endDate = this.parseDate(this.endDate);
15847         }
15848         this.update();
15849         this.updateNavArrows();
15850     },
15851     
15852     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15853     {
15854         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15855         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15856             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15857         }
15858         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15859             return parseInt(d, 10);
15860         });
15861         this.update();
15862         this.updateNavArrows();
15863     },
15864     
15865     updateNavArrows: function() 
15866     {
15867         if(this.singleMode){
15868             return;
15869         }
15870         
15871         var d = new Date(this.viewDate),
15872         year = d.getUTCFullYear(),
15873         month = d.getUTCMonth();
15874         
15875         Roo.each(this.picker().select('.prev', true).elements, function(v){
15876             v.show();
15877             switch (this.viewMode) {
15878                 case 0:
15879
15880                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15881                         v.hide();
15882                     }
15883                     break;
15884                 case 1:
15885                 case 2:
15886                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15887                         v.hide();
15888                     }
15889                     break;
15890             }
15891         });
15892         
15893         Roo.each(this.picker().select('.next', true).elements, function(v){
15894             v.show();
15895             switch (this.viewMode) {
15896                 case 0:
15897
15898                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15899                         v.hide();
15900                     }
15901                     break;
15902                 case 1:
15903                 case 2:
15904                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15905                         v.hide();
15906                     }
15907                     break;
15908             }
15909         })
15910     },
15911     
15912     moveMonth: function(date, dir)
15913     {
15914         if (!dir) return date;
15915         var new_date = new Date(date.valueOf()),
15916         day = new_date.getUTCDate(),
15917         month = new_date.getUTCMonth(),
15918         mag = Math.abs(dir),
15919         new_month, test;
15920         dir = dir > 0 ? 1 : -1;
15921         if (mag == 1){
15922             test = dir == -1
15923             // If going back one month, make sure month is not current month
15924             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15925             ? function(){
15926                 return new_date.getUTCMonth() == month;
15927             }
15928             // If going forward one month, make sure month is as expected
15929             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15930             : function(){
15931                 return new_date.getUTCMonth() != new_month;
15932             };
15933             new_month = month + dir;
15934             new_date.setUTCMonth(new_month);
15935             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15936             if (new_month < 0 || new_month > 11)
15937                 new_month = (new_month + 12) % 12;
15938         } else {
15939             // For magnitudes >1, move one month at a time...
15940             for (var i=0; i<mag; i++)
15941                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15942                 new_date = this.moveMonth(new_date, dir);
15943             // ...then reset the day, keeping it in the new month
15944             new_month = new_date.getUTCMonth();
15945             new_date.setUTCDate(day);
15946             test = function(){
15947                 return new_month != new_date.getUTCMonth();
15948             };
15949         }
15950         // Common date-resetting loop -- if date is beyond end of month, make it
15951         // end of month
15952         while (test()){
15953             new_date.setUTCDate(--day);
15954             new_date.setUTCMonth(new_month);
15955         }
15956         return new_date;
15957     },
15958
15959     moveYear: function(date, dir)
15960     {
15961         return this.moveMonth(date, dir*12);
15962     },
15963
15964     dateWithinRange: function(date)
15965     {
15966         return date >= this.startDate && date <= this.endDate;
15967     },
15968
15969     
15970     remove: function() 
15971     {
15972         this.picker().remove();
15973     }
15974    
15975 });
15976
15977 Roo.apply(Roo.bootstrap.DateField,  {
15978     
15979     head : {
15980         tag: 'thead',
15981         cn: [
15982         {
15983             tag: 'tr',
15984             cn: [
15985             {
15986                 tag: 'th',
15987                 cls: 'prev',
15988                 html: '<i class="fa fa-arrow-left"/>'
15989             },
15990             {
15991                 tag: 'th',
15992                 cls: 'switch',
15993                 colspan: '5'
15994             },
15995             {
15996                 tag: 'th',
15997                 cls: 'next',
15998                 html: '<i class="fa fa-arrow-right"/>'
15999             }
16000
16001             ]
16002         }
16003         ]
16004     },
16005     
16006     content : {
16007         tag: 'tbody',
16008         cn: [
16009         {
16010             tag: 'tr',
16011             cn: [
16012             {
16013                 tag: 'td',
16014                 colspan: '7'
16015             }
16016             ]
16017         }
16018         ]
16019     },
16020     
16021     footer : {
16022         tag: 'tfoot',
16023         cn: [
16024         {
16025             tag: 'tr',
16026             cn: [
16027             {
16028                 tag: 'th',
16029                 colspan: '7',
16030                 cls: 'today'
16031             }
16032                     
16033             ]
16034         }
16035         ]
16036     },
16037     
16038     dates:{
16039         en: {
16040             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
16041             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
16042             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
16043             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16044             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
16045             today: "Today"
16046         }
16047     },
16048     
16049     modes: [
16050     {
16051         clsName: 'days',
16052         navFnc: 'Month',
16053         navStep: 1
16054     },
16055     {
16056         clsName: 'months',
16057         navFnc: 'FullYear',
16058         navStep: 1
16059     },
16060     {
16061         clsName: 'years',
16062         navFnc: 'FullYear',
16063         navStep: 10
16064     }]
16065 });
16066
16067 Roo.apply(Roo.bootstrap.DateField,  {
16068   
16069     template : {
16070         tag: 'div',
16071         cls: 'datepicker dropdown-menu roo-dynamic',
16072         cn: [
16073         {
16074             tag: 'div',
16075             cls: 'datepicker-days',
16076             cn: [
16077             {
16078                 tag: 'table',
16079                 cls: 'table-condensed',
16080                 cn:[
16081                 Roo.bootstrap.DateField.head,
16082                 {
16083                     tag: 'tbody'
16084                 },
16085                 Roo.bootstrap.DateField.footer
16086                 ]
16087             }
16088             ]
16089         },
16090         {
16091             tag: 'div',
16092             cls: 'datepicker-months',
16093             cn: [
16094             {
16095                 tag: 'table',
16096                 cls: 'table-condensed',
16097                 cn:[
16098                 Roo.bootstrap.DateField.head,
16099                 Roo.bootstrap.DateField.content,
16100                 Roo.bootstrap.DateField.footer
16101                 ]
16102             }
16103             ]
16104         },
16105         {
16106             tag: 'div',
16107             cls: 'datepicker-years',
16108             cn: [
16109             {
16110                 tag: 'table',
16111                 cls: 'table-condensed',
16112                 cn:[
16113                 Roo.bootstrap.DateField.head,
16114                 Roo.bootstrap.DateField.content,
16115                 Roo.bootstrap.DateField.footer
16116                 ]
16117             }
16118             ]
16119         }
16120         ]
16121     }
16122 });
16123
16124  
16125
16126  /*
16127  * - LGPL
16128  *
16129  * TimeField
16130  * 
16131  */
16132
16133 /**
16134  * @class Roo.bootstrap.TimeField
16135  * @extends Roo.bootstrap.Input
16136  * Bootstrap DateField class
16137  * 
16138  * 
16139  * @constructor
16140  * Create a new TimeField
16141  * @param {Object} config The config object
16142  */
16143
16144 Roo.bootstrap.TimeField = function(config){
16145     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
16146     this.addEvents({
16147             /**
16148              * @event show
16149              * Fires when this field show.
16150              * @param {Roo.bootstrap.DateField} thisthis
16151              * @param {Mixed} date The date value
16152              */
16153             show : true,
16154             /**
16155              * @event show
16156              * Fires when this field hide.
16157              * @param {Roo.bootstrap.DateField} this
16158              * @param {Mixed} date The date value
16159              */
16160             hide : true,
16161             /**
16162              * @event select
16163              * Fires when select a date.
16164              * @param {Roo.bootstrap.DateField} this
16165              * @param {Mixed} date The date value
16166              */
16167             select : true
16168         });
16169 };
16170
16171 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
16172     
16173     /**
16174      * @cfg {String} format
16175      * The default time format string which can be overriden for localization support.  The format must be
16176      * valid according to {@link Date#parseDate} (defaults to 'H:i').
16177      */
16178     format : "H:i",
16179        
16180     onRender: function(ct, position)
16181     {
16182         
16183         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
16184                 
16185         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
16186         
16187         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16188         
16189         this.pop = this.picker().select('>.datepicker-time',true).first();
16190         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16191         
16192         this.picker().on('mousedown', this.onMousedown, this);
16193         this.picker().on('click', this.onClick, this);
16194         
16195         this.picker().addClass('datepicker-dropdown');
16196     
16197         this.fillTime();
16198         this.update();
16199             
16200         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
16201         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
16202         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
16203         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
16204         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
16205         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
16206
16207     },
16208     
16209     fireKey: function(e){
16210         if (!this.picker().isVisible()){
16211             if (e.keyCode == 27) { // allow escape to hide and re-show picker
16212                 this.show();
16213             }
16214             return;
16215         }
16216
16217         e.preventDefault();
16218         
16219         switch(e.keyCode){
16220             case 27: // escape
16221                 this.hide();
16222                 break;
16223             case 37: // left
16224             case 39: // right
16225                 this.onTogglePeriod();
16226                 break;
16227             case 38: // up
16228                 this.onIncrementMinutes();
16229                 break;
16230             case 40: // down
16231                 this.onDecrementMinutes();
16232                 break;
16233             case 13: // enter
16234             case 9: // tab
16235                 this.setTime();
16236                 break;
16237         }
16238     },
16239     
16240     onClick: function(e) {
16241         e.stopPropagation();
16242         e.preventDefault();
16243     },
16244     
16245     picker : function()
16246     {
16247         return this.el.select('.datepicker', true).first();
16248     },
16249     
16250     fillTime: function()
16251     {    
16252         var time = this.pop.select('tbody', true).first();
16253         
16254         time.dom.innerHTML = '';
16255         
16256         time.createChild({
16257             tag: 'tr',
16258             cn: [
16259                 {
16260                     tag: 'td',
16261                     cn: [
16262                         {
16263                             tag: 'a',
16264                             href: '#',
16265                             cls: 'btn',
16266                             cn: [
16267                                 {
16268                                     tag: 'span',
16269                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
16270                                 }
16271                             ]
16272                         } 
16273                     ]
16274                 },
16275                 {
16276                     tag: 'td',
16277                     cls: 'separator'
16278                 },
16279                 {
16280                     tag: 'td',
16281                     cn: [
16282                         {
16283                             tag: 'a',
16284                             href: '#',
16285                             cls: 'btn',
16286                             cn: [
16287                                 {
16288                                     tag: 'span',
16289                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
16290                                 }
16291                             ]
16292                         }
16293                     ]
16294                 },
16295                 {
16296                     tag: 'td',
16297                     cls: 'separator'
16298                 }
16299             ]
16300         });
16301         
16302         time.createChild({
16303             tag: 'tr',
16304             cn: [
16305                 {
16306                     tag: 'td',
16307                     cn: [
16308                         {
16309                             tag: 'span',
16310                             cls: 'timepicker-hour',
16311                             html: '00'
16312                         }  
16313                     ]
16314                 },
16315                 {
16316                     tag: 'td',
16317                     cls: 'separator',
16318                     html: ':'
16319                 },
16320                 {
16321                     tag: 'td',
16322                     cn: [
16323                         {
16324                             tag: 'span',
16325                             cls: 'timepicker-minute',
16326                             html: '00'
16327                         }  
16328                     ]
16329                 },
16330                 {
16331                     tag: 'td',
16332                     cls: 'separator'
16333                 },
16334                 {
16335                     tag: 'td',
16336                     cn: [
16337                         {
16338                             tag: 'button',
16339                             type: 'button',
16340                             cls: 'btn btn-primary period',
16341                             html: 'AM'
16342                             
16343                         }
16344                     ]
16345                 }
16346             ]
16347         });
16348         
16349         time.createChild({
16350             tag: 'tr',
16351             cn: [
16352                 {
16353                     tag: 'td',
16354                     cn: [
16355                         {
16356                             tag: 'a',
16357                             href: '#',
16358                             cls: 'btn',
16359                             cn: [
16360                                 {
16361                                     tag: 'span',
16362                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
16363                                 }
16364                             ]
16365                         }
16366                     ]
16367                 },
16368                 {
16369                     tag: 'td',
16370                     cls: 'separator'
16371                 },
16372                 {
16373                     tag: 'td',
16374                     cn: [
16375                         {
16376                             tag: 'a',
16377                             href: '#',
16378                             cls: 'btn',
16379                             cn: [
16380                                 {
16381                                     tag: 'span',
16382                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
16383                                 }
16384                             ]
16385                         }
16386                     ]
16387                 },
16388                 {
16389                     tag: 'td',
16390                     cls: 'separator'
16391                 }
16392             ]
16393         });
16394         
16395     },
16396     
16397     update: function()
16398     {
16399         
16400         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
16401         
16402         this.fill();
16403     },
16404     
16405     fill: function() 
16406     {
16407         var hours = this.time.getHours();
16408         var minutes = this.time.getMinutes();
16409         var period = 'AM';
16410         
16411         if(hours > 11){
16412             period = 'PM';
16413         }
16414         
16415         if(hours == 0){
16416             hours = 12;
16417         }
16418         
16419         
16420         if(hours > 12){
16421             hours = hours - 12;
16422         }
16423         
16424         if(hours < 10){
16425             hours = '0' + hours;
16426         }
16427         
16428         if(minutes < 10){
16429             minutes = '0' + minutes;
16430         }
16431         
16432         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
16433         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
16434         this.pop.select('button', true).first().dom.innerHTML = period;
16435         
16436     },
16437     
16438     place: function()
16439     {   
16440         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
16441         
16442         var cls = ['bottom'];
16443         
16444         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
16445             cls.pop();
16446             cls.push('top');
16447         }
16448         
16449         cls.push('right');
16450         
16451         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
16452             cls.pop();
16453             cls.push('left');
16454         }
16455         
16456         this.picker().addClass(cls.join('-'));
16457         
16458         var _this = this;
16459         
16460         Roo.each(cls, function(c){
16461             if(c == 'bottom'){
16462                 _this.picker().setTop(_this.inputEl().getHeight());
16463                 return;
16464             }
16465             if(c == 'top'){
16466                 _this.picker().setTop(0 - _this.picker().getHeight());
16467                 return;
16468             }
16469             
16470             if(c == 'left'){
16471                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
16472                 return;
16473             }
16474             if(c == 'right'){
16475                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
16476                 return;
16477             }
16478         });
16479         
16480     },
16481   
16482     onFocus : function()
16483     {
16484         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
16485         this.show();
16486     },
16487     
16488     onBlur : function()
16489     {
16490         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
16491         this.hide();
16492     },
16493     
16494     show : function()
16495     {
16496         this.picker().show();
16497         this.pop.show();
16498         this.update();
16499         this.place();
16500         
16501         this.fireEvent('show', this, this.date);
16502     },
16503     
16504     hide : function()
16505     {
16506         this.picker().hide();
16507         this.pop.hide();
16508         
16509         this.fireEvent('hide', this, this.date);
16510     },
16511     
16512     setTime : function()
16513     {
16514         this.hide();
16515         this.setValue(this.time.format(this.format));
16516         
16517         this.fireEvent('select', this, this.date);
16518         
16519         
16520     },
16521     
16522     onMousedown: function(e){
16523         e.stopPropagation();
16524         e.preventDefault();
16525     },
16526     
16527     onIncrementHours: function()
16528     {
16529         Roo.log('onIncrementHours');
16530         this.time = this.time.add(Date.HOUR, 1);
16531         this.update();
16532         
16533     },
16534     
16535     onDecrementHours: function()
16536     {
16537         Roo.log('onDecrementHours');
16538         this.time = this.time.add(Date.HOUR, -1);
16539         this.update();
16540     },
16541     
16542     onIncrementMinutes: function()
16543     {
16544         Roo.log('onIncrementMinutes');
16545         this.time = this.time.add(Date.MINUTE, 1);
16546         this.update();
16547     },
16548     
16549     onDecrementMinutes: function()
16550     {
16551         Roo.log('onDecrementMinutes');
16552         this.time = this.time.add(Date.MINUTE, -1);
16553         this.update();
16554     },
16555     
16556     onTogglePeriod: function()
16557     {
16558         Roo.log('onTogglePeriod');
16559         this.time = this.time.add(Date.HOUR, 12);
16560         this.update();
16561     }
16562     
16563    
16564 });
16565
16566 Roo.apply(Roo.bootstrap.TimeField,  {
16567     
16568     content : {
16569         tag: 'tbody',
16570         cn: [
16571             {
16572                 tag: 'tr',
16573                 cn: [
16574                 {
16575                     tag: 'td',
16576                     colspan: '7'
16577                 }
16578                 ]
16579             }
16580         ]
16581     },
16582     
16583     footer : {
16584         tag: 'tfoot',
16585         cn: [
16586             {
16587                 tag: 'tr',
16588                 cn: [
16589                 {
16590                     tag: 'th',
16591                     colspan: '7',
16592                     cls: '',
16593                     cn: [
16594                         {
16595                             tag: 'button',
16596                             cls: 'btn btn-info ok',
16597                             html: 'OK'
16598                         }
16599                     ]
16600                 }
16601
16602                 ]
16603             }
16604         ]
16605     }
16606 });
16607
16608 Roo.apply(Roo.bootstrap.TimeField,  {
16609   
16610     template : {
16611         tag: 'div',
16612         cls: 'datepicker dropdown-menu',
16613         cn: [
16614             {
16615                 tag: 'div',
16616                 cls: 'datepicker-time',
16617                 cn: [
16618                 {
16619                     tag: 'table',
16620                     cls: 'table-condensed',
16621                     cn:[
16622                     Roo.bootstrap.TimeField.content,
16623                     Roo.bootstrap.TimeField.footer
16624                     ]
16625                 }
16626                 ]
16627             }
16628         ]
16629     }
16630 });
16631
16632  
16633
16634  /*
16635  * - LGPL
16636  *
16637  * MonthField
16638  * 
16639  */
16640
16641 /**
16642  * @class Roo.bootstrap.MonthField
16643  * @extends Roo.bootstrap.Input
16644  * Bootstrap MonthField class
16645  * 
16646  * @cfg {String} language default en
16647  * 
16648  * @constructor
16649  * Create a new MonthField
16650  * @param {Object} config The config object
16651  */
16652
16653 Roo.bootstrap.MonthField = function(config){
16654     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
16655     
16656     this.addEvents({
16657         /**
16658          * @event show
16659          * Fires when this field show.
16660          * @param {Roo.bootstrap.MonthField} this
16661          * @param {Mixed} date The date value
16662          */
16663         show : true,
16664         /**
16665          * @event show
16666          * Fires when this field hide.
16667          * @param {Roo.bootstrap.MonthField} this
16668          * @param {Mixed} date The date value
16669          */
16670         hide : true,
16671         /**
16672          * @event select
16673          * Fires when select a date.
16674          * @param {Roo.bootstrap.MonthField} this
16675          * @param {String} oldvalue The old value
16676          * @param {String} newvalue The new value
16677          */
16678         select : true
16679     });
16680 };
16681
16682 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
16683     
16684     onRender: function(ct, position)
16685     {
16686         
16687         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
16688         
16689         this.language = this.language || 'en';
16690         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
16691         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
16692         
16693         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
16694         this.isInline = false;
16695         this.isInput = true;
16696         this.component = this.el.select('.add-on', true).first() || false;
16697         this.component = (this.component && this.component.length === 0) ? false : this.component;
16698         this.hasInput = this.component && this.inputEL().length;
16699         
16700         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
16701         
16702         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16703         
16704         this.picker().on('mousedown', this.onMousedown, this);
16705         this.picker().on('click', this.onClick, this);
16706         
16707         this.picker().addClass('datepicker-dropdown');
16708         
16709         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16710             v.setStyle('width', '189px');
16711         });
16712         
16713         this.fillMonths();
16714         
16715         this.update();
16716         
16717         if(this.isInline) {
16718             this.show();
16719         }
16720         
16721     },
16722     
16723     setValue: function(v, suppressEvent)
16724     {   
16725         var o = this.getValue();
16726         
16727         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
16728         
16729         this.update();
16730
16731         if(suppressEvent !== true){
16732             this.fireEvent('select', this, o, v);
16733         }
16734         
16735     },
16736     
16737     getValue: function()
16738     {
16739         return this.value;
16740     },
16741     
16742     onClick: function(e) 
16743     {
16744         e.stopPropagation();
16745         e.preventDefault();
16746         
16747         var target = e.getTarget();
16748         
16749         if(target.nodeName.toLowerCase() === 'i'){
16750             target = Roo.get(target).dom.parentNode;
16751         }
16752         
16753         var nodeName = target.nodeName;
16754         var className = target.className;
16755         var html = target.innerHTML;
16756         
16757         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
16758             return;
16759         }
16760         
16761         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
16762         
16763         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16764         
16765         this.hide();
16766                         
16767     },
16768     
16769     picker : function()
16770     {
16771         return this.pickerEl;
16772     },
16773     
16774     fillMonths: function()
16775     {    
16776         var i = 0;
16777         var months = this.picker().select('>.datepicker-months td', true).first();
16778         
16779         months.dom.innerHTML = '';
16780         
16781         while (i < 12) {
16782             var month = {
16783                 tag: 'span',
16784                 cls: 'month',
16785                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
16786             }
16787             
16788             months.createChild(month);
16789         }
16790         
16791     },
16792     
16793     update: function()
16794     {
16795         var _this = this;
16796         
16797         if(typeof(this.vIndex) == 'undefined' && this.value.length){
16798             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
16799         }
16800         
16801         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
16802             e.removeClass('active');
16803             
16804             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
16805                 e.addClass('active');
16806             }
16807         })
16808     },
16809     
16810     place: function()
16811     {
16812         if(this.isInline) return;
16813         
16814         this.picker().removeClass(['bottom', 'top']);
16815         
16816         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16817             /*
16818              * place to the top of element!
16819              *
16820              */
16821             
16822             this.picker().addClass('top');
16823             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16824             
16825             return;
16826         }
16827         
16828         this.picker().addClass('bottom');
16829         
16830         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16831     },
16832     
16833     onFocus : function()
16834     {
16835         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
16836         this.show();
16837     },
16838     
16839     onBlur : function()
16840     {
16841         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
16842         
16843         var d = this.inputEl().getValue();
16844         
16845         this.setValue(d);
16846                 
16847         this.hide();
16848     },
16849     
16850     show : function()
16851     {
16852         this.picker().show();
16853         this.picker().select('>.datepicker-months', true).first().show();
16854         this.update();
16855         this.place();
16856         
16857         this.fireEvent('show', this, this.date);
16858     },
16859     
16860     hide : function()
16861     {
16862         if(this.isInline) return;
16863         this.picker().hide();
16864         this.fireEvent('hide', this, this.date);
16865         
16866     },
16867     
16868     onMousedown: function(e)
16869     {
16870         e.stopPropagation();
16871         e.preventDefault();
16872     },
16873     
16874     keyup: function(e)
16875     {
16876         Roo.bootstrap.MonthField.superclass.keyup.call(this);
16877         this.update();
16878     },
16879
16880     fireKey: function(e)
16881     {
16882         if (!this.picker().isVisible()){
16883             if (e.keyCode == 27) // allow escape to hide and re-show picker
16884                 this.show();
16885             return;
16886         }
16887         
16888         var dir;
16889         
16890         switch(e.keyCode){
16891             case 27: // escape
16892                 this.hide();
16893                 e.preventDefault();
16894                 break;
16895             case 37: // left
16896             case 39: // right
16897                 dir = e.keyCode == 37 ? -1 : 1;
16898                 
16899                 this.vIndex = this.vIndex + dir;
16900                 
16901                 if(this.vIndex < 0){
16902                     this.vIndex = 0;
16903                 }
16904                 
16905                 if(this.vIndex > 11){
16906                     this.vIndex = 11;
16907                 }
16908                 
16909                 if(isNaN(this.vIndex)){
16910                     this.vIndex = 0;
16911                 }
16912                 
16913                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16914                 
16915                 break;
16916             case 38: // up
16917             case 40: // down
16918                 
16919                 dir = e.keyCode == 38 ? -1 : 1;
16920                 
16921                 this.vIndex = this.vIndex + dir * 4;
16922                 
16923                 if(this.vIndex < 0){
16924                     this.vIndex = 0;
16925                 }
16926                 
16927                 if(this.vIndex > 11){
16928                     this.vIndex = 11;
16929                 }
16930                 
16931                 if(isNaN(this.vIndex)){
16932                     this.vIndex = 0;
16933                 }
16934                 
16935                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16936                 break;
16937                 
16938             case 13: // enter
16939                 
16940                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16941                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16942                 }
16943                 
16944                 this.hide();
16945                 e.preventDefault();
16946                 break;
16947             case 9: // tab
16948                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16949                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16950                 }
16951                 this.hide();
16952                 break;
16953             case 16: // shift
16954             case 17: // ctrl
16955             case 18: // alt
16956                 break;
16957             default :
16958                 this.hide();
16959                 
16960         }
16961     },
16962     
16963     remove: function() 
16964     {
16965         this.picker().remove();
16966     }
16967    
16968 });
16969
16970 Roo.apply(Roo.bootstrap.MonthField,  {
16971     
16972     content : {
16973         tag: 'tbody',
16974         cn: [
16975         {
16976             tag: 'tr',
16977             cn: [
16978             {
16979                 tag: 'td',
16980                 colspan: '7'
16981             }
16982             ]
16983         }
16984         ]
16985     },
16986     
16987     dates:{
16988         en: {
16989             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16990             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
16991         }
16992     }
16993 });
16994
16995 Roo.apply(Roo.bootstrap.MonthField,  {
16996   
16997     template : {
16998         tag: 'div',
16999         cls: 'datepicker dropdown-menu roo-dynamic',
17000         cn: [
17001             {
17002                 tag: 'div',
17003                 cls: 'datepicker-months',
17004                 cn: [
17005                 {
17006                     tag: 'table',
17007                     cls: 'table-condensed',
17008                     cn:[
17009                         Roo.bootstrap.DateField.content
17010                     ]
17011                 }
17012                 ]
17013             }
17014         ]
17015     }
17016 });
17017
17018  
17019
17020  
17021  /*
17022  * - LGPL
17023  *
17024  * CheckBox
17025  * 
17026  */
17027
17028 /**
17029  * @class Roo.bootstrap.CheckBox
17030  * @extends Roo.bootstrap.Input
17031  * Bootstrap CheckBox class
17032  * 
17033  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17034  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17035  * @cfg {String} boxLabel The text that appears beside the checkbox
17036  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
17037  * @cfg {Boolean} checked initnal the element
17038  * @cfg {Boolean} inline inline the element (default false)
17039  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
17040  * 
17041  * @constructor
17042  * Create a new CheckBox
17043  * @param {Object} config The config object
17044  */
17045
17046 Roo.bootstrap.CheckBox = function(config){
17047     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
17048    
17049     this.addEvents({
17050         /**
17051         * @event check
17052         * Fires when the element is checked or unchecked.
17053         * @param {Roo.bootstrap.CheckBox} this This input
17054         * @param {Boolean} checked The new checked value
17055         */
17056        check : true
17057     });
17058     
17059 };
17060
17061 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
17062   
17063     inputType: 'checkbox',
17064     inputValue: 1,
17065     valueOff: 0,
17066     boxLabel: false,
17067     checked: false,
17068     weight : false,
17069     inline: false,
17070     
17071     getAutoCreate : function()
17072     {
17073         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17074         
17075         var id = Roo.id();
17076         
17077         var cfg = {};
17078         
17079         cfg.cls = 'form-group ' + this.inputType; //input-group
17080         
17081         if(this.inline){
17082             cfg.cls += ' ' + this.inputType + '-inline';
17083         }
17084         
17085         var input =  {
17086             tag: 'input',
17087             id : id,
17088             type : this.inputType,
17089             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
17090             cls : 'roo-' + this.inputType, //'form-box',
17091             placeholder : this.placeholder || ''
17092             
17093         };
17094         
17095         if (this.weight) { // Validity check?
17096             cfg.cls += " " + this.inputType + "-" + this.weight;
17097         }
17098         
17099         if (this.disabled) {
17100             input.disabled=true;
17101         }
17102         
17103         if(this.checked){
17104             input.checked = this.checked;
17105         }
17106         
17107         if (this.name) {
17108             input.name = this.name;
17109         }
17110         
17111         if (this.size) {
17112             input.cls += ' input-' + this.size;
17113         }
17114         
17115         var settings=this;
17116         
17117         ['xs','sm','md','lg'].map(function(size){
17118             if (settings[size]) {
17119                 cfg.cls += ' col-' + size + '-' + settings[size];
17120             }
17121         });
17122         
17123         var inputblock = input;
17124          
17125         if (this.before || this.after) {
17126             
17127             inputblock = {
17128                 cls : 'input-group',
17129                 cn :  [] 
17130             };
17131             
17132             if (this.before) {
17133                 inputblock.cn.push({
17134                     tag :'span',
17135                     cls : 'input-group-addon',
17136                     html : this.before
17137                 });
17138             }
17139             
17140             inputblock.cn.push(input);
17141             
17142             if (this.after) {
17143                 inputblock.cn.push({
17144                     tag :'span',
17145                     cls : 'input-group-addon',
17146                     html : this.after
17147                 });
17148             }
17149             
17150         }
17151         
17152         if (align ==='left' && this.fieldLabel.length) {
17153                 Roo.log("left and has label");
17154                 cfg.cn = [
17155                     
17156                     {
17157                         tag: 'label',
17158                         'for' :  id,
17159                         cls : 'control-label col-md-' + this.labelWidth,
17160                         html : this.fieldLabel
17161                         
17162                     },
17163                     {
17164                         cls : "col-md-" + (12 - this.labelWidth), 
17165                         cn: [
17166                             inputblock
17167                         ]
17168                     }
17169                     
17170                 ];
17171         } else if ( this.fieldLabel.length) {
17172                 Roo.log(" label");
17173                 cfg.cn = [
17174                    
17175                     {
17176                         tag: this.boxLabel ? 'span' : 'label',
17177                         'for': id,
17178                         cls: 'control-label box-input-label',
17179                         //cls : 'input-group-addon',
17180                         html : this.fieldLabel
17181                         
17182                     },
17183                     
17184                     inputblock
17185                     
17186                 ];
17187
17188         } else {
17189             
17190                 Roo.log(" no label && no align");
17191                 cfg.cn = [  inputblock ] ;
17192                 
17193                 
17194         }
17195         if(this.boxLabel){
17196              var boxLabelCfg = {
17197                 tag: 'label',
17198                 //'for': id, // box label is handled by onclick - so no for...
17199                 cls: 'box-label',
17200                 html: this.boxLabel
17201             }
17202             
17203             if(this.tooltip){
17204                 boxLabelCfg.tooltip = this.tooltip;
17205             }
17206              
17207             cfg.cn.push(boxLabelCfg);
17208         }
17209         
17210         
17211        
17212         return cfg;
17213         
17214     },
17215     
17216     /**
17217      * return the real input element.
17218      */
17219     inputEl: function ()
17220     {
17221         return this.el.select('input.roo-' + this.inputType,true).first();
17222     },
17223     
17224     labelEl: function()
17225     {
17226         return this.el.select('label.control-label',true).first();
17227     },
17228     /* depricated... */
17229     
17230     label: function()
17231     {
17232         return this.labelEl();
17233     },
17234     
17235     initEvents : function()
17236     {
17237 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17238         
17239         this.inputEl().on('click', this.onClick,  this);
17240         
17241         if (this.boxLabel) { 
17242             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
17243         }
17244         
17245         this.startValue = this.getValue();
17246         
17247         if(this.groupId){
17248             Roo.bootstrap.CheckBox.register(this);
17249         }
17250     },
17251     
17252     onClick : function()
17253     {   
17254         this.setChecked(!this.checked);
17255     },
17256     
17257     setChecked : function(state,suppressEvent)
17258     {
17259         this.startValue = this.getValue();
17260         
17261         if(this.inputType == 'radio'){
17262             
17263             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17264                 e.dom.checked = false;
17265             });
17266             
17267             this.inputEl().dom.checked = true;
17268             
17269             this.inputEl().dom.value = this.inputValue;
17270             
17271             if(suppressEvent !== true){
17272                 this.fireEvent('check', this, true);
17273             }
17274             
17275             this.validate();
17276             
17277             return;
17278         }
17279         
17280         this.checked = state;
17281         
17282         this.inputEl().dom.checked = state;
17283         
17284         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17285         
17286         if(suppressEvent !== true){
17287             this.fireEvent('check', this, state);
17288         }
17289         
17290         this.validate();
17291     },
17292     
17293     getValue : function()
17294     {
17295         if(this.inputType == 'radio'){
17296             return this.getGroupValue();
17297         }
17298         
17299         return this.inputEl().getValue();
17300         
17301     },
17302     
17303     getGroupValue : function()
17304     {
17305         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
17306             return '';
17307         }
17308         
17309         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
17310     },
17311     
17312     setValue : function(v,suppressEvent)
17313     {
17314         if(this.inputType == 'radio'){
17315             this.setGroupValue(v, suppressEvent);
17316             return;
17317         }
17318         
17319         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
17320         
17321         this.validate();
17322     },
17323     
17324     setGroupValue : function(v, suppressEvent)
17325     {
17326         this.startValue = this.getValue();
17327         
17328         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17329             e.dom.checked = false;
17330             
17331             if(e.dom.value == v){
17332                 e.dom.checked = true;
17333             }
17334         });
17335         
17336         if(suppressEvent !== true){
17337             this.fireEvent('check', this, true);
17338         }
17339
17340         this.validate();
17341         
17342         return;
17343     },
17344     
17345     validate : function()
17346     {
17347         if(
17348                 this.disabled || 
17349                 (this.inputType == 'radio' && this.validateRadio()) ||
17350                 (this.inputType == 'checkbox' && this.validateCheckbox())
17351         ){
17352             this.markValid();
17353             return true;
17354         }
17355         
17356         this.markInvalid();
17357         return false;
17358     },
17359     
17360     validateRadio : function()
17361     {
17362         var valid = false;
17363         
17364         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17365             if(!e.dom.checked){
17366                 return;
17367             }
17368             
17369             valid = true;
17370             
17371             return false;
17372         });
17373         
17374         return valid;
17375     },
17376     
17377     validateCheckbox : function()
17378     {
17379         if(!this.groupId){
17380             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
17381         }
17382         
17383         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17384         
17385         if(!group){
17386             return false;
17387         }
17388         
17389         var r = false;
17390         
17391         for(var i in group){
17392             if(r){
17393                 break;
17394             }
17395             
17396             r = (group[i].getValue() == group[i].inputValue) ? true : false;
17397         }
17398         
17399         return r;
17400     },
17401     
17402     /**
17403      * Mark this field as valid
17404      */
17405     markValid : function()
17406     {
17407         if(this.allowBlank){
17408             return;
17409         }
17410         
17411         var _this = this;
17412         
17413         this.fireEvent('valid', this);
17414         
17415         if(this.inputType == 'radio'){
17416             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17417                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17418                 e.findParent('.form-group', false, true).addClass(_this.validClass);
17419             });
17420             
17421             return;
17422         }
17423         
17424         if(!this.groupId){
17425             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17426             this.el.findParent('.form-group', false, true).addClass(this.validClass);
17427             return;
17428         }
17429         
17430         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17431             
17432         if(!group){
17433             return;
17434         }
17435         
17436         for(var i in group){
17437             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17438             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
17439         }
17440     },
17441     
17442      /**
17443      * Mark this field as invalid
17444      * @param {String} msg The validation message
17445      */
17446     markInvalid : function(msg)
17447     {
17448         if(this.allowBlank){
17449             return;
17450         }
17451         
17452         var _this = this;
17453         
17454         this.fireEvent('invalid', this, msg);
17455         
17456         if(this.inputType == 'radio'){
17457             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17458                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17459                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
17460             });
17461             
17462             return;
17463         }
17464         
17465         if(!this.groupId){
17466             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17467             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
17468             return;
17469         }
17470         
17471         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17472             
17473         if(!group){
17474             return;
17475         }
17476         
17477         for(var i in group){
17478             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17479             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
17480         }
17481         
17482     }
17483     
17484 });
17485
17486 Roo.apply(Roo.bootstrap.CheckBox, {
17487     
17488     groups: {},
17489     
17490      /**
17491     * register a CheckBox Group
17492     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
17493     */
17494     register : function(checkbox)
17495     {
17496         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
17497             this.groups[checkbox.groupId] = {};
17498         }
17499         
17500         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
17501             return;
17502         }
17503         
17504         this.groups[checkbox.groupId][checkbox.name] = checkbox;
17505         
17506     },
17507     /**
17508     * fetch a CheckBox Group based on the group ID
17509     * @param {string} the group ID
17510     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
17511     */
17512     get: function(groupId) {
17513         if (typeof(this.groups[groupId]) == 'undefined') {
17514             return false;
17515         }
17516         
17517         return this.groups[groupId] ;
17518     }
17519     
17520     
17521 });
17522 /*
17523  * - LGPL
17524  *
17525  * Radio
17526  *
17527  *
17528  * not inline
17529  *<div class="radio">
17530   <label>
17531     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
17532     Option one is this and that&mdash;be sure to include why it's great
17533   </label>
17534 </div>
17535  *
17536  *
17537  *inline
17538  *<span>
17539  *<label class="radio-inline">fieldLabel</label>
17540  *<label class="radio-inline">
17541   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
17542 </label>
17543 <span>
17544  * 
17545  * 
17546  */
17547
17548 /**
17549  * @class Roo.bootstrap.Radio
17550  * @extends Roo.bootstrap.CheckBox
17551  * Bootstrap Radio class
17552
17553  * @constructor
17554  * Create a new Radio
17555  * @param {Object} config The config object
17556  */
17557
17558 Roo.bootstrap.Radio = function(config){
17559     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
17560    
17561 };
17562
17563 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
17564     
17565     inputType: 'radio',
17566     inputValue: '',
17567     valueOff: '',
17568     
17569     getAutoCreate : function()
17570     {
17571         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17572         align = align || 'left'; // default...
17573         
17574         
17575         
17576         var id = Roo.id();
17577         
17578         var cfg = {
17579                 tag : this.inline ? 'span' : 'div',
17580                 cls : '',
17581                 cn : []
17582         };
17583         
17584         var inline = this.inline ? ' radio-inline' : '';
17585         
17586         var lbl = {
17587                 tag: 'label' ,
17588                 // does not need for, as we wrap the input with it..
17589                 'for' : id,
17590                 cls : 'control-label box-label' + inline,
17591                 cn : []
17592         };
17593         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
17594         
17595         var fieldLabel = {
17596             tag: 'label' ,
17597             //cls : 'control-label' + inline,
17598             html : this.fieldLabel,
17599             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
17600         };
17601         
17602  
17603         
17604         
17605         var input =  {
17606             tag: 'input',
17607             id : id,
17608             type : this.inputType,
17609             //value : (!this.checked) ? this.valueOff : this.inputValue,
17610             value : this.inputValue,
17611             cls : 'roo-radio',
17612             placeholder : this.placeholder || '' // ?? needed????
17613             
17614         };
17615         if (this.weight) { // Validity check?
17616             input.cls += " radio-" + this.weight;
17617         }
17618         if (this.disabled) {
17619             input.disabled=true;
17620         }
17621         
17622         if(this.checked){
17623             input.checked = this.checked;
17624         }
17625         
17626         if (this.name) {
17627             input.name = this.name;
17628         }
17629         
17630         if (this.size) {
17631             input.cls += ' input-' + this.size;
17632         }
17633         
17634         //?? can span's inline have a width??
17635         
17636         var settings=this;
17637         ['xs','sm','md','lg'].map(function(size){
17638             if (settings[size]) {
17639                 cfg.cls += ' col-' + size + '-' + settings[size];
17640             }
17641         });
17642         
17643         var inputblock = input;
17644         
17645         if (this.before || this.after) {
17646             
17647             inputblock = {
17648                 cls : 'input-group',
17649                 tag : 'span',
17650                 cn :  [] 
17651             };
17652             if (this.before) {
17653                 inputblock.cn.push({
17654                     tag :'span',
17655                     cls : 'input-group-addon',
17656                     html : this.before
17657                 });
17658             }
17659             inputblock.cn.push(input);
17660             if (this.after) {
17661                 inputblock.cn.push({
17662                     tag :'span',
17663                     cls : 'input-group-addon',
17664                     html : this.after
17665                 });
17666             }
17667             
17668         };
17669         
17670         
17671         if (this.fieldLabel && this.fieldLabel.length) {
17672             cfg.cn.push(fieldLabel);
17673         }
17674        
17675         // normal bootstrap puts the input inside the label.
17676         // however with our styled version - it has to go after the input.
17677        
17678         //lbl.cn.push(inputblock);
17679         
17680         var lblwrap =  {
17681             tag: 'span',
17682             cls: 'radio' + inline,
17683             cn: [
17684                 inputblock,
17685                 lbl
17686             ]
17687         };
17688         
17689         cfg.cn.push( lblwrap);
17690         
17691         if(this.boxLabel){
17692             lbl.cn.push({
17693                 tag: 'span',
17694                 html: this.boxLabel
17695             })
17696         }
17697          
17698         
17699         return cfg;
17700         
17701     },
17702     
17703     initEvents : function()
17704     {
17705 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17706         
17707         this.inputEl().on('click', this.onClick,  this);
17708         if (this.boxLabel) {
17709             Roo.log('find label')
17710             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
17711         }
17712         
17713     },
17714     
17715     inputEl: function ()
17716     {
17717         return this.el.select('input.roo-radio',true).first();
17718     },
17719     onClick : function()
17720     {   
17721         Roo.log("click");
17722         this.setChecked(true);
17723     },
17724     
17725     setChecked : function(state,suppressEvent)
17726     {
17727         if(state){
17728             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17729                 v.dom.checked = false;
17730             });
17731         }
17732         Roo.log(this.inputEl().dom);
17733         this.checked = state;
17734         this.inputEl().dom.checked = state;
17735         
17736         if(suppressEvent !== true){
17737             this.fireEvent('check', this, state);
17738         }
17739         
17740         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17741         
17742     },
17743     
17744     getGroupValue : function()
17745     {
17746         var value = '';
17747         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17748             if(v.dom.checked == true){
17749                 value = v.dom.value;
17750             }
17751         });
17752         
17753         return value;
17754     },
17755     
17756     /**
17757      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
17758      * @return {Mixed} value The field value
17759      */
17760     getValue : function(){
17761         return this.getGroupValue();
17762     }
17763     
17764 });
17765
17766  
17767 //<script type="text/javascript">
17768
17769 /*
17770  * Based  Ext JS Library 1.1.1
17771  * Copyright(c) 2006-2007, Ext JS, LLC.
17772  * LGPL
17773  *
17774  */
17775  
17776 /**
17777  * @class Roo.HtmlEditorCore
17778  * @extends Roo.Component
17779  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
17780  *
17781  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
17782  */
17783
17784 Roo.HtmlEditorCore = function(config){
17785     
17786     
17787     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
17788     
17789     
17790     this.addEvents({
17791         /**
17792          * @event initialize
17793          * Fires when the editor is fully initialized (including the iframe)
17794          * @param {Roo.HtmlEditorCore} this
17795          */
17796         initialize: true,
17797         /**
17798          * @event activate
17799          * Fires when the editor is first receives the focus. Any insertion must wait
17800          * until after this event.
17801          * @param {Roo.HtmlEditorCore} this
17802          */
17803         activate: true,
17804          /**
17805          * @event beforesync
17806          * Fires before the textarea is updated with content from the editor iframe. Return false
17807          * to cancel the sync.
17808          * @param {Roo.HtmlEditorCore} this
17809          * @param {String} html
17810          */
17811         beforesync: true,
17812          /**
17813          * @event beforepush
17814          * Fires before the iframe editor is updated with content from the textarea. Return false
17815          * to cancel the push.
17816          * @param {Roo.HtmlEditorCore} this
17817          * @param {String} html
17818          */
17819         beforepush: true,
17820          /**
17821          * @event sync
17822          * Fires when the textarea is updated with content from the editor iframe.
17823          * @param {Roo.HtmlEditorCore} this
17824          * @param {String} html
17825          */
17826         sync: true,
17827          /**
17828          * @event push
17829          * Fires when the iframe editor is updated with content from the textarea.
17830          * @param {Roo.HtmlEditorCore} this
17831          * @param {String} html
17832          */
17833         push: true,
17834         
17835         /**
17836          * @event editorevent
17837          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17838          * @param {Roo.HtmlEditorCore} this
17839          */
17840         editorevent: true
17841         
17842     });
17843     
17844     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
17845     
17846     // defaults : white / black...
17847     this.applyBlacklists();
17848     
17849     
17850     
17851 };
17852
17853
17854 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
17855
17856
17857      /**
17858      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
17859      */
17860     
17861     owner : false,
17862     
17863      /**
17864      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17865      *                        Roo.resizable.
17866      */
17867     resizable : false,
17868      /**
17869      * @cfg {Number} height (in pixels)
17870      */   
17871     height: 300,
17872    /**
17873      * @cfg {Number} width (in pixels)
17874      */   
17875     width: 500,
17876     
17877     /**
17878      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17879      * 
17880      */
17881     stylesheets: false,
17882     
17883     // id of frame..
17884     frameId: false,
17885     
17886     // private properties
17887     validationEvent : false,
17888     deferHeight: true,
17889     initialized : false,
17890     activated : false,
17891     sourceEditMode : false,
17892     onFocus : Roo.emptyFn,
17893     iframePad:3,
17894     hideMode:'offsets',
17895     
17896     clearUp: true,
17897     
17898     // blacklist + whitelisted elements..
17899     black: false,
17900     white: false,
17901      
17902     
17903
17904     /**
17905      * Protected method that will not generally be called directly. It
17906      * is called when the editor initializes the iframe with HTML contents. Override this method if you
17907      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
17908      */
17909     getDocMarkup : function(){
17910         // body styles..
17911         var st = '';
17912         
17913         // inherit styels from page...?? 
17914         if (this.stylesheets === false) {
17915             
17916             Roo.get(document.head).select('style').each(function(node) {
17917                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17918             });
17919             
17920             Roo.get(document.head).select('link').each(function(node) { 
17921                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17922             });
17923             
17924         } else if (!this.stylesheets.length) {
17925                 // simple..
17926                 st = '<style type="text/css">' +
17927                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17928                    '</style>';
17929         } else { 
17930             
17931         }
17932         
17933         st +=  '<style type="text/css">' +
17934             'IMG { cursor: pointer } ' +
17935         '</style>';
17936
17937         
17938         return '<html><head>' + st  +
17939             //<style type="text/css">' +
17940             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17941             //'</style>' +
17942             ' </head><body class="roo-htmleditor-body"></body></html>';
17943     },
17944
17945     // private
17946     onRender : function(ct, position)
17947     {
17948         var _t = this;
17949         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
17950         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
17951         
17952         
17953         this.el.dom.style.border = '0 none';
17954         this.el.dom.setAttribute('tabIndex', -1);
17955         this.el.addClass('x-hidden hide');
17956         
17957         
17958         
17959         if(Roo.isIE){ // fix IE 1px bogus margin
17960             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
17961         }
17962        
17963         
17964         this.frameId = Roo.id();
17965         
17966          
17967         
17968         var iframe = this.owner.wrap.createChild({
17969             tag: 'iframe',
17970             cls: 'form-control', // bootstrap..
17971             id: this.frameId,
17972             name: this.frameId,
17973             frameBorder : 'no',
17974             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
17975         }, this.el
17976         );
17977         
17978         
17979         this.iframe = iframe.dom;
17980
17981          this.assignDocWin();
17982         
17983         this.doc.designMode = 'on';
17984        
17985         this.doc.open();
17986         this.doc.write(this.getDocMarkup());
17987         this.doc.close();
17988
17989         
17990         var task = { // must defer to wait for browser to be ready
17991             run : function(){
17992                 //console.log("run task?" + this.doc.readyState);
17993                 this.assignDocWin();
17994                 if(this.doc.body || this.doc.readyState == 'complete'){
17995                     try {
17996                         this.doc.designMode="on";
17997                     } catch (e) {
17998                         return;
17999                     }
18000                     Roo.TaskMgr.stop(task);
18001                     this.initEditor.defer(10, this);
18002                 }
18003             },
18004             interval : 10,
18005             duration: 10000,
18006             scope: this
18007         };
18008         Roo.TaskMgr.start(task);
18009
18010     },
18011
18012     // private
18013     onResize : function(w, h)
18014     {
18015          Roo.log('resize: ' +w + ',' + h );
18016         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18017         if(!this.iframe){
18018             return;
18019         }
18020         if(typeof w == 'number'){
18021             
18022             this.iframe.style.width = w + 'px';
18023         }
18024         if(typeof h == 'number'){
18025             
18026             this.iframe.style.height = h + 'px';
18027             if(this.doc){
18028                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18029             }
18030         }
18031         
18032     },
18033
18034     /**
18035      * Toggles the editor between standard and source edit mode.
18036      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18037      */
18038     toggleSourceEdit : function(sourceEditMode){
18039         
18040         this.sourceEditMode = sourceEditMode === true;
18041         
18042         if(this.sourceEditMode){
18043  
18044             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
18045             
18046         }else{
18047             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
18048             //this.iframe.className = '';
18049             this.deferFocus();
18050         }
18051         //this.setSize(this.owner.wrap.getSize());
18052         //this.fireEvent('editmodechange', this, this.sourceEditMode);
18053     },
18054
18055     
18056   
18057
18058     /**
18059      * Protected method that will not generally be called directly. If you need/want
18060      * custom HTML cleanup, this is the method you should override.
18061      * @param {String} html The HTML to be cleaned
18062      * return {String} The cleaned HTML
18063      */
18064     cleanHtml : function(html){
18065         html = String(html);
18066         if(html.length > 5){
18067             if(Roo.isSafari){ // strip safari nonsense
18068                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
18069             }
18070         }
18071         if(html == '&nbsp;'){
18072             html = '';
18073         }
18074         return html;
18075     },
18076
18077     /**
18078      * HTML Editor -> Textarea
18079      * Protected method that will not generally be called directly. Syncs the contents
18080      * of the editor iframe with the textarea.
18081      */
18082     syncValue : function(){
18083         if(this.initialized){
18084             var bd = (this.doc.body || this.doc.documentElement);
18085             //this.cleanUpPaste(); -- this is done else where and causes havoc..
18086             var html = bd.innerHTML;
18087             if(Roo.isSafari){
18088                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
18089                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
18090                 if(m && m[1]){
18091                     html = '<div style="'+m[0]+'">' + html + '</div>';
18092                 }
18093             }
18094             html = this.cleanHtml(html);
18095             // fix up the special chars.. normaly like back quotes in word...
18096             // however we do not want to do this with chinese..
18097             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
18098                 var cc = b.charCodeAt();
18099                 if (
18100                     (cc >= 0x4E00 && cc < 0xA000 ) ||
18101                     (cc >= 0x3400 && cc < 0x4E00 ) ||
18102                     (cc >= 0xf900 && cc < 0xfb00 )
18103                 ) {
18104                         return b;
18105                 }
18106                 return "&#"+cc+";" 
18107             });
18108             if(this.owner.fireEvent('beforesync', this, html) !== false){
18109                 this.el.dom.value = html;
18110                 this.owner.fireEvent('sync', this, html);
18111             }
18112         }
18113     },
18114
18115     /**
18116      * Protected method that will not generally be called directly. Pushes the value of the textarea
18117      * into the iframe editor.
18118      */
18119     pushValue : function(){
18120         if(this.initialized){
18121             var v = this.el.dom.value.trim();
18122             
18123 //            if(v.length < 1){
18124 //                v = '&#160;';
18125 //            }
18126             
18127             if(this.owner.fireEvent('beforepush', this, v) !== false){
18128                 var d = (this.doc.body || this.doc.documentElement);
18129                 d.innerHTML = v;
18130                 this.cleanUpPaste();
18131                 this.el.dom.value = d.innerHTML;
18132                 this.owner.fireEvent('push', this, v);
18133             }
18134         }
18135     },
18136
18137     // private
18138     deferFocus : function(){
18139         this.focus.defer(10, this);
18140     },
18141
18142     // doc'ed in Field
18143     focus : function(){
18144         if(this.win && !this.sourceEditMode){
18145             this.win.focus();
18146         }else{
18147             this.el.focus();
18148         }
18149     },
18150     
18151     assignDocWin: function()
18152     {
18153         var iframe = this.iframe;
18154         
18155          if(Roo.isIE){
18156             this.doc = iframe.contentWindow.document;
18157             this.win = iframe.contentWindow;
18158         } else {
18159 //            if (!Roo.get(this.frameId)) {
18160 //                return;
18161 //            }
18162 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18163 //            this.win = Roo.get(this.frameId).dom.contentWindow;
18164             
18165             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
18166                 return;
18167             }
18168             
18169             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18170             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
18171         }
18172     },
18173     
18174     // private
18175     initEditor : function(){
18176         //console.log("INIT EDITOR");
18177         this.assignDocWin();
18178         
18179         
18180         
18181         this.doc.designMode="on";
18182         this.doc.open();
18183         this.doc.write(this.getDocMarkup());
18184         this.doc.close();
18185         
18186         var dbody = (this.doc.body || this.doc.documentElement);
18187         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
18188         // this copies styles from the containing element into thsi one..
18189         // not sure why we need all of this..
18190         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
18191         
18192         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
18193         //ss['background-attachment'] = 'fixed'; // w3c
18194         dbody.bgProperties = 'fixed'; // ie
18195         //Roo.DomHelper.applyStyles(dbody, ss);
18196         Roo.EventManager.on(this.doc, {
18197             //'mousedown': this.onEditorEvent,
18198             'mouseup': this.onEditorEvent,
18199             'dblclick': this.onEditorEvent,
18200             'click': this.onEditorEvent,
18201             'keyup': this.onEditorEvent,
18202             buffer:100,
18203             scope: this
18204         });
18205         if(Roo.isGecko){
18206             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
18207         }
18208         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
18209             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
18210         }
18211         this.initialized = true;
18212
18213         this.owner.fireEvent('initialize', this);
18214         this.pushValue();
18215     },
18216
18217     // private
18218     onDestroy : function(){
18219         
18220         
18221         
18222         if(this.rendered){
18223             
18224             //for (var i =0; i < this.toolbars.length;i++) {
18225             //    // fixme - ask toolbars for heights?
18226             //    this.toolbars[i].onDestroy();
18227            // }
18228             
18229             //this.wrap.dom.innerHTML = '';
18230             //this.wrap.remove();
18231         }
18232     },
18233
18234     // private
18235     onFirstFocus : function(){
18236         
18237         this.assignDocWin();
18238         
18239         
18240         this.activated = true;
18241          
18242     
18243         if(Roo.isGecko){ // prevent silly gecko errors
18244             this.win.focus();
18245             var s = this.win.getSelection();
18246             if(!s.focusNode || s.focusNode.nodeType != 3){
18247                 var r = s.getRangeAt(0);
18248                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
18249                 r.collapse(true);
18250                 this.deferFocus();
18251             }
18252             try{
18253                 this.execCmd('useCSS', true);
18254                 this.execCmd('styleWithCSS', false);
18255             }catch(e){}
18256         }
18257         this.owner.fireEvent('activate', this);
18258     },
18259
18260     // private
18261     adjustFont: function(btn){
18262         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
18263         //if(Roo.isSafari){ // safari
18264         //    adjust *= 2;
18265        // }
18266         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
18267         if(Roo.isSafari){ // safari
18268             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
18269             v =  (v < 10) ? 10 : v;
18270             v =  (v > 48) ? 48 : v;
18271             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
18272             
18273         }
18274         
18275         
18276         v = Math.max(1, v+adjust);
18277         
18278         this.execCmd('FontSize', v  );
18279     },
18280
18281     onEditorEvent : function(e){
18282         this.owner.fireEvent('editorevent', this, e);
18283       //  this.updateToolbar();
18284         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
18285     },
18286
18287     insertTag : function(tg)
18288     {
18289         // could be a bit smarter... -> wrap the current selected tRoo..
18290         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
18291             
18292             range = this.createRange(this.getSelection());
18293             var wrappingNode = this.doc.createElement(tg.toLowerCase());
18294             wrappingNode.appendChild(range.extractContents());
18295             range.insertNode(wrappingNode);
18296
18297             return;
18298             
18299             
18300             
18301         }
18302         this.execCmd("formatblock",   tg);
18303         
18304     },
18305     
18306     insertText : function(txt)
18307     {
18308         
18309         
18310         var range = this.createRange();
18311         range.deleteContents();
18312                //alert(Sender.getAttribute('label'));
18313                
18314         range.insertNode(this.doc.createTextNode(txt));
18315     } ,
18316     
18317      
18318
18319     /**
18320      * Executes a Midas editor command on the editor document and performs necessary focus and
18321      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
18322      * @param {String} cmd The Midas command
18323      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18324      */
18325     relayCmd : function(cmd, value){
18326         this.win.focus();
18327         this.execCmd(cmd, value);
18328         this.owner.fireEvent('editorevent', this);
18329         //this.updateToolbar();
18330         this.owner.deferFocus();
18331     },
18332
18333     /**
18334      * Executes a Midas editor command directly on the editor document.
18335      * For visual commands, you should use {@link #relayCmd} instead.
18336      * <b>This should only be called after the editor is initialized.</b>
18337      * @param {String} cmd The Midas command
18338      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18339      */
18340     execCmd : function(cmd, value){
18341         this.doc.execCommand(cmd, false, value === undefined ? null : value);
18342         this.syncValue();
18343     },
18344  
18345  
18346    
18347     /**
18348      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
18349      * to insert tRoo.
18350      * @param {String} text | dom node.. 
18351      */
18352     insertAtCursor : function(text)
18353     {
18354         
18355         
18356         
18357         if(!this.activated){
18358             return;
18359         }
18360         /*
18361         if(Roo.isIE){
18362             this.win.focus();
18363             var r = this.doc.selection.createRange();
18364             if(r){
18365                 r.collapse(true);
18366                 r.pasteHTML(text);
18367                 this.syncValue();
18368                 this.deferFocus();
18369             
18370             }
18371             return;
18372         }
18373         */
18374         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
18375             this.win.focus();
18376             
18377             
18378             // from jquery ui (MIT licenced)
18379             var range, node;
18380             var win = this.win;
18381             
18382             if (win.getSelection && win.getSelection().getRangeAt) {
18383                 range = win.getSelection().getRangeAt(0);
18384                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
18385                 range.insertNode(node);
18386             } else if (win.document.selection && win.document.selection.createRange) {
18387                 // no firefox support
18388                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18389                 win.document.selection.createRange().pasteHTML(txt);
18390             } else {
18391                 // no firefox support
18392                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18393                 this.execCmd('InsertHTML', txt);
18394             } 
18395             
18396             this.syncValue();
18397             
18398             this.deferFocus();
18399         }
18400     },
18401  // private
18402     mozKeyPress : function(e){
18403         if(e.ctrlKey){
18404             var c = e.getCharCode(), cmd;
18405           
18406             if(c > 0){
18407                 c = String.fromCharCode(c).toLowerCase();
18408                 switch(c){
18409                     case 'b':
18410                         cmd = 'bold';
18411                         break;
18412                     case 'i':
18413                         cmd = 'italic';
18414                         break;
18415                     
18416                     case 'u':
18417                         cmd = 'underline';
18418                         break;
18419                     
18420                     case 'v':
18421                         this.cleanUpPaste.defer(100, this);
18422                         return;
18423                         
18424                 }
18425                 if(cmd){
18426                     this.win.focus();
18427                     this.execCmd(cmd);
18428                     this.deferFocus();
18429                     e.preventDefault();
18430                 }
18431                 
18432             }
18433         }
18434     },
18435
18436     // private
18437     fixKeys : function(){ // load time branching for fastest keydown performance
18438         if(Roo.isIE){
18439             return function(e){
18440                 var k = e.getKey(), r;
18441                 if(k == e.TAB){
18442                     e.stopEvent();
18443                     r = this.doc.selection.createRange();
18444                     if(r){
18445                         r.collapse(true);
18446                         r.pasteHTML('&#160;&#160;&#160;&#160;');
18447                         this.deferFocus();
18448                     }
18449                     return;
18450                 }
18451                 
18452                 if(k == e.ENTER){
18453                     r = this.doc.selection.createRange();
18454                     if(r){
18455                         var target = r.parentElement();
18456                         if(!target || target.tagName.toLowerCase() != 'li'){
18457                             e.stopEvent();
18458                             r.pasteHTML('<br />');
18459                             r.collapse(false);
18460                             r.select();
18461                         }
18462                     }
18463                 }
18464                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18465                     this.cleanUpPaste.defer(100, this);
18466                     return;
18467                 }
18468                 
18469                 
18470             };
18471         }else if(Roo.isOpera){
18472             return function(e){
18473                 var k = e.getKey();
18474                 if(k == e.TAB){
18475                     e.stopEvent();
18476                     this.win.focus();
18477                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
18478                     this.deferFocus();
18479                 }
18480                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18481                     this.cleanUpPaste.defer(100, this);
18482                     return;
18483                 }
18484                 
18485             };
18486         }else if(Roo.isSafari){
18487             return function(e){
18488                 var k = e.getKey();
18489                 
18490                 if(k == e.TAB){
18491                     e.stopEvent();
18492                     this.execCmd('InsertText','\t');
18493                     this.deferFocus();
18494                     return;
18495                 }
18496                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18497                     this.cleanUpPaste.defer(100, this);
18498                     return;
18499                 }
18500                 
18501              };
18502         }
18503     }(),
18504     
18505     getAllAncestors: function()
18506     {
18507         var p = this.getSelectedNode();
18508         var a = [];
18509         if (!p) {
18510             a.push(p); // push blank onto stack..
18511             p = this.getParentElement();
18512         }
18513         
18514         
18515         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
18516             a.push(p);
18517             p = p.parentNode;
18518         }
18519         a.push(this.doc.body);
18520         return a;
18521     },
18522     lastSel : false,
18523     lastSelNode : false,
18524     
18525     
18526     getSelection : function() 
18527     {
18528         this.assignDocWin();
18529         return Roo.isIE ? this.doc.selection : this.win.getSelection();
18530     },
18531     
18532     getSelectedNode: function() 
18533     {
18534         // this may only work on Gecko!!!
18535         
18536         // should we cache this!!!!
18537         
18538         
18539         
18540          
18541         var range = this.createRange(this.getSelection()).cloneRange();
18542         
18543         if (Roo.isIE) {
18544             var parent = range.parentElement();
18545             while (true) {
18546                 var testRange = range.duplicate();
18547                 testRange.moveToElementText(parent);
18548                 if (testRange.inRange(range)) {
18549                     break;
18550                 }
18551                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
18552                     break;
18553                 }
18554                 parent = parent.parentElement;
18555             }
18556             return parent;
18557         }
18558         
18559         // is ancestor a text element.
18560         var ac =  range.commonAncestorContainer;
18561         if (ac.nodeType == 3) {
18562             ac = ac.parentNode;
18563         }
18564         
18565         var ar = ac.childNodes;
18566          
18567         var nodes = [];
18568         var other_nodes = [];
18569         var has_other_nodes = false;
18570         for (var i=0;i<ar.length;i++) {
18571             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
18572                 continue;
18573             }
18574             // fullly contained node.
18575             
18576             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
18577                 nodes.push(ar[i]);
18578                 continue;
18579             }
18580             
18581             // probably selected..
18582             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
18583                 other_nodes.push(ar[i]);
18584                 continue;
18585             }
18586             // outer..
18587             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
18588                 continue;
18589             }
18590             
18591             
18592             has_other_nodes = true;
18593         }
18594         if (!nodes.length && other_nodes.length) {
18595             nodes= other_nodes;
18596         }
18597         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
18598             return false;
18599         }
18600         
18601         return nodes[0];
18602     },
18603     createRange: function(sel)
18604     {
18605         // this has strange effects when using with 
18606         // top toolbar - not sure if it's a great idea.
18607         //this.editor.contentWindow.focus();
18608         if (typeof sel != "undefined") {
18609             try {
18610                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
18611             } catch(e) {
18612                 return this.doc.createRange();
18613             }
18614         } else {
18615             return this.doc.createRange();
18616         }
18617     },
18618     getParentElement: function()
18619     {
18620         
18621         this.assignDocWin();
18622         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
18623         
18624         var range = this.createRange(sel);
18625          
18626         try {
18627             var p = range.commonAncestorContainer;
18628             while (p.nodeType == 3) { // text node
18629                 p = p.parentNode;
18630             }
18631             return p;
18632         } catch (e) {
18633             return null;
18634         }
18635     
18636     },
18637     /***
18638      *
18639      * Range intersection.. the hard stuff...
18640      *  '-1' = before
18641      *  '0' = hits..
18642      *  '1' = after.
18643      *         [ -- selected range --- ]
18644      *   [fail]                        [fail]
18645      *
18646      *    basically..
18647      *      if end is before start or  hits it. fail.
18648      *      if start is after end or hits it fail.
18649      *
18650      *   if either hits (but other is outside. - then it's not 
18651      *   
18652      *    
18653      **/
18654     
18655     
18656     // @see http://www.thismuchiknow.co.uk/?p=64.
18657     rangeIntersectsNode : function(range, node)
18658     {
18659         var nodeRange = node.ownerDocument.createRange();
18660         try {
18661             nodeRange.selectNode(node);
18662         } catch (e) {
18663             nodeRange.selectNodeContents(node);
18664         }
18665     
18666         var rangeStartRange = range.cloneRange();
18667         rangeStartRange.collapse(true);
18668     
18669         var rangeEndRange = range.cloneRange();
18670         rangeEndRange.collapse(false);
18671     
18672         var nodeStartRange = nodeRange.cloneRange();
18673         nodeStartRange.collapse(true);
18674     
18675         var nodeEndRange = nodeRange.cloneRange();
18676         nodeEndRange.collapse(false);
18677     
18678         return rangeStartRange.compareBoundaryPoints(
18679                  Range.START_TO_START, nodeEndRange) == -1 &&
18680                rangeEndRange.compareBoundaryPoints(
18681                  Range.START_TO_START, nodeStartRange) == 1;
18682         
18683          
18684     },
18685     rangeCompareNode : function(range, node)
18686     {
18687         var nodeRange = node.ownerDocument.createRange();
18688         try {
18689             nodeRange.selectNode(node);
18690         } catch (e) {
18691             nodeRange.selectNodeContents(node);
18692         }
18693         
18694         
18695         range.collapse(true);
18696     
18697         nodeRange.collapse(true);
18698      
18699         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
18700         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
18701          
18702         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
18703         
18704         var nodeIsBefore   =  ss == 1;
18705         var nodeIsAfter    = ee == -1;
18706         
18707         if (nodeIsBefore && nodeIsAfter)
18708             return 0; // outer
18709         if (!nodeIsBefore && nodeIsAfter)
18710             return 1; //right trailed.
18711         
18712         if (nodeIsBefore && !nodeIsAfter)
18713             return 2;  // left trailed.
18714         // fully contined.
18715         return 3;
18716     },
18717
18718     // private? - in a new class?
18719     cleanUpPaste :  function()
18720     {
18721         // cleans up the whole document..
18722         Roo.log('cleanuppaste');
18723         
18724         this.cleanUpChildren(this.doc.body);
18725         var clean = this.cleanWordChars(this.doc.body.innerHTML);
18726         if (clean != this.doc.body.innerHTML) {
18727             this.doc.body.innerHTML = clean;
18728         }
18729         
18730     },
18731     
18732     cleanWordChars : function(input) {// change the chars to hex code
18733         var he = Roo.HtmlEditorCore;
18734         
18735         var output = input;
18736         Roo.each(he.swapCodes, function(sw) { 
18737             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
18738             
18739             output = output.replace(swapper, sw[1]);
18740         });
18741         
18742         return output;
18743     },
18744     
18745     
18746     cleanUpChildren : function (n)
18747     {
18748         if (!n.childNodes.length) {
18749             return;
18750         }
18751         for (var i = n.childNodes.length-1; i > -1 ; i--) {
18752            this.cleanUpChild(n.childNodes[i]);
18753         }
18754     },
18755     
18756     
18757         
18758     
18759     cleanUpChild : function (node)
18760     {
18761         var ed = this;
18762         //console.log(node);
18763         if (node.nodeName == "#text") {
18764             // clean up silly Windows -- stuff?
18765             return; 
18766         }
18767         if (node.nodeName == "#comment") {
18768             node.parentNode.removeChild(node);
18769             // clean up silly Windows -- stuff?
18770             return; 
18771         }
18772         var lcname = node.tagName.toLowerCase();
18773         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
18774         // whitelist of tags..
18775         
18776         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
18777             // remove node.
18778             node.parentNode.removeChild(node);
18779             return;
18780             
18781         }
18782         
18783         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
18784         
18785         // remove <a name=....> as rendering on yahoo mailer is borked with this.
18786         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
18787         
18788         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
18789         //    remove_keep_children = true;
18790         //}
18791         
18792         if (remove_keep_children) {
18793             this.cleanUpChildren(node);
18794             // inserts everything just before this node...
18795             while (node.childNodes.length) {
18796                 var cn = node.childNodes[0];
18797                 node.removeChild(cn);
18798                 node.parentNode.insertBefore(cn, node);
18799             }
18800             node.parentNode.removeChild(node);
18801             return;
18802         }
18803         
18804         if (!node.attributes || !node.attributes.length) {
18805             this.cleanUpChildren(node);
18806             return;
18807         }
18808         
18809         function cleanAttr(n,v)
18810         {
18811             
18812             if (v.match(/^\./) || v.match(/^\//)) {
18813                 return;
18814             }
18815             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
18816                 return;
18817             }
18818             if (v.match(/^#/)) {
18819                 return;
18820             }
18821 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
18822             node.removeAttribute(n);
18823             
18824         }
18825         
18826         var cwhite = this.cwhite;
18827         var cblack = this.cblack;
18828             
18829         function cleanStyle(n,v)
18830         {
18831             if (v.match(/expression/)) { //XSS?? should we even bother..
18832                 node.removeAttribute(n);
18833                 return;
18834             }
18835             
18836             var parts = v.split(/;/);
18837             var clean = [];
18838             
18839             Roo.each(parts, function(p) {
18840                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
18841                 if (!p.length) {
18842                     return true;
18843                 }
18844                 var l = p.split(':').shift().replace(/\s+/g,'');
18845                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
18846                 
18847                 if ( cwhite.length && cblack.indexOf(l) > -1) {
18848 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18849                     //node.removeAttribute(n);
18850                     return true;
18851                 }
18852                 //Roo.log()
18853                 // only allow 'c whitelisted system attributes'
18854                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
18855 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18856                     //node.removeAttribute(n);
18857                     return true;
18858                 }
18859                 
18860                 
18861                  
18862                 
18863                 clean.push(p);
18864                 return true;
18865             });
18866             if (clean.length) { 
18867                 node.setAttribute(n, clean.join(';'));
18868             } else {
18869                 node.removeAttribute(n);
18870             }
18871             
18872         }
18873         
18874         
18875         for (var i = node.attributes.length-1; i > -1 ; i--) {
18876             var a = node.attributes[i];
18877             //console.log(a);
18878             
18879             if (a.name.toLowerCase().substr(0,2)=='on')  {
18880                 node.removeAttribute(a.name);
18881                 continue;
18882             }
18883             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
18884                 node.removeAttribute(a.name);
18885                 continue;
18886             }
18887             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
18888                 cleanAttr(a.name,a.value); // fixme..
18889                 continue;
18890             }
18891             if (a.name == 'style') {
18892                 cleanStyle(a.name,a.value);
18893                 continue;
18894             }
18895             /// clean up MS crap..
18896             // tecnically this should be a list of valid class'es..
18897             
18898             
18899             if (a.name == 'class') {
18900                 if (a.value.match(/^Mso/)) {
18901                     node.className = '';
18902                 }
18903                 
18904                 if (a.value.match(/body/)) {
18905                     node.className = '';
18906                 }
18907                 continue;
18908             }
18909             
18910             // style cleanup!?
18911             // class cleanup?
18912             
18913         }
18914         
18915         
18916         this.cleanUpChildren(node);
18917         
18918         
18919     },
18920     /**
18921      * Clean up MS wordisms...
18922      */
18923     cleanWord : function(node)
18924     {
18925         var _t = this;
18926         var cleanWordChildren = function()
18927         {
18928             if (!node.childNodes.length) {
18929                 return;
18930             }
18931             for (var i = node.childNodes.length-1; i > -1 ; i--) {
18932                _t.cleanWord(node.childNodes[i]);
18933             }
18934         }
18935         
18936         
18937         if (!node) {
18938             this.cleanWord(this.doc.body);
18939             return;
18940         }
18941         if (node.nodeName == "#text") {
18942             // clean up silly Windows -- stuff?
18943             return; 
18944         }
18945         if (node.nodeName == "#comment") {
18946             node.parentNode.removeChild(node);
18947             // clean up silly Windows -- stuff?
18948             return; 
18949         }
18950         
18951         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
18952             node.parentNode.removeChild(node);
18953             return;
18954         }
18955         
18956         // remove - but keep children..
18957         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
18958             while (node.childNodes.length) {
18959                 var cn = node.childNodes[0];
18960                 node.removeChild(cn);
18961                 node.parentNode.insertBefore(cn, node);
18962             }
18963             node.parentNode.removeChild(node);
18964             cleanWordChildren();
18965             return;
18966         }
18967         // clean styles
18968         if (node.className.length) {
18969             
18970             var cn = node.className.split(/\W+/);
18971             var cna = [];
18972             Roo.each(cn, function(cls) {
18973                 if (cls.match(/Mso[a-zA-Z]+/)) {
18974                     return;
18975                 }
18976                 cna.push(cls);
18977             });
18978             node.className = cna.length ? cna.join(' ') : '';
18979             if (!cna.length) {
18980                 node.removeAttribute("class");
18981             }
18982         }
18983         
18984         if (node.hasAttribute("lang")) {
18985             node.removeAttribute("lang");
18986         }
18987         
18988         if (node.hasAttribute("style")) {
18989             
18990             var styles = node.getAttribute("style").split(";");
18991             var nstyle = [];
18992             Roo.each(styles, function(s) {
18993                 if (!s.match(/:/)) {
18994                     return;
18995                 }
18996                 var kv = s.split(":");
18997                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
18998                     return;
18999                 }
19000                 // what ever is left... we allow.
19001                 nstyle.push(s);
19002             });
19003             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19004             if (!nstyle.length) {
19005                 node.removeAttribute('style');
19006             }
19007         }
19008         
19009         cleanWordChildren();
19010         
19011         
19012     },
19013     domToHTML : function(currentElement, depth, nopadtext) {
19014         
19015         depth = depth || 0;
19016         nopadtext = nopadtext || false;
19017     
19018         if (!currentElement) {
19019             return this.domToHTML(this.doc.body);
19020         }
19021         
19022         //Roo.log(currentElement);
19023         var j;
19024         var allText = false;
19025         var nodeName = currentElement.nodeName;
19026         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
19027         
19028         if  (nodeName == '#text') {
19029             
19030             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
19031         }
19032         
19033         
19034         var ret = '';
19035         if (nodeName != 'BODY') {
19036              
19037             var i = 0;
19038             // Prints the node tagName, such as <A>, <IMG>, etc
19039             if (tagName) {
19040                 var attr = [];
19041                 for(i = 0; i < currentElement.attributes.length;i++) {
19042                     // quoting?
19043                     var aname = currentElement.attributes.item(i).name;
19044                     if (!currentElement.attributes.item(i).value.length) {
19045                         continue;
19046                     }
19047                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
19048                 }
19049                 
19050                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
19051             } 
19052             else {
19053                 
19054                 // eack
19055             }
19056         } else {
19057             tagName = false;
19058         }
19059         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
19060             return ret;
19061         }
19062         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
19063             nopadtext = true;
19064         }
19065         
19066         
19067         // Traverse the tree
19068         i = 0;
19069         var currentElementChild = currentElement.childNodes.item(i);
19070         var allText = true;
19071         var innerHTML  = '';
19072         lastnode = '';
19073         while (currentElementChild) {
19074             // Formatting code (indent the tree so it looks nice on the screen)
19075             var nopad = nopadtext;
19076             if (lastnode == 'SPAN') {
19077                 nopad  = true;
19078             }
19079             // text
19080             if  (currentElementChild.nodeName == '#text') {
19081                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
19082                 toadd = nopadtext ? toadd : toadd.trim();
19083                 if (!nopad && toadd.length > 80) {
19084                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
19085                 }
19086                 innerHTML  += toadd;
19087                 
19088                 i++;
19089                 currentElementChild = currentElement.childNodes.item(i);
19090                 lastNode = '';
19091                 continue;
19092             }
19093             allText = false;
19094             
19095             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
19096                 
19097             // Recursively traverse the tree structure of the child node
19098             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
19099             lastnode = currentElementChild.nodeName;
19100             i++;
19101             currentElementChild=currentElement.childNodes.item(i);
19102         }
19103         
19104         ret += innerHTML;
19105         
19106         if (!allText) {
19107                 // The remaining code is mostly for formatting the tree
19108             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
19109         }
19110         
19111         
19112         if (tagName) {
19113             ret+= "</"+tagName+">";
19114         }
19115         return ret;
19116         
19117     },
19118         
19119     applyBlacklists : function()
19120     {
19121         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
19122         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
19123         
19124         this.white = [];
19125         this.black = [];
19126         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
19127             if (b.indexOf(tag) > -1) {
19128                 return;
19129             }
19130             this.white.push(tag);
19131             
19132         }, this);
19133         
19134         Roo.each(w, function(tag) {
19135             if (b.indexOf(tag) > -1) {
19136                 return;
19137             }
19138             if (this.white.indexOf(tag) > -1) {
19139                 return;
19140             }
19141             this.white.push(tag);
19142             
19143         }, this);
19144         
19145         
19146         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
19147             if (w.indexOf(tag) > -1) {
19148                 return;
19149             }
19150             this.black.push(tag);
19151             
19152         }, this);
19153         
19154         Roo.each(b, function(tag) {
19155             if (w.indexOf(tag) > -1) {
19156                 return;
19157             }
19158             if (this.black.indexOf(tag) > -1) {
19159                 return;
19160             }
19161             this.black.push(tag);
19162             
19163         }, this);
19164         
19165         
19166         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
19167         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
19168         
19169         this.cwhite = [];
19170         this.cblack = [];
19171         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
19172             if (b.indexOf(tag) > -1) {
19173                 return;
19174             }
19175             this.cwhite.push(tag);
19176             
19177         }, this);
19178         
19179         Roo.each(w, function(tag) {
19180             if (b.indexOf(tag) > -1) {
19181                 return;
19182             }
19183             if (this.cwhite.indexOf(tag) > -1) {
19184                 return;
19185             }
19186             this.cwhite.push(tag);
19187             
19188         }, this);
19189         
19190         
19191         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
19192             if (w.indexOf(tag) > -1) {
19193                 return;
19194             }
19195             this.cblack.push(tag);
19196             
19197         }, this);
19198         
19199         Roo.each(b, function(tag) {
19200             if (w.indexOf(tag) > -1) {
19201                 return;
19202             }
19203             if (this.cblack.indexOf(tag) > -1) {
19204                 return;
19205             }
19206             this.cblack.push(tag);
19207             
19208         }, this);
19209     },
19210     
19211     setStylesheets : function(stylesheets)
19212     {
19213         if(typeof(stylesheets) == 'string'){
19214             Roo.get(this.iframe.contentDocument.head).createChild({
19215                 tag : 'link',
19216                 rel : 'stylesheet',
19217                 type : 'text/css',
19218                 href : stylesheets
19219             });
19220             
19221             return;
19222         }
19223         var _this = this;
19224      
19225         Roo.each(stylesheets, function(s) {
19226             if(!s.length){
19227                 return;
19228             }
19229             
19230             Roo.get(_this.iframe.contentDocument.head).createChild({
19231                 tag : 'link',
19232                 rel : 'stylesheet',
19233                 type : 'text/css',
19234                 href : s
19235             });
19236         });
19237
19238         
19239     },
19240     
19241     removeStylesheets : function()
19242     {
19243         var _this = this;
19244         
19245         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
19246             s.remove();
19247         });
19248     }
19249     
19250     // hide stuff that is not compatible
19251     /**
19252      * @event blur
19253      * @hide
19254      */
19255     /**
19256      * @event change
19257      * @hide
19258      */
19259     /**
19260      * @event focus
19261      * @hide
19262      */
19263     /**
19264      * @event specialkey
19265      * @hide
19266      */
19267     /**
19268      * @cfg {String} fieldClass @hide
19269      */
19270     /**
19271      * @cfg {String} focusClass @hide
19272      */
19273     /**
19274      * @cfg {String} autoCreate @hide
19275      */
19276     /**
19277      * @cfg {String} inputType @hide
19278      */
19279     /**
19280      * @cfg {String} invalidClass @hide
19281      */
19282     /**
19283      * @cfg {String} invalidText @hide
19284      */
19285     /**
19286      * @cfg {String} msgFx @hide
19287      */
19288     /**
19289      * @cfg {String} validateOnBlur @hide
19290      */
19291 });
19292
19293 Roo.HtmlEditorCore.white = [
19294         'area', 'br', 'img', 'input', 'hr', 'wbr',
19295         
19296        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
19297        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
19298        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
19299        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
19300        'table',   'ul',         'xmp', 
19301        
19302        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
19303       'thead',   'tr', 
19304      
19305       'dir', 'menu', 'ol', 'ul', 'dl',
19306        
19307       'embed',  'object'
19308 ];
19309
19310
19311 Roo.HtmlEditorCore.black = [
19312     //    'embed',  'object', // enable - backend responsiblity to clean thiese
19313         'applet', // 
19314         'base',   'basefont', 'bgsound', 'blink',  'body', 
19315         'frame',  'frameset', 'head',    'html',   'ilayer', 
19316         'iframe', 'layer',  'link',     'meta',    'object',   
19317         'script', 'style' ,'title',  'xml' // clean later..
19318 ];
19319 Roo.HtmlEditorCore.clean = [
19320     'script', 'style', 'title', 'xml'
19321 ];
19322 Roo.HtmlEditorCore.remove = [
19323     'font'
19324 ];
19325 // attributes..
19326
19327 Roo.HtmlEditorCore.ablack = [
19328     'on'
19329 ];
19330     
19331 Roo.HtmlEditorCore.aclean = [ 
19332     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
19333 ];
19334
19335 // protocols..
19336 Roo.HtmlEditorCore.pwhite= [
19337         'http',  'https',  'mailto'
19338 ];
19339
19340 // white listed style attributes.
19341 Roo.HtmlEditorCore.cwhite= [
19342       //  'text-align', /// default is to allow most things..
19343       
19344          
19345 //        'font-size'//??
19346 ];
19347
19348 // black listed style attributes.
19349 Roo.HtmlEditorCore.cblack= [
19350       //  'font-size' -- this can be set by the project 
19351 ];
19352
19353
19354 Roo.HtmlEditorCore.swapCodes   =[ 
19355     [    8211, "--" ], 
19356     [    8212, "--" ], 
19357     [    8216,  "'" ],  
19358     [    8217, "'" ],  
19359     [    8220, '"' ],  
19360     [    8221, '"' ],  
19361     [    8226, "*" ],  
19362     [    8230, "..." ]
19363 ]; 
19364
19365     /*
19366  * - LGPL
19367  *
19368  * HtmlEditor
19369  * 
19370  */
19371
19372 /**
19373  * @class Roo.bootstrap.HtmlEditor
19374  * @extends Roo.bootstrap.TextArea
19375  * Bootstrap HtmlEditor class
19376
19377  * @constructor
19378  * Create a new HtmlEditor
19379  * @param {Object} config The config object
19380  */
19381
19382 Roo.bootstrap.HtmlEditor = function(config){
19383     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
19384     if (!this.toolbars) {
19385         this.toolbars = [];
19386     }
19387     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
19388     this.addEvents({
19389             /**
19390              * @event initialize
19391              * Fires when the editor is fully initialized (including the iframe)
19392              * @param {HtmlEditor} this
19393              */
19394             initialize: true,
19395             /**
19396              * @event activate
19397              * Fires when the editor is first receives the focus. Any insertion must wait
19398              * until after this event.
19399              * @param {HtmlEditor} this
19400              */
19401             activate: true,
19402              /**
19403              * @event beforesync
19404              * Fires before the textarea is updated with content from the editor iframe. Return false
19405              * to cancel the sync.
19406              * @param {HtmlEditor} this
19407              * @param {String} html
19408              */
19409             beforesync: true,
19410              /**
19411              * @event beforepush
19412              * Fires before the iframe editor is updated with content from the textarea. Return false
19413              * to cancel the push.
19414              * @param {HtmlEditor} this
19415              * @param {String} html
19416              */
19417             beforepush: true,
19418              /**
19419              * @event sync
19420              * Fires when the textarea is updated with content from the editor iframe.
19421              * @param {HtmlEditor} this
19422              * @param {String} html
19423              */
19424             sync: true,
19425              /**
19426              * @event push
19427              * Fires when the iframe editor is updated with content from the textarea.
19428              * @param {HtmlEditor} this
19429              * @param {String} html
19430              */
19431             push: true,
19432              /**
19433              * @event editmodechange
19434              * Fires when the editor switches edit modes
19435              * @param {HtmlEditor} this
19436              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
19437              */
19438             editmodechange: true,
19439             /**
19440              * @event editorevent
19441              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19442              * @param {HtmlEditor} this
19443              */
19444             editorevent: true,
19445             /**
19446              * @event firstfocus
19447              * Fires when on first focus - needed by toolbars..
19448              * @param {HtmlEditor} this
19449              */
19450             firstfocus: true,
19451             /**
19452              * @event autosave
19453              * Auto save the htmlEditor value as a file into Events
19454              * @param {HtmlEditor} this
19455              */
19456             autosave: true,
19457             /**
19458              * @event savedpreview
19459              * preview the saved version of htmlEditor
19460              * @param {HtmlEditor} this
19461              */
19462             savedpreview: true
19463         });
19464 };
19465
19466
19467 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
19468     
19469     
19470       /**
19471      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
19472      */
19473     toolbars : false,
19474    
19475      /**
19476      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19477      *                        Roo.resizable.
19478      */
19479     resizable : false,
19480      /**
19481      * @cfg {Number} height (in pixels)
19482      */   
19483     height: 300,
19484    /**
19485      * @cfg {Number} width (in pixels)
19486      */   
19487     width: false,
19488     
19489     /**
19490      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19491      * 
19492      */
19493     stylesheets: false,
19494     
19495     // id of frame..
19496     frameId: false,
19497     
19498     // private properties
19499     validationEvent : false,
19500     deferHeight: true,
19501     initialized : false,
19502     activated : false,
19503     
19504     onFocus : Roo.emptyFn,
19505     iframePad:3,
19506     hideMode:'offsets',
19507     
19508     
19509     tbContainer : false,
19510     
19511     toolbarContainer :function() {
19512         return this.wrap.select('.x-html-editor-tb',true).first();
19513     },
19514
19515     /**
19516      * Protected method that will not generally be called directly. It
19517      * is called when the editor creates its toolbar. Override this method if you need to
19518      * add custom toolbar buttons.
19519      * @param {HtmlEditor} editor
19520      */
19521     createToolbar : function(){
19522         
19523         Roo.log("create toolbars");
19524         
19525         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
19526         this.toolbars[0].render(this.toolbarContainer());
19527         
19528         return;
19529         
19530 //        if (!editor.toolbars || !editor.toolbars.length) {
19531 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
19532 //        }
19533 //        
19534 //        for (var i =0 ; i < editor.toolbars.length;i++) {
19535 //            editor.toolbars[i] = Roo.factory(
19536 //                    typeof(editor.toolbars[i]) == 'string' ?
19537 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
19538 //                Roo.bootstrap.HtmlEditor);
19539 //            editor.toolbars[i].init(editor);
19540 //        }
19541     },
19542
19543      
19544     // private
19545     onRender : function(ct, position)
19546     {
19547        // Roo.log("Call onRender: " + this.xtype);
19548         var _t = this;
19549         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
19550       
19551         this.wrap = this.inputEl().wrap({
19552             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
19553         });
19554         
19555         this.editorcore.onRender(ct, position);
19556          
19557         if (this.resizable) {
19558             this.resizeEl = new Roo.Resizable(this.wrap, {
19559                 pinned : true,
19560                 wrap: true,
19561                 dynamic : true,
19562                 minHeight : this.height,
19563                 height: this.height,
19564                 handles : this.resizable,
19565                 width: this.width,
19566                 listeners : {
19567                     resize : function(r, w, h) {
19568                         _t.onResize(w,h); // -something
19569                     }
19570                 }
19571             });
19572             
19573         }
19574         this.createToolbar(this);
19575        
19576         
19577         if(!this.width && this.resizable){
19578             this.setSize(this.wrap.getSize());
19579         }
19580         if (this.resizeEl) {
19581             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
19582             // should trigger onReize..
19583         }
19584         
19585     },
19586
19587     // private
19588     onResize : function(w, h)
19589     {
19590         Roo.log('resize: ' +w + ',' + h );
19591         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
19592         var ew = false;
19593         var eh = false;
19594         
19595         if(this.inputEl() ){
19596             if(typeof w == 'number'){
19597                 var aw = w - this.wrap.getFrameWidth('lr');
19598                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
19599                 ew = aw;
19600             }
19601             if(typeof h == 'number'){
19602                  var tbh = -11;  // fixme it needs to tool bar size!
19603                 for (var i =0; i < this.toolbars.length;i++) {
19604                     // fixme - ask toolbars for heights?
19605                     tbh += this.toolbars[i].el.getHeight();
19606                     //if (this.toolbars[i].footer) {
19607                     //    tbh += this.toolbars[i].footer.el.getHeight();
19608                     //}
19609                 }
19610               
19611                 
19612                 
19613                 
19614                 
19615                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
19616                 ah -= 5; // knock a few pixes off for look..
19617                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
19618                 var eh = ah;
19619             }
19620         }
19621         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
19622         this.editorcore.onResize(ew,eh);
19623         
19624     },
19625
19626     /**
19627      * Toggles the editor between standard and source edit mode.
19628      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19629      */
19630     toggleSourceEdit : function(sourceEditMode)
19631     {
19632         this.editorcore.toggleSourceEdit(sourceEditMode);
19633         
19634         if(this.editorcore.sourceEditMode){
19635             Roo.log('editor - showing textarea');
19636             
19637 //            Roo.log('in');
19638 //            Roo.log(this.syncValue());
19639             this.syncValue();
19640             this.inputEl().removeClass(['hide', 'x-hidden']);
19641             this.inputEl().dom.removeAttribute('tabIndex');
19642             this.inputEl().focus();
19643         }else{
19644             Roo.log('editor - hiding textarea');
19645 //            Roo.log('out')
19646 //            Roo.log(this.pushValue()); 
19647             this.pushValue();
19648             
19649             this.inputEl().addClass(['hide', 'x-hidden']);
19650             this.inputEl().dom.setAttribute('tabIndex', -1);
19651             //this.deferFocus();
19652         }
19653          
19654         if(this.resizable){
19655             this.setSize(this.wrap.getSize());
19656         }
19657         
19658         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
19659     },
19660  
19661     // private (for BoxComponent)
19662     adjustSize : Roo.BoxComponent.prototype.adjustSize,
19663
19664     // private (for BoxComponent)
19665     getResizeEl : function(){
19666         return this.wrap;
19667     },
19668
19669     // private (for BoxComponent)
19670     getPositionEl : function(){
19671         return this.wrap;
19672     },
19673
19674     // private
19675     initEvents : function(){
19676         this.originalValue = this.getValue();
19677     },
19678
19679 //    /**
19680 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19681 //     * @method
19682 //     */
19683 //    markInvalid : Roo.emptyFn,
19684 //    /**
19685 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19686 //     * @method
19687 //     */
19688 //    clearInvalid : Roo.emptyFn,
19689
19690     setValue : function(v){
19691         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
19692         this.editorcore.pushValue();
19693     },
19694
19695      
19696     // private
19697     deferFocus : function(){
19698         this.focus.defer(10, this);
19699     },
19700
19701     // doc'ed in Field
19702     focus : function(){
19703         this.editorcore.focus();
19704         
19705     },
19706       
19707
19708     // private
19709     onDestroy : function(){
19710         
19711         
19712         
19713         if(this.rendered){
19714             
19715             for (var i =0; i < this.toolbars.length;i++) {
19716                 // fixme - ask toolbars for heights?
19717                 this.toolbars[i].onDestroy();
19718             }
19719             
19720             this.wrap.dom.innerHTML = '';
19721             this.wrap.remove();
19722         }
19723     },
19724
19725     // private
19726     onFirstFocus : function(){
19727         //Roo.log("onFirstFocus");
19728         this.editorcore.onFirstFocus();
19729          for (var i =0; i < this.toolbars.length;i++) {
19730             this.toolbars[i].onFirstFocus();
19731         }
19732         
19733     },
19734     
19735     // private
19736     syncValue : function()
19737     {   
19738         this.editorcore.syncValue();
19739     },
19740     
19741     pushValue : function()
19742     {   
19743         this.editorcore.pushValue();
19744     }
19745      
19746     
19747     // hide stuff that is not compatible
19748     /**
19749      * @event blur
19750      * @hide
19751      */
19752     /**
19753      * @event change
19754      * @hide
19755      */
19756     /**
19757      * @event focus
19758      * @hide
19759      */
19760     /**
19761      * @event specialkey
19762      * @hide
19763      */
19764     /**
19765      * @cfg {String} fieldClass @hide
19766      */
19767     /**
19768      * @cfg {String} focusClass @hide
19769      */
19770     /**
19771      * @cfg {String} autoCreate @hide
19772      */
19773     /**
19774      * @cfg {String} inputType @hide
19775      */
19776     /**
19777      * @cfg {String} invalidClass @hide
19778      */
19779     /**
19780      * @cfg {String} invalidText @hide
19781      */
19782     /**
19783      * @cfg {String} msgFx @hide
19784      */
19785     /**
19786      * @cfg {String} validateOnBlur @hide
19787      */
19788 });
19789  
19790     
19791    
19792    
19793    
19794       
19795 Roo.namespace('Roo.bootstrap.htmleditor');
19796 /**
19797  * @class Roo.bootstrap.HtmlEditorToolbar1
19798  * Basic Toolbar
19799  * 
19800  * Usage:
19801  *
19802  new Roo.bootstrap.HtmlEditor({
19803     ....
19804     toolbars : [
19805         new Roo.bootstrap.HtmlEditorToolbar1({
19806             disable : { fonts: 1 , format: 1, ..., ... , ...],
19807             btns : [ .... ]
19808         })
19809     }
19810      
19811  * 
19812  * @cfg {Object} disable List of elements to disable..
19813  * @cfg {Array} btns List of additional buttons.
19814  * 
19815  * 
19816  * NEEDS Extra CSS? 
19817  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
19818  */
19819  
19820 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
19821 {
19822     
19823     Roo.apply(this, config);
19824     
19825     // default disabled, based on 'good practice'..
19826     this.disable = this.disable || {};
19827     Roo.applyIf(this.disable, {
19828         fontSize : true,
19829         colors : true,
19830         specialElements : true
19831     });
19832     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
19833     
19834     this.editor = config.editor;
19835     this.editorcore = config.editor.editorcore;
19836     
19837     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
19838     
19839     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
19840     // dont call parent... till later.
19841 }
19842 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
19843      
19844     bar : true,
19845     
19846     editor : false,
19847     editorcore : false,
19848     
19849     
19850     formats : [
19851         "p" ,  
19852         "h1","h2","h3","h4","h5","h6", 
19853         "pre", "code", 
19854         "abbr", "acronym", "address", "cite", "samp", "var",
19855         'div','span'
19856     ],
19857     
19858     onRender : function(ct, position)
19859     {
19860        // Roo.log("Call onRender: " + this.xtype);
19861         
19862        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
19863        Roo.log(this.el);
19864        this.el.dom.style.marginBottom = '0';
19865        var _this = this;
19866        var editorcore = this.editorcore;
19867        var editor= this.editor;
19868        
19869        var children = [];
19870        var btn = function(id,cmd , toggle, handler){
19871        
19872             var  event = toggle ? 'toggle' : 'click';
19873        
19874             var a = {
19875                 size : 'sm',
19876                 xtype: 'Button',
19877                 xns: Roo.bootstrap,
19878                 glyphicon : id,
19879                 cmd : id || cmd,
19880                 enableToggle:toggle !== false,
19881                 //html : 'submit'
19882                 pressed : toggle ? false : null,
19883                 listeners : {}
19884             }
19885             a.listeners[toggle ? 'toggle' : 'click'] = function() {
19886                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
19887             }
19888             children.push(a);
19889             return a;
19890        }
19891         
19892         var style = {
19893                 xtype: 'Button',
19894                 size : 'sm',
19895                 xns: Roo.bootstrap,
19896                 glyphicon : 'font',
19897                 //html : 'submit'
19898                 menu : {
19899                     xtype: 'Menu',
19900                     xns: Roo.bootstrap,
19901                     items:  []
19902                 }
19903         };
19904         Roo.each(this.formats, function(f) {
19905             style.menu.items.push({
19906                 xtype :'MenuItem',
19907                 xns: Roo.bootstrap,
19908                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
19909                 tagname : f,
19910                 listeners : {
19911                     click : function()
19912                     {
19913                         editorcore.insertTag(this.tagname);
19914                         editor.focus();
19915                     }
19916                 }
19917                 
19918             });
19919         });
19920          children.push(style);   
19921             
19922             
19923         btn('bold',false,true);
19924         btn('italic',false,true);
19925         btn('align-left', 'justifyleft',true);
19926         btn('align-center', 'justifycenter',true);
19927         btn('align-right' , 'justifyright',true);
19928         btn('link', false, false, function(btn) {
19929             //Roo.log("create link?");
19930             var url = prompt(this.createLinkText, this.defaultLinkValue);
19931             if(url && url != 'http:/'+'/'){
19932                 this.editorcore.relayCmd('createlink', url);
19933             }
19934         }),
19935         btn('list','insertunorderedlist',true);
19936         btn('pencil', false,true, function(btn){
19937                 Roo.log(this);
19938                 
19939                 this.toggleSourceEdit(btn.pressed);
19940         });
19941         /*
19942         var cog = {
19943                 xtype: 'Button',
19944                 size : 'sm',
19945                 xns: Roo.bootstrap,
19946                 glyphicon : 'cog',
19947                 //html : 'submit'
19948                 menu : {
19949                     xtype: 'Menu',
19950                     xns: Roo.bootstrap,
19951                     items:  []
19952                 }
19953         };
19954         
19955         cog.menu.items.push({
19956             xtype :'MenuItem',
19957             xns: Roo.bootstrap,
19958             html : Clean styles,
19959             tagname : f,
19960             listeners : {
19961                 click : function()
19962                 {
19963                     editorcore.insertTag(this.tagname);
19964                     editor.focus();
19965                 }
19966             }
19967             
19968         });
19969        */
19970         
19971          
19972        this.xtype = 'NavSimplebar';
19973         
19974         for(var i=0;i< children.length;i++) {
19975             
19976             this.buttons.add(this.addxtypeChild(children[i]));
19977             
19978         }
19979         
19980         editor.on('editorevent', this.updateToolbar, this);
19981     },
19982     onBtnClick : function(id)
19983     {
19984        this.editorcore.relayCmd(id);
19985        this.editorcore.focus();
19986     },
19987     
19988     /**
19989      * Protected method that will not generally be called directly. It triggers
19990      * a toolbar update by reading the markup state of the current selection in the editor.
19991      */
19992     updateToolbar: function(){
19993
19994         if(!this.editorcore.activated){
19995             this.editor.onFirstFocus(); // is this neeed?
19996             return;
19997         }
19998
19999         var btns = this.buttons; 
20000         var doc = this.editorcore.doc;
20001         btns.get('bold').setActive(doc.queryCommandState('bold'));
20002         btns.get('italic').setActive(doc.queryCommandState('italic'));
20003         //btns.get('underline').setActive(doc.queryCommandState('underline'));
20004         
20005         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
20006         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
20007         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
20008         
20009         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
20010         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
20011          /*
20012         
20013         var ans = this.editorcore.getAllAncestors();
20014         if (this.formatCombo) {
20015             
20016             
20017             var store = this.formatCombo.store;
20018             this.formatCombo.setValue("");
20019             for (var i =0; i < ans.length;i++) {
20020                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
20021                     // select it..
20022                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
20023                     break;
20024                 }
20025             }
20026         }
20027         
20028         
20029         
20030         // hides menus... - so this cant be on a menu...
20031         Roo.bootstrap.MenuMgr.hideAll();
20032         */
20033         Roo.bootstrap.MenuMgr.hideAll();
20034         //this.editorsyncValue();
20035     },
20036     onFirstFocus: function() {
20037         this.buttons.each(function(item){
20038            item.enable();
20039         });
20040     },
20041     toggleSourceEdit : function(sourceEditMode){
20042         
20043           
20044         if(sourceEditMode){
20045             Roo.log("disabling buttons");
20046            this.buttons.each( function(item){
20047                 if(item.cmd != 'pencil'){
20048                     item.disable();
20049                 }
20050             });
20051           
20052         }else{
20053             Roo.log("enabling buttons");
20054             if(this.editorcore.initialized){
20055                 this.buttons.each( function(item){
20056                     item.enable();
20057                 });
20058             }
20059             
20060         }
20061         Roo.log("calling toggole on editor");
20062         // tell the editor that it's been pressed..
20063         this.editor.toggleSourceEdit(sourceEditMode);
20064        
20065     }
20066 });
20067
20068
20069
20070
20071
20072 /**
20073  * @class Roo.bootstrap.Table.AbstractSelectionModel
20074  * @extends Roo.util.Observable
20075  * Abstract base class for grid SelectionModels.  It provides the interface that should be
20076  * implemented by descendant classes.  This class should not be directly instantiated.
20077  * @constructor
20078  */
20079 Roo.bootstrap.Table.AbstractSelectionModel = function(){
20080     this.locked = false;
20081     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
20082 };
20083
20084
20085 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
20086     /** @ignore Called by the grid automatically. Do not call directly. */
20087     init : function(grid){
20088         this.grid = grid;
20089         this.initEvents();
20090     },
20091
20092     /**
20093      * Locks the selections.
20094      */
20095     lock : function(){
20096         this.locked = true;
20097     },
20098
20099     /**
20100      * Unlocks the selections.
20101      */
20102     unlock : function(){
20103         this.locked = false;
20104     },
20105
20106     /**
20107      * Returns true if the selections are locked.
20108      * @return {Boolean}
20109      */
20110     isLocked : function(){
20111         return this.locked;
20112     }
20113 });
20114 /**
20115  * @extends Roo.bootstrap.Table.AbstractSelectionModel
20116  * @class Roo.bootstrap.Table.RowSelectionModel
20117  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
20118  * It supports multiple selections and keyboard selection/navigation. 
20119  * @constructor
20120  * @param {Object} config
20121  */
20122
20123 Roo.bootstrap.Table.RowSelectionModel = function(config){
20124     Roo.apply(this, config);
20125     this.selections = new Roo.util.MixedCollection(false, function(o){
20126         return o.id;
20127     });
20128
20129     this.last = false;
20130     this.lastActive = false;
20131
20132     this.addEvents({
20133         /**
20134              * @event selectionchange
20135              * Fires when the selection changes
20136              * @param {SelectionModel} this
20137              */
20138             "selectionchange" : true,
20139         /**
20140              * @event afterselectionchange
20141              * Fires after the selection changes (eg. by key press or clicking)
20142              * @param {SelectionModel} this
20143              */
20144             "afterselectionchange" : true,
20145         /**
20146              * @event beforerowselect
20147              * Fires when a row is selected being selected, return false to cancel.
20148              * @param {SelectionModel} this
20149              * @param {Number} rowIndex The selected index
20150              * @param {Boolean} keepExisting False if other selections will be cleared
20151              */
20152             "beforerowselect" : true,
20153         /**
20154              * @event rowselect
20155              * Fires when a row is selected.
20156              * @param {SelectionModel} this
20157              * @param {Number} rowIndex The selected index
20158              * @param {Roo.data.Record} r The record
20159              */
20160             "rowselect" : true,
20161         /**
20162              * @event rowdeselect
20163              * Fires when a row is deselected.
20164              * @param {SelectionModel} this
20165              * @param {Number} rowIndex The selected index
20166              */
20167         "rowdeselect" : true
20168     });
20169     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
20170     this.locked = false;
20171 };
20172
20173 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
20174     /**
20175      * @cfg {Boolean} singleSelect
20176      * True to allow selection of only one row at a time (defaults to false)
20177      */
20178     singleSelect : false,
20179
20180     // private
20181     initEvents : function(){
20182
20183         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
20184             this.grid.on("mousedown", this.handleMouseDown, this);
20185         }else{ // allow click to work like normal
20186             this.grid.on("rowclick", this.handleDragableRowClick, this);
20187         }
20188
20189         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
20190             "up" : function(e){
20191                 if(!e.shiftKey){
20192                     this.selectPrevious(e.shiftKey);
20193                 }else if(this.last !== false && this.lastActive !== false){
20194                     var last = this.last;
20195                     this.selectRange(this.last,  this.lastActive-1);
20196                     this.grid.getView().focusRow(this.lastActive);
20197                     if(last !== false){
20198                         this.last = last;
20199                     }
20200                 }else{
20201                     this.selectFirstRow();
20202                 }
20203                 this.fireEvent("afterselectionchange", this);
20204             },
20205             "down" : function(e){
20206                 if(!e.shiftKey){
20207                     this.selectNext(e.shiftKey);
20208                 }else if(this.last !== false && this.lastActive !== false){
20209                     var last = this.last;
20210                     this.selectRange(this.last,  this.lastActive+1);
20211                     this.grid.getView().focusRow(this.lastActive);
20212                     if(last !== false){
20213                         this.last = last;
20214                     }
20215                 }else{
20216                     this.selectFirstRow();
20217                 }
20218                 this.fireEvent("afterselectionchange", this);
20219             },
20220             scope: this
20221         });
20222
20223         var view = this.grid.view;
20224         view.on("refresh", this.onRefresh, this);
20225         view.on("rowupdated", this.onRowUpdated, this);
20226         view.on("rowremoved", this.onRemove, this);
20227     },
20228
20229     // private
20230     onRefresh : function(){
20231         var ds = this.grid.dataSource, i, v = this.grid.view;
20232         var s = this.selections;
20233         s.each(function(r){
20234             if((i = ds.indexOfId(r.id)) != -1){
20235                 v.onRowSelect(i);
20236             }else{
20237                 s.remove(r);
20238             }
20239         });
20240     },
20241
20242     // private
20243     onRemove : function(v, index, r){
20244         this.selections.remove(r);
20245     },
20246
20247     // private
20248     onRowUpdated : function(v, index, r){
20249         if(this.isSelected(r)){
20250             v.onRowSelect(index);
20251         }
20252     },
20253
20254     /**
20255      * Select records.
20256      * @param {Array} records The records to select
20257      * @param {Boolean} keepExisting (optional) True to keep existing selections
20258      */
20259     selectRecords : function(records, keepExisting){
20260         if(!keepExisting){
20261             this.clearSelections();
20262         }
20263         var ds = this.grid.dataSource;
20264         for(var i = 0, len = records.length; i < len; i++){
20265             this.selectRow(ds.indexOf(records[i]), true);
20266         }
20267     },
20268
20269     /**
20270      * Gets the number of selected rows.
20271      * @return {Number}
20272      */
20273     getCount : function(){
20274         return this.selections.length;
20275     },
20276
20277     /**
20278      * Selects the first row in the grid.
20279      */
20280     selectFirstRow : function(){
20281         this.selectRow(0);
20282     },
20283
20284     /**
20285      * Select the last row.
20286      * @param {Boolean} keepExisting (optional) True to keep existing selections
20287      */
20288     selectLastRow : function(keepExisting){
20289         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
20290     },
20291
20292     /**
20293      * Selects the row immediately following the last selected row.
20294      * @param {Boolean} keepExisting (optional) True to keep existing selections
20295      */
20296     selectNext : function(keepExisting){
20297         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
20298             this.selectRow(this.last+1, keepExisting);
20299             this.grid.getView().focusRow(this.last);
20300         }
20301     },
20302
20303     /**
20304      * Selects the row that precedes the last selected row.
20305      * @param {Boolean} keepExisting (optional) True to keep existing selections
20306      */
20307     selectPrevious : function(keepExisting){
20308         if(this.last){
20309             this.selectRow(this.last-1, keepExisting);
20310             this.grid.getView().focusRow(this.last);
20311         }
20312     },
20313
20314     /**
20315      * Returns the selected records
20316      * @return {Array} Array of selected records
20317      */
20318     getSelections : function(){
20319         return [].concat(this.selections.items);
20320     },
20321
20322     /**
20323      * Returns the first selected record.
20324      * @return {Record}
20325      */
20326     getSelected : function(){
20327         return this.selections.itemAt(0);
20328     },
20329
20330
20331     /**
20332      * Clears all selections.
20333      */
20334     clearSelections : function(fast){
20335         if(this.locked) return;
20336         if(fast !== true){
20337             var ds = this.grid.dataSource;
20338             var s = this.selections;
20339             s.each(function(r){
20340                 this.deselectRow(ds.indexOfId(r.id));
20341             }, this);
20342             s.clear();
20343         }else{
20344             this.selections.clear();
20345         }
20346         this.last = false;
20347     },
20348
20349
20350     /**
20351      * Selects all rows.
20352      */
20353     selectAll : function(){
20354         if(this.locked) return;
20355         this.selections.clear();
20356         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
20357             this.selectRow(i, true);
20358         }
20359     },
20360
20361     /**
20362      * Returns True if there is a selection.
20363      * @return {Boolean}
20364      */
20365     hasSelection : function(){
20366         return this.selections.length > 0;
20367     },
20368
20369     /**
20370      * Returns True if the specified row is selected.
20371      * @param {Number/Record} record The record or index of the record to check
20372      * @return {Boolean}
20373      */
20374     isSelected : function(index){
20375         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
20376         return (r && this.selections.key(r.id) ? true : false);
20377     },
20378
20379     /**
20380      * Returns True if the specified record id is selected.
20381      * @param {String} id The id of record to check
20382      * @return {Boolean}
20383      */
20384     isIdSelected : function(id){
20385         return (this.selections.key(id) ? true : false);
20386     },
20387
20388     // private
20389     handleMouseDown : function(e, t){
20390         var view = this.grid.getView(), rowIndex;
20391         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
20392             return;
20393         };
20394         if(e.shiftKey && this.last !== false){
20395             var last = this.last;
20396             this.selectRange(last, rowIndex, e.ctrlKey);
20397             this.last = last; // reset the last
20398             view.focusRow(rowIndex);
20399         }else{
20400             var isSelected = this.isSelected(rowIndex);
20401             if(e.button !== 0 && isSelected){
20402                 view.focusRow(rowIndex);
20403             }else if(e.ctrlKey && isSelected){
20404                 this.deselectRow(rowIndex);
20405             }else if(!isSelected){
20406                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
20407                 view.focusRow(rowIndex);
20408             }
20409         }
20410         this.fireEvent("afterselectionchange", this);
20411     },
20412     // private
20413     handleDragableRowClick :  function(grid, rowIndex, e) 
20414     {
20415         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
20416             this.selectRow(rowIndex, false);
20417             grid.view.focusRow(rowIndex);
20418              this.fireEvent("afterselectionchange", this);
20419         }
20420     },
20421     
20422     /**
20423      * Selects multiple rows.
20424      * @param {Array} rows Array of the indexes of the row to select
20425      * @param {Boolean} keepExisting (optional) True to keep existing selections
20426      */
20427     selectRows : function(rows, keepExisting){
20428         if(!keepExisting){
20429             this.clearSelections();
20430         }
20431         for(var i = 0, len = rows.length; i < len; i++){
20432             this.selectRow(rows[i], true);
20433         }
20434     },
20435
20436     /**
20437      * Selects a range of rows. All rows in between startRow and endRow are also selected.
20438      * @param {Number} startRow The index of the first row in the range
20439      * @param {Number} endRow The index of the last row in the range
20440      * @param {Boolean} keepExisting (optional) True to retain existing selections
20441      */
20442     selectRange : function(startRow, endRow, keepExisting){
20443         if(this.locked) return;
20444         if(!keepExisting){
20445             this.clearSelections();
20446         }
20447         if(startRow <= endRow){
20448             for(var i = startRow; i <= endRow; i++){
20449                 this.selectRow(i, true);
20450             }
20451         }else{
20452             for(var i = startRow; i >= endRow; i--){
20453                 this.selectRow(i, true);
20454             }
20455         }
20456     },
20457
20458     /**
20459      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
20460      * @param {Number} startRow The index of the first row in the range
20461      * @param {Number} endRow The index of the last row in the range
20462      */
20463     deselectRange : function(startRow, endRow, preventViewNotify){
20464         if(this.locked) return;
20465         for(var i = startRow; i <= endRow; i++){
20466             this.deselectRow(i, preventViewNotify);
20467         }
20468     },
20469
20470     /**
20471      * Selects a row.
20472      * @param {Number} row The index of the row to select
20473      * @param {Boolean} keepExisting (optional) True to keep existing selections
20474      */
20475     selectRow : function(index, keepExisting, preventViewNotify){
20476         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
20477         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
20478             if(!keepExisting || this.singleSelect){
20479                 this.clearSelections();
20480             }
20481             var r = this.grid.dataSource.getAt(index);
20482             this.selections.add(r);
20483             this.last = this.lastActive = index;
20484             if(!preventViewNotify){
20485                 this.grid.getView().onRowSelect(index);
20486             }
20487             this.fireEvent("rowselect", this, index, r);
20488             this.fireEvent("selectionchange", this);
20489         }
20490     },
20491
20492     /**
20493      * Deselects a row.
20494      * @param {Number} row The index of the row to deselect
20495      */
20496     deselectRow : function(index, preventViewNotify){
20497         if(this.locked) return;
20498         if(this.last == index){
20499             this.last = false;
20500         }
20501         if(this.lastActive == index){
20502             this.lastActive = false;
20503         }
20504         var r = this.grid.dataSource.getAt(index);
20505         this.selections.remove(r);
20506         if(!preventViewNotify){
20507             this.grid.getView().onRowDeselect(index);
20508         }
20509         this.fireEvent("rowdeselect", this, index);
20510         this.fireEvent("selectionchange", this);
20511     },
20512
20513     // private
20514     restoreLast : function(){
20515         if(this._last){
20516             this.last = this._last;
20517         }
20518     },
20519
20520     // private
20521     acceptsNav : function(row, col, cm){
20522         return !cm.isHidden(col) && cm.isCellEditable(col, row);
20523     },
20524
20525     // private
20526     onEditorKey : function(field, e){
20527         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
20528         if(k == e.TAB){
20529             e.stopEvent();
20530             ed.completeEdit();
20531             if(e.shiftKey){
20532                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
20533             }else{
20534                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
20535             }
20536         }else if(k == e.ENTER && !e.ctrlKey){
20537             e.stopEvent();
20538             ed.completeEdit();
20539             if(e.shiftKey){
20540                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
20541             }else{
20542                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
20543             }
20544         }else if(k == e.ESC){
20545             ed.cancelEdit();
20546         }
20547         if(newCell){
20548             g.startEditing(newCell[0], newCell[1]);
20549         }
20550     }
20551 });/*
20552  * Based on:
20553  * Ext JS Library 1.1.1
20554  * Copyright(c) 2006-2007, Ext JS, LLC.
20555  *
20556  * Originally Released Under LGPL - original licence link has changed is not relivant.
20557  *
20558  * Fork - LGPL
20559  * <script type="text/javascript">
20560  */
20561  
20562 /**
20563  * @class Roo.bootstrap.PagingToolbar
20564  * @extends Roo.Row
20565  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
20566  * @constructor
20567  * Create a new PagingToolbar
20568  * @param {Object} config The config object
20569  */
20570 Roo.bootstrap.PagingToolbar = function(config)
20571 {
20572     // old args format still supported... - xtype is prefered..
20573         // created from xtype...
20574     var ds = config.dataSource;
20575     this.toolbarItems = [];
20576     if (config.items) {
20577         this.toolbarItems = config.items;
20578 //        config.items = [];
20579     }
20580     
20581     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
20582     this.ds = ds;
20583     this.cursor = 0;
20584     if (ds) { 
20585         this.bind(ds);
20586     }
20587     
20588     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
20589     
20590 };
20591
20592 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
20593     /**
20594      * @cfg {Roo.data.Store} dataSource
20595      * The underlying data store providing the paged data
20596      */
20597     /**
20598      * @cfg {String/HTMLElement/Element} container
20599      * container The id or element that will contain the toolbar
20600      */
20601     /**
20602      * @cfg {Boolean} displayInfo
20603      * True to display the displayMsg (defaults to false)
20604      */
20605     /**
20606      * @cfg {Number} pageSize
20607      * The number of records to display per page (defaults to 20)
20608      */
20609     pageSize: 20,
20610     /**
20611      * @cfg {String} displayMsg
20612      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
20613      */
20614     displayMsg : 'Displaying {0} - {1} of {2}',
20615     /**
20616      * @cfg {String} emptyMsg
20617      * The message to display when no records are found (defaults to "No data to display")
20618      */
20619     emptyMsg : 'No data to display',
20620     /**
20621      * Customizable piece of the default paging text (defaults to "Page")
20622      * @type String
20623      */
20624     beforePageText : "Page",
20625     /**
20626      * Customizable piece of the default paging text (defaults to "of %0")
20627      * @type String
20628      */
20629     afterPageText : "of {0}",
20630     /**
20631      * Customizable piece of the default paging text (defaults to "First Page")
20632      * @type String
20633      */
20634     firstText : "First Page",
20635     /**
20636      * Customizable piece of the default paging text (defaults to "Previous Page")
20637      * @type String
20638      */
20639     prevText : "Previous Page",
20640     /**
20641      * Customizable piece of the default paging text (defaults to "Next Page")
20642      * @type String
20643      */
20644     nextText : "Next Page",
20645     /**
20646      * Customizable piece of the default paging text (defaults to "Last Page")
20647      * @type String
20648      */
20649     lastText : "Last Page",
20650     /**
20651      * Customizable piece of the default paging text (defaults to "Refresh")
20652      * @type String
20653      */
20654     refreshText : "Refresh",
20655
20656     buttons : false,
20657     // private
20658     onRender : function(ct, position) 
20659     {
20660         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
20661         this.navgroup.parentId = this.id;
20662         this.navgroup.onRender(this.el, null);
20663         // add the buttons to the navgroup
20664         
20665         if(this.displayInfo){
20666             Roo.log(this.el.select('ul.navbar-nav',true).first());
20667             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
20668             this.displayEl = this.el.select('.x-paging-info', true).first();
20669 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
20670 //            this.displayEl = navel.el.select('span',true).first();
20671         }
20672         
20673         var _this = this;
20674         
20675         if(this.buttons){
20676             Roo.each(_this.buttons, function(e){
20677                Roo.factory(e).onRender(_this.el, null);
20678             });
20679         }
20680             
20681         Roo.each(_this.toolbarItems, function(e) {
20682             _this.navgroup.addItem(e);
20683         });
20684         
20685         
20686         this.first = this.navgroup.addItem({
20687             tooltip: this.firstText,
20688             cls: "prev",
20689             icon : 'fa fa-backward',
20690             disabled: true,
20691             preventDefault: true,
20692             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
20693         });
20694         
20695         this.prev =  this.navgroup.addItem({
20696             tooltip: this.prevText,
20697             cls: "prev",
20698             icon : 'fa fa-step-backward',
20699             disabled: true,
20700             preventDefault: true,
20701             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
20702         });
20703     //this.addSeparator();
20704         
20705         
20706         var field = this.navgroup.addItem( {
20707             tagtype : 'span',
20708             cls : 'x-paging-position',
20709             
20710             html : this.beforePageText  +
20711                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
20712                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
20713          } ); //?? escaped?
20714         
20715         this.field = field.el.select('input', true).first();
20716         this.field.on("keydown", this.onPagingKeydown, this);
20717         this.field.on("focus", function(){this.dom.select();});
20718     
20719     
20720         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
20721         //this.field.setHeight(18);
20722         //this.addSeparator();
20723         this.next = this.navgroup.addItem({
20724             tooltip: this.nextText,
20725             cls: "next",
20726             html : ' <i class="fa fa-step-forward">',
20727             disabled: true,
20728             preventDefault: true,
20729             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
20730         });
20731         this.last = this.navgroup.addItem({
20732             tooltip: this.lastText,
20733             icon : 'fa fa-forward',
20734             cls: "next",
20735             disabled: true,
20736             preventDefault: true,
20737             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
20738         });
20739     //this.addSeparator();
20740         this.loading = this.navgroup.addItem({
20741             tooltip: this.refreshText,
20742             icon: 'fa fa-refresh',
20743             preventDefault: true,
20744             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
20745         });
20746
20747     },
20748
20749     // private
20750     updateInfo : function(){
20751         if(this.displayEl){
20752             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
20753             var msg = count == 0 ?
20754                 this.emptyMsg :
20755                 String.format(
20756                     this.displayMsg,
20757                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
20758                 );
20759             this.displayEl.update(msg);
20760         }
20761     },
20762
20763     // private
20764     onLoad : function(ds, r, o){
20765        this.cursor = o.params ? o.params.start : 0;
20766        var d = this.getPageData(),
20767             ap = d.activePage,
20768             ps = d.pages;
20769         
20770        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
20771        this.field.dom.value = ap;
20772        this.first.setDisabled(ap == 1);
20773        this.prev.setDisabled(ap == 1);
20774        this.next.setDisabled(ap == ps);
20775        this.last.setDisabled(ap == ps);
20776        this.loading.enable();
20777        this.updateInfo();
20778     },
20779
20780     // private
20781     getPageData : function(){
20782         var total = this.ds.getTotalCount();
20783         return {
20784             total : total,
20785             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
20786             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
20787         };
20788     },
20789
20790     // private
20791     onLoadError : function(){
20792         this.loading.enable();
20793     },
20794
20795     // private
20796     onPagingKeydown : function(e){
20797         var k = e.getKey();
20798         var d = this.getPageData();
20799         if(k == e.RETURN){
20800             var v = this.field.dom.value, pageNum;
20801             if(!v || isNaN(pageNum = parseInt(v, 10))){
20802                 this.field.dom.value = d.activePage;
20803                 return;
20804             }
20805             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
20806             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20807             e.stopEvent();
20808         }
20809         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))
20810         {
20811           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
20812           this.field.dom.value = pageNum;
20813           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
20814           e.stopEvent();
20815         }
20816         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20817         {
20818           var v = this.field.dom.value, pageNum; 
20819           var increment = (e.shiftKey) ? 10 : 1;
20820           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20821             increment *= -1;
20822           if(!v || isNaN(pageNum = parseInt(v, 10))) {
20823             this.field.dom.value = d.activePage;
20824             return;
20825           }
20826           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
20827           {
20828             this.field.dom.value = parseInt(v, 10) + increment;
20829             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
20830             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20831           }
20832           e.stopEvent();
20833         }
20834     },
20835
20836     // private
20837     beforeLoad : function(){
20838         if(this.loading){
20839             this.loading.disable();
20840         }
20841     },
20842
20843     // private
20844     onClick : function(which){
20845         
20846         var ds = this.ds;
20847         if (!ds) {
20848             return;
20849         }
20850         
20851         switch(which){
20852             case "first":
20853                 ds.load({params:{start: 0, limit: this.pageSize}});
20854             break;
20855             case "prev":
20856                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
20857             break;
20858             case "next":
20859                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
20860             break;
20861             case "last":
20862                 var total = ds.getTotalCount();
20863                 var extra = total % this.pageSize;
20864                 var lastStart = extra ? (total - extra) : total-this.pageSize;
20865                 ds.load({params:{start: lastStart, limit: this.pageSize}});
20866             break;
20867             case "refresh":
20868                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
20869             break;
20870         }
20871     },
20872
20873     /**
20874      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
20875      * @param {Roo.data.Store} store The data store to unbind
20876      */
20877     unbind : function(ds){
20878         ds.un("beforeload", this.beforeLoad, this);
20879         ds.un("load", this.onLoad, this);
20880         ds.un("loadexception", this.onLoadError, this);
20881         ds.un("remove", this.updateInfo, this);
20882         ds.un("add", this.updateInfo, this);
20883         this.ds = undefined;
20884     },
20885
20886     /**
20887      * Binds the paging toolbar to the specified {@link Roo.data.Store}
20888      * @param {Roo.data.Store} store The data store to bind
20889      */
20890     bind : function(ds){
20891         ds.on("beforeload", this.beforeLoad, this);
20892         ds.on("load", this.onLoad, this);
20893         ds.on("loadexception", this.onLoadError, this);
20894         ds.on("remove", this.updateInfo, this);
20895         ds.on("add", this.updateInfo, this);
20896         this.ds = ds;
20897     }
20898 });/*
20899  * - LGPL
20900  *
20901  * element
20902  * 
20903  */
20904
20905 /**
20906  * @class Roo.bootstrap.MessageBar
20907  * @extends Roo.bootstrap.Component
20908  * Bootstrap MessageBar class
20909  * @cfg {String} html contents of the MessageBar
20910  * @cfg {String} weight (info | success | warning | danger) default info
20911  * @cfg {String} beforeClass insert the bar before the given class
20912  * @cfg {Boolean} closable (true | false) default false
20913  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
20914  * 
20915  * @constructor
20916  * Create a new Element
20917  * @param {Object} config The config object
20918  */
20919
20920 Roo.bootstrap.MessageBar = function(config){
20921     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
20922 };
20923
20924 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
20925     
20926     html: '',
20927     weight: 'info',
20928     closable: false,
20929     fixed: false,
20930     beforeClass: 'bootstrap-sticky-wrap',
20931     
20932     getAutoCreate : function(){
20933         
20934         var cfg = {
20935             tag: 'div',
20936             cls: 'alert alert-dismissable alert-' + this.weight,
20937             cn: [
20938                 {
20939                     tag: 'span',
20940                     cls: 'message',
20941                     html: this.html || ''
20942                 }
20943             ]
20944         }
20945         
20946         if(this.fixed){
20947             cfg.cls += ' alert-messages-fixed';
20948         }
20949         
20950         if(this.closable){
20951             cfg.cn.push({
20952                 tag: 'button',
20953                 cls: 'close',
20954                 html: 'x'
20955             });
20956         }
20957         
20958         return cfg;
20959     },
20960     
20961     onRender : function(ct, position)
20962     {
20963         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20964         
20965         if(!this.el){
20966             var cfg = Roo.apply({},  this.getAutoCreate());
20967             cfg.id = Roo.id();
20968             
20969             if (this.cls) {
20970                 cfg.cls += ' ' + this.cls;
20971             }
20972             if (this.style) {
20973                 cfg.style = this.style;
20974             }
20975             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
20976             
20977             this.el.setVisibilityMode(Roo.Element.DISPLAY);
20978         }
20979         
20980         this.el.select('>button.close').on('click', this.hide, this);
20981         
20982     },
20983     
20984     show : function()
20985     {
20986         if (!this.rendered) {
20987             this.render();
20988         }
20989         
20990         this.el.show();
20991         
20992         this.fireEvent('show', this);
20993         
20994     },
20995     
20996     hide : function()
20997     {
20998         if (!this.rendered) {
20999             this.render();
21000         }
21001         
21002         this.el.hide();
21003         
21004         this.fireEvent('hide', this);
21005     },
21006     
21007     update : function()
21008     {
21009 //        var e = this.el.dom.firstChild;
21010 //        
21011 //        if(this.closable){
21012 //            e = e.nextSibling;
21013 //        }
21014 //        
21015 //        e.data = this.html || '';
21016
21017         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
21018     }
21019    
21020 });
21021
21022  
21023
21024      /*
21025  * - LGPL
21026  *
21027  * Graph
21028  * 
21029  */
21030
21031
21032 /**
21033  * @class Roo.bootstrap.Graph
21034  * @extends Roo.bootstrap.Component
21035  * Bootstrap Graph class
21036 > Prameters
21037  -sm {number} sm 4
21038  -md {number} md 5
21039  @cfg {String} graphtype  bar | vbar | pie
21040  @cfg {number} g_x coodinator | centre x (pie)
21041  @cfg {number} g_y coodinator | centre y (pie)
21042  @cfg {number} g_r radius (pie)
21043  @cfg {number} g_height height of the chart (respected by all elements in the set)
21044  @cfg {number} g_width width of the chart (respected by all elements in the set)
21045  @cfg {Object} title The title of the chart
21046     
21047  -{Array}  values
21048  -opts (object) options for the chart 
21049      o {
21050      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
21051      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
21052      o vgutter (number)
21053      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.
21054      o stacked (boolean) whether or not to tread values as in a stacked bar chart
21055      o to
21056      o stretch (boolean)
21057      o }
21058  -opts (object) options for the pie
21059      o{
21060      o cut
21061      o startAngle (number)
21062      o endAngle (number)
21063      } 
21064  *
21065  * @constructor
21066  * Create a new Input
21067  * @param {Object} config The config object
21068  */
21069
21070 Roo.bootstrap.Graph = function(config){
21071     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
21072     
21073     this.addEvents({
21074         // img events
21075         /**
21076          * @event click
21077          * The img click event for the img.
21078          * @param {Roo.EventObject} e
21079          */
21080         "click" : true
21081     });
21082 };
21083
21084 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
21085     
21086     sm: 4,
21087     md: 5,
21088     graphtype: 'bar',
21089     g_height: 250,
21090     g_width: 400,
21091     g_x: 50,
21092     g_y: 50,
21093     g_r: 30,
21094     opts:{
21095         //g_colors: this.colors,
21096         g_type: 'soft',
21097         g_gutter: '20%'
21098
21099     },
21100     title : false,
21101
21102     getAutoCreate : function(){
21103         
21104         var cfg = {
21105             tag: 'div',
21106             html : null
21107         }
21108         
21109         
21110         return  cfg;
21111     },
21112
21113     onRender : function(ct,position){
21114         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
21115         this.raphael = Raphael(this.el.dom);
21116         
21117                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21118                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21119                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21120                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
21121                 /*
21122                 r.text(160, 10, "Single Series Chart").attr(txtattr);
21123                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
21124                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
21125                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
21126                 
21127                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
21128                 r.barchart(330, 10, 300, 220, data1);
21129                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
21130                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
21131                 */
21132                 
21133                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21134                 // r.barchart(30, 30, 560, 250,  xdata, {
21135                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
21136                 //     axis : "0 0 1 1",
21137                 //     axisxlabels :  xdata
21138                 //     //yvalues : cols,
21139                    
21140                 // });
21141 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21142 //        
21143 //        this.load(null,xdata,{
21144 //                axis : "0 0 1 1",
21145 //                axisxlabels :  xdata
21146 //                });
21147
21148     },
21149
21150     load : function(graphtype,xdata,opts){
21151         this.raphael.clear();
21152         if(!graphtype) {
21153             graphtype = this.graphtype;
21154         }
21155         if(!opts){
21156             opts = this.opts;
21157         }
21158         var r = this.raphael,
21159             fin = function () {
21160                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
21161             },
21162             fout = function () {
21163                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
21164             },
21165             pfin = function() {
21166                 this.sector.stop();
21167                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
21168
21169                 if (this.label) {
21170                     this.label[0].stop();
21171                     this.label[0].attr({ r: 7.5 });
21172                     this.label[1].attr({ "font-weight": 800 });
21173                 }
21174             },
21175             pfout = function() {
21176                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
21177
21178                 if (this.label) {
21179                     this.label[0].animate({ r: 5 }, 500, "bounce");
21180                     this.label[1].attr({ "font-weight": 400 });
21181                 }
21182             };
21183
21184         switch(graphtype){
21185             case 'bar':
21186                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21187                 break;
21188             case 'hbar':
21189                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21190                 break;
21191             case 'pie':
21192 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
21193 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
21194 //            
21195                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
21196                 
21197                 break;
21198
21199         }
21200         
21201         if(this.title){
21202             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
21203         }
21204         
21205     },
21206     
21207     setTitle: function(o)
21208     {
21209         this.title = o;
21210     },
21211     
21212     initEvents: function() {
21213         
21214         if(!this.href){
21215             this.el.on('click', this.onClick, this);
21216         }
21217     },
21218     
21219     onClick : function(e)
21220     {
21221         Roo.log('img onclick');
21222         this.fireEvent('click', this, e);
21223     }
21224    
21225 });
21226
21227  
21228 /*
21229  * - LGPL
21230  *
21231  * numberBox
21232  * 
21233  */
21234 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21235
21236 /**
21237  * @class Roo.bootstrap.dash.NumberBox
21238  * @extends Roo.bootstrap.Component
21239  * Bootstrap NumberBox class
21240  * @cfg {String} headline Box headline
21241  * @cfg {String} content Box content
21242  * @cfg {String} icon Box icon
21243  * @cfg {String} footer Footer text
21244  * @cfg {String} fhref Footer href
21245  * 
21246  * @constructor
21247  * Create a new NumberBox
21248  * @param {Object} config The config object
21249  */
21250
21251
21252 Roo.bootstrap.dash.NumberBox = function(config){
21253     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
21254     
21255 };
21256
21257 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
21258     
21259     headline : '',
21260     content : '',
21261     icon : '',
21262     footer : '',
21263     fhref : '',
21264     ficon : '',
21265     
21266     getAutoCreate : function(){
21267         
21268         var cfg = {
21269             tag : 'div',
21270             cls : 'small-box ',
21271             cn : [
21272                 {
21273                     tag : 'div',
21274                     cls : 'inner',
21275                     cn :[
21276                         {
21277                             tag : 'h3',
21278                             cls : 'roo-headline',
21279                             html : this.headline
21280                         },
21281                         {
21282                             tag : 'p',
21283                             cls : 'roo-content',
21284                             html : this.content
21285                         }
21286                     ]
21287                 }
21288             ]
21289         }
21290         
21291         if(this.icon){
21292             cfg.cn.push({
21293                 tag : 'div',
21294                 cls : 'icon',
21295                 cn :[
21296                     {
21297                         tag : 'i',
21298                         cls : 'ion ' + this.icon
21299                     }
21300                 ]
21301             });
21302         }
21303         
21304         if(this.footer){
21305             var footer = {
21306                 tag : 'a',
21307                 cls : 'small-box-footer',
21308                 href : this.fhref || '#',
21309                 html : this.footer
21310             };
21311             
21312             cfg.cn.push(footer);
21313             
21314         }
21315         
21316         return  cfg;
21317     },
21318
21319     onRender : function(ct,position){
21320         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
21321
21322
21323        
21324                 
21325     },
21326
21327     setHeadline: function (value)
21328     {
21329         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
21330     },
21331     
21332     setFooter: function (value, href)
21333     {
21334         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
21335         
21336         if(href){
21337             this.el.select('a.small-box-footer',true).first().attr('href', href);
21338         }
21339         
21340     },
21341
21342     setContent: function (value)
21343     {
21344         this.el.select('.roo-content',true).first().dom.innerHTML = value;
21345     },
21346
21347     initEvents: function() 
21348     {   
21349         
21350     }
21351     
21352 });
21353
21354  
21355 /*
21356  * - LGPL
21357  *
21358  * TabBox
21359  * 
21360  */
21361 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21362
21363 /**
21364  * @class Roo.bootstrap.dash.TabBox
21365  * @extends Roo.bootstrap.Component
21366  * Bootstrap TabBox class
21367  * @cfg {String} title Title of the TabBox
21368  * @cfg {String} icon Icon of the TabBox
21369  * @cfg {Boolean} showtabs (true|false) show the tabs default true
21370  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
21371  * 
21372  * @constructor
21373  * Create a new TabBox
21374  * @param {Object} config The config object
21375  */
21376
21377
21378 Roo.bootstrap.dash.TabBox = function(config){
21379     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
21380     this.addEvents({
21381         // raw events
21382         /**
21383          * @event addpane
21384          * When a pane is added
21385          * @param {Roo.bootstrap.dash.TabPane} pane
21386          */
21387         "addpane" : true,
21388         /**
21389          * @event activatepane
21390          * When a pane is activated
21391          * @param {Roo.bootstrap.dash.TabPane} pane
21392          */
21393         "activatepane" : true
21394         
21395          
21396     });
21397     
21398     this.panes = [];
21399 };
21400
21401 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
21402
21403     title : '',
21404     icon : false,
21405     showtabs : true,
21406     tabScrollable : false,
21407     
21408     getChildContainer : function()
21409     {
21410         return this.el.select('.tab-content', true).first();
21411     },
21412     
21413     getAutoCreate : function(){
21414         
21415         var header = {
21416             tag: 'li',
21417             cls: 'pull-left header',
21418             html: this.title,
21419             cn : []
21420         };
21421         
21422         if(this.icon){
21423             header.cn.push({
21424                 tag: 'i',
21425                 cls: 'fa ' + this.icon
21426             });
21427         }
21428         
21429         var h = {
21430             tag: 'ul',
21431             cls: 'nav nav-tabs pull-right',
21432             cn: [
21433                 header
21434             ]
21435         };
21436         
21437         if(this.tabScrollable){
21438             h = {
21439                 tag: 'div',
21440                 cls: 'tab-header',
21441                 cn: [
21442                     {
21443                         tag: 'ul',
21444                         cls: 'nav nav-tabs pull-right',
21445                         cn: [
21446                             header
21447                         ]
21448                     }
21449                 ]
21450             }
21451         }
21452         
21453         var cfg = {
21454             tag: 'div',
21455             cls: 'nav-tabs-custom',
21456             cn: [
21457                 h,
21458                 {
21459                     tag: 'div',
21460                     cls: 'tab-content no-padding',
21461                     cn: []
21462                 }
21463             ]
21464         }
21465
21466         return  cfg;
21467     },
21468     initEvents : function()
21469     {
21470         //Roo.log('add add pane handler');
21471         this.on('addpane', this.onAddPane, this);
21472     },
21473      /**
21474      * Updates the box title
21475      * @param {String} html to set the title to.
21476      */
21477     setTitle : function(value)
21478     {
21479         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
21480     },
21481     onAddPane : function(pane)
21482     {
21483         this.panes.push(pane);
21484         //Roo.log('addpane');
21485         //Roo.log(pane);
21486         // tabs are rendere left to right..
21487         if(!this.showtabs){
21488             return;
21489         }
21490         
21491         var ctr = this.el.select('.nav-tabs', true).first();
21492          
21493          
21494         var existing = ctr.select('.nav-tab',true);
21495         var qty = existing.getCount();;
21496         
21497         
21498         var tab = ctr.createChild({
21499             tag : 'li',
21500             cls : 'nav-tab' + (qty ? '' : ' active'),
21501             cn : [
21502                 {
21503                     tag : 'a',
21504                     href:'#',
21505                     html : pane.title
21506                 }
21507             ]
21508         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
21509         pane.tab = tab;
21510         
21511         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
21512         if (!qty) {
21513             pane.el.addClass('active');
21514         }
21515         
21516                 
21517     },
21518     onTabClick : function(ev,un,ob,pane)
21519     {
21520         //Roo.log('tab - prev default');
21521         ev.preventDefault();
21522         
21523         
21524         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
21525         pane.tab.addClass('active');
21526         //Roo.log(pane.title);
21527         this.getChildContainer().select('.tab-pane',true).removeClass('active');
21528         // technically we should have a deactivate event.. but maybe add later.
21529         // and it should not de-activate the selected tab...
21530         this.fireEvent('activatepane', pane);
21531         pane.el.addClass('active');
21532         pane.fireEvent('activate');
21533         
21534         
21535     },
21536     
21537     getActivePane : function()
21538     {
21539         var r = false;
21540         Roo.each(this.panes, function(p) {
21541             if(p.el.hasClass('active')){
21542                 r = p;
21543                 return false;
21544             }
21545             
21546             return;
21547         });
21548         
21549         return r;
21550     }
21551     
21552     
21553 });
21554
21555  
21556 /*
21557  * - LGPL
21558  *
21559  * Tab pane
21560  * 
21561  */
21562 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21563 /**
21564  * @class Roo.bootstrap.TabPane
21565  * @extends Roo.bootstrap.Component
21566  * Bootstrap TabPane class
21567  * @cfg {Boolean} active (false | true) Default false
21568  * @cfg {String} title title of panel
21569
21570  * 
21571  * @constructor
21572  * Create a new TabPane
21573  * @param {Object} config The config object
21574  */
21575
21576 Roo.bootstrap.dash.TabPane = function(config){
21577     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
21578     
21579     this.addEvents({
21580         // raw events
21581         /**
21582          * @event activate
21583          * When a pane is activated
21584          * @param {Roo.bootstrap.dash.TabPane} pane
21585          */
21586         "activate" : true
21587          
21588     });
21589 };
21590
21591 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
21592     
21593     active : false,
21594     title : '',
21595     
21596     // the tabBox that this is attached to.
21597     tab : false,
21598      
21599     getAutoCreate : function() 
21600     {
21601         var cfg = {
21602             tag: 'div',
21603             cls: 'tab-pane'
21604         }
21605         
21606         if(this.active){
21607             cfg.cls += ' active';
21608         }
21609         
21610         return cfg;
21611     },
21612     initEvents  : function()
21613     {
21614         //Roo.log('trigger add pane handler');
21615         this.parent().fireEvent('addpane', this)
21616     },
21617     
21618      /**
21619      * Updates the tab title 
21620      * @param {String} html to set the title to.
21621      */
21622     setTitle: function(str)
21623     {
21624         if (!this.tab) {
21625             return;
21626         }
21627         this.title = str;
21628         this.tab.select('a', true).first().dom.innerHTML = str;
21629         
21630     }
21631     
21632     
21633     
21634 });
21635
21636  
21637
21638
21639  /*
21640  * - LGPL
21641  *
21642  * menu
21643  * 
21644  */
21645 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21646
21647 /**
21648  * @class Roo.bootstrap.menu.Menu
21649  * @extends Roo.bootstrap.Component
21650  * Bootstrap Menu class - container for Menu
21651  * @cfg {String} html Text of the menu
21652  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
21653  * @cfg {String} icon Font awesome icon
21654  * @cfg {String} pos Menu align to (top | bottom) default bottom
21655  * 
21656  * 
21657  * @constructor
21658  * Create a new Menu
21659  * @param {Object} config The config object
21660  */
21661
21662
21663 Roo.bootstrap.menu.Menu = function(config){
21664     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
21665     
21666     this.addEvents({
21667         /**
21668          * @event beforeshow
21669          * Fires before this menu is displayed
21670          * @param {Roo.bootstrap.menu.Menu} this
21671          */
21672         beforeshow : true,
21673         /**
21674          * @event beforehide
21675          * Fires before this menu is hidden
21676          * @param {Roo.bootstrap.menu.Menu} this
21677          */
21678         beforehide : true,
21679         /**
21680          * @event show
21681          * Fires after this menu is displayed
21682          * @param {Roo.bootstrap.menu.Menu} this
21683          */
21684         show : true,
21685         /**
21686          * @event hide
21687          * Fires after this menu is hidden
21688          * @param {Roo.bootstrap.menu.Menu} this
21689          */
21690         hide : true,
21691         /**
21692          * @event click
21693          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
21694          * @param {Roo.bootstrap.menu.Menu} this
21695          * @param {Roo.EventObject} e
21696          */
21697         click : true
21698     });
21699     
21700 };
21701
21702 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
21703     
21704     submenu : false,
21705     html : '',
21706     weight : 'default',
21707     icon : false,
21708     pos : 'bottom',
21709     
21710     
21711     getChildContainer : function() {
21712         if(this.isSubMenu){
21713             return this.el;
21714         }
21715         
21716         return this.el.select('ul.dropdown-menu', true).first();  
21717     },
21718     
21719     getAutoCreate : function()
21720     {
21721         var text = [
21722             {
21723                 tag : 'span',
21724                 cls : 'roo-menu-text',
21725                 html : this.html
21726             }
21727         ];
21728         
21729         if(this.icon){
21730             text.unshift({
21731                 tag : 'i',
21732                 cls : 'fa ' + this.icon
21733             })
21734         }
21735         
21736         
21737         var cfg = {
21738             tag : 'div',
21739             cls : 'btn-group',
21740             cn : [
21741                 {
21742                     tag : 'button',
21743                     cls : 'dropdown-button btn btn-' + this.weight,
21744                     cn : text
21745                 },
21746                 {
21747                     tag : 'button',
21748                     cls : 'dropdown-toggle btn btn-' + this.weight,
21749                     cn : [
21750                         {
21751                             tag : 'span',
21752                             cls : 'caret'
21753                         }
21754                     ]
21755                 },
21756                 {
21757                     tag : 'ul',
21758                     cls : 'dropdown-menu'
21759                 }
21760             ]
21761             
21762         };
21763         
21764         if(this.pos == 'top'){
21765             cfg.cls += ' dropup';
21766         }
21767         
21768         if(this.isSubMenu){
21769             cfg = {
21770                 tag : 'ul',
21771                 cls : 'dropdown-menu'
21772             }
21773         }
21774         
21775         return cfg;
21776     },
21777     
21778     onRender : function(ct, position)
21779     {
21780         this.isSubMenu = ct.hasClass('dropdown-submenu');
21781         
21782         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
21783     },
21784     
21785     initEvents : function() 
21786     {
21787         if(this.isSubMenu){
21788             return;
21789         }
21790         
21791         this.hidden = true;
21792         
21793         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
21794         this.triggerEl.on('click', this.onTriggerPress, this);
21795         
21796         this.buttonEl = this.el.select('button.dropdown-button', true).first();
21797         this.buttonEl.on('click', this.onClick, this);
21798         
21799     },
21800     
21801     list : function()
21802     {
21803         if(this.isSubMenu){
21804             return this.el;
21805         }
21806         
21807         return this.el.select('ul.dropdown-menu', true).first();
21808     },
21809     
21810     onClick : function(e)
21811     {
21812         this.fireEvent("click", this, e);
21813     },
21814     
21815     onTriggerPress  : function(e)
21816     {   
21817         if (this.isVisible()) {
21818             this.hide();
21819         } else {
21820             this.show();
21821         }
21822     },
21823     
21824     isVisible : function(){
21825         return !this.hidden;
21826     },
21827     
21828     show : function()
21829     {
21830         this.fireEvent("beforeshow", this);
21831         
21832         this.hidden = false;
21833         this.el.addClass('open');
21834         
21835         Roo.get(document).on("mouseup", this.onMouseUp, this);
21836         
21837         this.fireEvent("show", this);
21838         
21839         
21840     },
21841     
21842     hide : function()
21843     {
21844         this.fireEvent("beforehide", this);
21845         
21846         this.hidden = true;
21847         this.el.removeClass('open');
21848         
21849         Roo.get(document).un("mouseup", this.onMouseUp);
21850         
21851         this.fireEvent("hide", this);
21852     },
21853     
21854     onMouseUp : function()
21855     {
21856         this.hide();
21857     }
21858     
21859 });
21860
21861  
21862  /*
21863  * - LGPL
21864  *
21865  * menu item
21866  * 
21867  */
21868 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21869
21870 /**
21871  * @class Roo.bootstrap.menu.Item
21872  * @extends Roo.bootstrap.Component
21873  * Bootstrap MenuItem class
21874  * @cfg {Boolean} submenu (true | false) default false
21875  * @cfg {String} html text of the item
21876  * @cfg {String} href the link
21877  * @cfg {Boolean} disable (true | false) default false
21878  * @cfg {Boolean} preventDefault (true | false) default true
21879  * @cfg {String} icon Font awesome icon
21880  * @cfg {String} pos Submenu align to (left | right) default right 
21881  * 
21882  * 
21883  * @constructor
21884  * Create a new Item
21885  * @param {Object} config The config object
21886  */
21887
21888
21889 Roo.bootstrap.menu.Item = function(config){
21890     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
21891     this.addEvents({
21892         /**
21893          * @event mouseover
21894          * Fires when the mouse is hovering over this menu
21895          * @param {Roo.bootstrap.menu.Item} this
21896          * @param {Roo.EventObject} e
21897          */
21898         mouseover : true,
21899         /**
21900          * @event mouseout
21901          * Fires when the mouse exits this menu
21902          * @param {Roo.bootstrap.menu.Item} this
21903          * @param {Roo.EventObject} e
21904          */
21905         mouseout : true,
21906         // raw events
21907         /**
21908          * @event click
21909          * The raw click event for the entire grid.
21910          * @param {Roo.EventObject} e
21911          */
21912         click : true
21913     });
21914 };
21915
21916 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
21917     
21918     submenu : false,
21919     href : '',
21920     html : '',
21921     preventDefault: true,
21922     disable : false,
21923     icon : false,
21924     pos : 'right',
21925     
21926     getAutoCreate : function()
21927     {
21928         var text = [
21929             {
21930                 tag : 'span',
21931                 cls : 'roo-menu-item-text',
21932                 html : this.html
21933             }
21934         ];
21935         
21936         if(this.icon){
21937             text.unshift({
21938                 tag : 'i',
21939                 cls : 'fa ' + this.icon
21940             })
21941         }
21942         
21943         var cfg = {
21944             tag : 'li',
21945             cn : [
21946                 {
21947                     tag : 'a',
21948                     href : this.href || '#',
21949                     cn : text
21950                 }
21951             ]
21952         };
21953         
21954         if(this.disable){
21955             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
21956         }
21957         
21958         if(this.submenu){
21959             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
21960             
21961             if(this.pos == 'left'){
21962                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
21963             }
21964         }
21965         
21966         return cfg;
21967     },
21968     
21969     initEvents : function() 
21970     {
21971         this.el.on('mouseover', this.onMouseOver, this);
21972         this.el.on('mouseout', this.onMouseOut, this);
21973         
21974         this.el.select('a', true).first().on('click', this.onClick, this);
21975         
21976     },
21977     
21978     onClick : function(e)
21979     {
21980         if(this.preventDefault){
21981             e.preventDefault();
21982         }
21983         
21984         this.fireEvent("click", this, e);
21985     },
21986     
21987     onMouseOver : function(e)
21988     {
21989         if(this.submenu && this.pos == 'left'){
21990             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
21991         }
21992         
21993         this.fireEvent("mouseover", this, e);
21994     },
21995     
21996     onMouseOut : function(e)
21997     {
21998         this.fireEvent("mouseout", this, e);
21999     }
22000 });
22001
22002  
22003
22004  /*
22005  * - LGPL
22006  *
22007  * menu separator
22008  * 
22009  */
22010 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22011
22012 /**
22013  * @class Roo.bootstrap.menu.Separator
22014  * @extends Roo.bootstrap.Component
22015  * Bootstrap Separator class
22016  * 
22017  * @constructor
22018  * Create a new Separator
22019  * @param {Object} config The config object
22020  */
22021
22022
22023 Roo.bootstrap.menu.Separator = function(config){
22024     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
22025 };
22026
22027 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
22028     
22029     getAutoCreate : function(){
22030         var cfg = {
22031             tag : 'li',
22032             cls: 'divider'
22033         };
22034         
22035         return cfg;
22036     }
22037    
22038 });
22039
22040  
22041
22042  /*
22043  * - LGPL
22044  *
22045  * Tooltip
22046  * 
22047  */
22048
22049 /**
22050  * @class Roo.bootstrap.Tooltip
22051  * Bootstrap Tooltip class
22052  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
22053  * to determine which dom element triggers the tooltip.
22054  * 
22055  * It needs to add support for additional attributes like tooltip-position
22056  * 
22057  * @constructor
22058  * Create a new Toolti
22059  * @param {Object} config The config object
22060  */
22061
22062 Roo.bootstrap.Tooltip = function(config){
22063     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
22064 };
22065
22066 Roo.apply(Roo.bootstrap.Tooltip, {
22067     /**
22068      * @function init initialize tooltip monitoring.
22069      * @static
22070      */
22071     currentEl : false,
22072     currentTip : false,
22073     currentRegion : false,
22074     
22075     //  init : delay?
22076     
22077     init : function()
22078     {
22079         Roo.get(document).on('mouseover', this.enter ,this);
22080         Roo.get(document).on('mouseout', this.leave, this);
22081          
22082         
22083         this.currentTip = new Roo.bootstrap.Tooltip();
22084     },
22085     
22086     enter : function(ev)
22087     {
22088         var dom = ev.getTarget();
22089         
22090         //Roo.log(['enter',dom]);
22091         var el = Roo.fly(dom);
22092         if (this.currentEl) {
22093             //Roo.log(dom);
22094             //Roo.log(this.currentEl);
22095             //Roo.log(this.currentEl.contains(dom));
22096             if (this.currentEl == el) {
22097                 return;
22098             }
22099             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
22100                 return;
22101             }
22102
22103         }
22104         
22105         
22106         
22107         if (this.currentTip.el) {
22108             this.currentTip.el.hide(); // force hiding...
22109         }    
22110         //Roo.log(ev);
22111         var bindEl = el;
22112         
22113         // you can not look for children, as if el is the body.. then everythign is the child..
22114         if (!el.attr('tooltip')) { //
22115             if (!el.select("[tooltip]").elements.length) {
22116                 return;
22117             }
22118             // is the mouse over this child...?
22119             bindEl = el.select("[tooltip]").first();
22120             var xy = ev.getXY();
22121             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
22122                 //Roo.log("not in region.");
22123                 return;
22124             }
22125             //Roo.log("child element over..");
22126             
22127         }
22128         this.currentEl = bindEl;
22129         this.currentTip.bind(bindEl);
22130         this.currentRegion = Roo.lib.Region.getRegion(dom);
22131         this.currentTip.enter();
22132         
22133     },
22134     leave : function(ev)
22135     {
22136         var dom = ev.getTarget();
22137         //Roo.log(['leave',dom]);
22138         if (!this.currentEl) {
22139             return;
22140         }
22141         
22142         
22143         if (dom != this.currentEl.dom) {
22144             return;
22145         }
22146         var xy = ev.getXY();
22147         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
22148             return;
22149         }
22150         // only activate leave if mouse cursor is outside... bounding box..
22151         
22152         
22153         
22154         
22155         if (this.currentTip) {
22156             this.currentTip.leave();
22157         }
22158         //Roo.log('clear currentEl');
22159         this.currentEl = false;
22160         
22161         
22162     },
22163     alignment : {
22164         'left' : ['r-l', [-2,0], 'right'],
22165         'right' : ['l-r', [2,0], 'left'],
22166         'bottom' : ['t-b', [0,2], 'top'],
22167         'top' : [ 'b-t', [0,-2], 'bottom']
22168     }
22169     
22170 });
22171
22172
22173 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
22174     
22175     
22176     bindEl : false,
22177     
22178     delay : null, // can be { show : 300 , hide: 500}
22179     
22180     timeout : null,
22181     
22182     hoverState : null, //???
22183     
22184     placement : 'bottom', 
22185     
22186     getAutoCreate : function(){
22187     
22188         var cfg = {
22189            cls : 'tooltip',
22190            role : 'tooltip',
22191            cn : [
22192                 {
22193                     cls : 'tooltip-arrow'
22194                 },
22195                 {
22196                     cls : 'tooltip-inner'
22197                 }
22198            ]
22199         };
22200         
22201         return cfg;
22202     },
22203     bind : function(el)
22204     {
22205         this.bindEl = el;
22206     },
22207       
22208     
22209     enter : function () {
22210        
22211         if (this.timeout != null) {
22212             clearTimeout(this.timeout);
22213         }
22214         
22215         this.hoverState = 'in';
22216          //Roo.log("enter - show");
22217         if (!this.delay || !this.delay.show) {
22218             this.show();
22219             return;
22220         }
22221         var _t = this;
22222         this.timeout = setTimeout(function () {
22223             if (_t.hoverState == 'in') {
22224                 _t.show();
22225             }
22226         }, this.delay.show);
22227     },
22228     leave : function()
22229     {
22230         clearTimeout(this.timeout);
22231     
22232         this.hoverState = 'out';
22233          if (!this.delay || !this.delay.hide) {
22234             this.hide();
22235             return;
22236         }
22237        
22238         var _t = this;
22239         this.timeout = setTimeout(function () {
22240             //Roo.log("leave - timeout");
22241             
22242             if (_t.hoverState == 'out') {
22243                 _t.hide();
22244                 Roo.bootstrap.Tooltip.currentEl = false;
22245             }
22246         }, delay);
22247     },
22248     
22249     show : function ()
22250     {
22251         if (!this.el) {
22252             this.render(document.body);
22253         }
22254         // set content.
22255         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
22256         
22257         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
22258         
22259         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
22260         
22261         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
22262         
22263         var placement = typeof this.placement == 'function' ?
22264             this.placement.call(this, this.el, on_el) :
22265             this.placement;
22266             
22267         var autoToken = /\s?auto?\s?/i;
22268         var autoPlace = autoToken.test(placement);
22269         if (autoPlace) {
22270             placement = placement.replace(autoToken, '') || 'top';
22271         }
22272         
22273         //this.el.detach()
22274         //this.el.setXY([0,0]);
22275         this.el.show();
22276         //this.el.dom.style.display='block';
22277         this.el.addClass(placement);
22278         
22279         //this.el.appendTo(on_el);
22280         
22281         var p = this.getPosition();
22282         var box = this.el.getBox();
22283         
22284         if (autoPlace) {
22285             // fixme..
22286         }
22287         var align = Roo.bootstrap.Tooltip.alignment[placement];
22288         this.el.alignTo(this.bindEl, align[0],align[1]);
22289         //var arrow = this.el.select('.arrow',true).first();
22290         //arrow.set(align[2], 
22291         
22292         this.el.addClass('in fade');
22293         this.hoverState = null;
22294         
22295         if (this.el.hasClass('fade')) {
22296             // fade it?
22297         }
22298         
22299     },
22300     hide : function()
22301     {
22302          
22303         if (!this.el) {
22304             return;
22305         }
22306         //this.el.setXY([0,0]);
22307         this.el.removeClass('in');
22308         //this.el.hide();
22309         
22310     }
22311     
22312 });
22313  
22314
22315  /*
22316  * - LGPL
22317  *
22318  * Location Picker
22319  * 
22320  */
22321
22322 /**
22323  * @class Roo.bootstrap.LocationPicker
22324  * @extends Roo.bootstrap.Component
22325  * Bootstrap LocationPicker class
22326  * @cfg {Number} latitude Position when init default 0
22327  * @cfg {Number} longitude Position when init default 0
22328  * @cfg {Number} zoom default 15
22329  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
22330  * @cfg {Boolean} mapTypeControl default false
22331  * @cfg {Boolean} disableDoubleClickZoom default false
22332  * @cfg {Boolean} scrollwheel default true
22333  * @cfg {Boolean} streetViewControl default false
22334  * @cfg {Number} radius default 0
22335  * @cfg {String} locationName
22336  * @cfg {Boolean} draggable default true
22337  * @cfg {Boolean} enableAutocomplete default false
22338  * @cfg {Boolean} enableReverseGeocode default true
22339  * @cfg {String} markerTitle
22340  * 
22341  * @constructor
22342  * Create a new LocationPicker
22343  * @param {Object} config The config object
22344  */
22345
22346
22347 Roo.bootstrap.LocationPicker = function(config){
22348     
22349     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
22350     
22351     this.addEvents({
22352         /**
22353          * @event initial
22354          * Fires when the picker initialized.
22355          * @param {Roo.bootstrap.LocationPicker} this
22356          * @param {Google Location} location
22357          */
22358         initial : true,
22359         /**
22360          * @event positionchanged
22361          * Fires when the picker position changed.
22362          * @param {Roo.bootstrap.LocationPicker} this
22363          * @param {Google Location} location
22364          */
22365         positionchanged : true,
22366         /**
22367          * @event resize
22368          * Fires when the map resize.
22369          * @param {Roo.bootstrap.LocationPicker} this
22370          */
22371         resize : true,
22372         /**
22373          * @event show
22374          * Fires when the map show.
22375          * @param {Roo.bootstrap.LocationPicker} this
22376          */
22377         show : true,
22378         /**
22379          * @event hide
22380          * Fires when the map hide.
22381          * @param {Roo.bootstrap.LocationPicker} this
22382          */
22383         hide : true,
22384         /**
22385          * @event mapClick
22386          * Fires when click the map.
22387          * @param {Roo.bootstrap.LocationPicker} this
22388          * @param {Map event} e
22389          */
22390         mapClick : true,
22391         /**
22392          * @event mapRightClick
22393          * Fires when right click the map.
22394          * @param {Roo.bootstrap.LocationPicker} this
22395          * @param {Map event} e
22396          */
22397         mapRightClick : true,
22398         /**
22399          * @event markerClick
22400          * Fires when click the marker.
22401          * @param {Roo.bootstrap.LocationPicker} this
22402          * @param {Map event} e
22403          */
22404         markerClick : true,
22405         /**
22406          * @event markerRightClick
22407          * Fires when right click the marker.
22408          * @param {Roo.bootstrap.LocationPicker} this
22409          * @param {Map event} e
22410          */
22411         markerRightClick : true,
22412         /**
22413          * @event OverlayViewDraw
22414          * Fires when OverlayView Draw
22415          * @param {Roo.bootstrap.LocationPicker} this
22416          */
22417         OverlayViewDraw : true,
22418         /**
22419          * @event OverlayViewOnAdd
22420          * Fires when OverlayView Draw
22421          * @param {Roo.bootstrap.LocationPicker} this
22422          */
22423         OverlayViewOnAdd : true,
22424         /**
22425          * @event OverlayViewOnRemove
22426          * Fires when OverlayView Draw
22427          * @param {Roo.bootstrap.LocationPicker} this
22428          */
22429         OverlayViewOnRemove : true,
22430         /**
22431          * @event OverlayViewShow
22432          * Fires when OverlayView Draw
22433          * @param {Roo.bootstrap.LocationPicker} this
22434          * @param {Pixel} cpx
22435          */
22436         OverlayViewShow : true,
22437         /**
22438          * @event OverlayViewHide
22439          * Fires when OverlayView Draw
22440          * @param {Roo.bootstrap.LocationPicker} this
22441          */
22442         OverlayViewHide : true
22443     });
22444         
22445 };
22446
22447 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
22448     
22449     gMapContext: false,
22450     
22451     latitude: 0,
22452     longitude: 0,
22453     zoom: 15,
22454     mapTypeId: false,
22455     mapTypeControl: false,
22456     disableDoubleClickZoom: false,
22457     scrollwheel: true,
22458     streetViewControl: false,
22459     radius: 0,
22460     locationName: '',
22461     draggable: true,
22462     enableAutocomplete: false,
22463     enableReverseGeocode: true,
22464     markerTitle: '',
22465     
22466     getAutoCreate: function()
22467     {
22468
22469         var cfg = {
22470             tag: 'div',
22471             cls: 'roo-location-picker'
22472         };
22473         
22474         return cfg
22475     },
22476     
22477     initEvents: function(ct, position)
22478     {       
22479         if(!this.el.getWidth() || this.isApplied()){
22480             return;
22481         }
22482         
22483         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22484         
22485         this.initial();
22486     },
22487     
22488     initial: function()
22489     {
22490         if(!this.mapTypeId){
22491             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
22492         }
22493         
22494         this.gMapContext = this.GMapContext();
22495         
22496         this.initOverlayView();
22497         
22498         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
22499         
22500         var _this = this;
22501                 
22502         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
22503             _this.setPosition(_this.gMapContext.marker.position);
22504         });
22505         
22506         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
22507             _this.fireEvent('mapClick', this, event);
22508             
22509         });
22510
22511         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
22512             _this.fireEvent('mapRightClick', this, event);
22513             
22514         });
22515         
22516         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
22517             _this.fireEvent('markerClick', this, event);
22518             
22519         });
22520
22521         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
22522             _this.fireEvent('markerRightClick', this, event);
22523             
22524         });
22525         
22526         this.setPosition(this.gMapContext.location);
22527         
22528         this.fireEvent('initial', this, this.gMapContext.location);
22529     },
22530     
22531     initOverlayView: function()
22532     {
22533         var _this = this;
22534         
22535         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
22536             
22537             draw: function()
22538             {
22539                 _this.fireEvent('OverlayViewDraw', _this);
22540             },
22541             
22542             onAdd: function()
22543             {
22544                 _this.fireEvent('OverlayViewOnAdd', _this);
22545             },
22546             
22547             onRemove: function()
22548             {
22549                 _this.fireEvent('OverlayViewOnRemove', _this);
22550             },
22551             
22552             show: function(cpx)
22553             {
22554                 _this.fireEvent('OverlayViewShow', _this, cpx);
22555             },
22556             
22557             hide: function()
22558             {
22559                 _this.fireEvent('OverlayViewHide', _this);
22560             }
22561             
22562         });
22563     },
22564     
22565     fromLatLngToContainerPixel: function(event)
22566     {
22567         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
22568     },
22569     
22570     isApplied: function() 
22571     {
22572         return this.getGmapContext() == false ? false : true;
22573     },
22574     
22575     getGmapContext: function() 
22576     {
22577         return this.gMapContext
22578     },
22579     
22580     GMapContext: function() 
22581     {
22582         var position = new google.maps.LatLng(this.latitude, this.longitude);
22583         
22584         var _map = new google.maps.Map(this.el.dom, {
22585             center: position,
22586             zoom: this.zoom,
22587             mapTypeId: this.mapTypeId,
22588             mapTypeControl: this.mapTypeControl,
22589             disableDoubleClickZoom: this.disableDoubleClickZoom,
22590             scrollwheel: this.scrollwheel,
22591             streetViewControl: this.streetViewControl,
22592             locationName: this.locationName,
22593             draggable: this.draggable,
22594             enableAutocomplete: this.enableAutocomplete,
22595             enableReverseGeocode: this.enableReverseGeocode
22596         });
22597         
22598         var _marker = new google.maps.Marker({
22599             position: position,
22600             map: _map,
22601             title: this.markerTitle,
22602             draggable: this.draggable
22603         });
22604         
22605         return {
22606             map: _map,
22607             marker: _marker,
22608             circle: null,
22609             location: position,
22610             radius: this.radius,
22611             locationName: this.locationName,
22612             addressComponents: {
22613                 formatted_address: null,
22614                 addressLine1: null,
22615                 addressLine2: null,
22616                 streetName: null,
22617                 streetNumber: null,
22618                 city: null,
22619                 district: null,
22620                 state: null,
22621                 stateOrProvince: null
22622             },
22623             settings: this,
22624             domContainer: this.el.dom,
22625             geodecoder: new google.maps.Geocoder()
22626         };
22627     },
22628     
22629     drawCircle: function(center, radius, options) 
22630     {
22631         if (this.gMapContext.circle != null) {
22632             this.gMapContext.circle.setMap(null);
22633         }
22634         if (radius > 0) {
22635             radius *= 1;
22636             options = Roo.apply({}, options, {
22637                 strokeColor: "#0000FF",
22638                 strokeOpacity: .35,
22639                 strokeWeight: 2,
22640                 fillColor: "#0000FF",
22641                 fillOpacity: .2
22642             });
22643             
22644             options.map = this.gMapContext.map;
22645             options.radius = radius;
22646             options.center = center;
22647             this.gMapContext.circle = new google.maps.Circle(options);
22648             return this.gMapContext.circle;
22649         }
22650         
22651         return null;
22652     },
22653     
22654     setPosition: function(location) 
22655     {
22656         this.gMapContext.location = location;
22657         this.gMapContext.marker.setPosition(location);
22658         this.gMapContext.map.panTo(location);
22659         this.drawCircle(location, this.gMapContext.radius, {});
22660         
22661         var _this = this;
22662         
22663         if (this.gMapContext.settings.enableReverseGeocode) {
22664             this.gMapContext.geodecoder.geocode({
22665                 latLng: this.gMapContext.location
22666             }, function(results, status) {
22667                 
22668                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
22669                     _this.gMapContext.locationName = results[0].formatted_address;
22670                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
22671                     
22672                     _this.fireEvent('positionchanged', this, location);
22673                 }
22674             });
22675             
22676             return;
22677         }
22678         
22679         this.fireEvent('positionchanged', this, location);
22680     },
22681     
22682     resize: function()
22683     {
22684         google.maps.event.trigger(this.gMapContext.map, "resize");
22685         
22686         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
22687         
22688         this.fireEvent('resize', this);
22689     },
22690     
22691     setPositionByLatLng: function(latitude, longitude)
22692     {
22693         this.setPosition(new google.maps.LatLng(latitude, longitude));
22694     },
22695     
22696     getCurrentPosition: function() 
22697     {
22698         return {
22699             latitude: this.gMapContext.location.lat(),
22700             longitude: this.gMapContext.location.lng()
22701         };
22702     },
22703     
22704     getAddressName: function() 
22705     {
22706         return this.gMapContext.locationName;
22707     },
22708     
22709     getAddressComponents: function() 
22710     {
22711         return this.gMapContext.addressComponents;
22712     },
22713     
22714     address_component_from_google_geocode: function(address_components) 
22715     {
22716         var result = {};
22717         
22718         for (var i = 0; i < address_components.length; i++) {
22719             var component = address_components[i];
22720             if (component.types.indexOf("postal_code") >= 0) {
22721                 result.postalCode = component.short_name;
22722             } else if (component.types.indexOf("street_number") >= 0) {
22723                 result.streetNumber = component.short_name;
22724             } else if (component.types.indexOf("route") >= 0) {
22725                 result.streetName = component.short_name;
22726             } else if (component.types.indexOf("neighborhood") >= 0) {
22727                 result.city = component.short_name;
22728             } else if (component.types.indexOf("locality") >= 0) {
22729                 result.city = component.short_name;
22730             } else if (component.types.indexOf("sublocality") >= 0) {
22731                 result.district = component.short_name;
22732             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
22733                 result.stateOrProvince = component.short_name;
22734             } else if (component.types.indexOf("country") >= 0) {
22735                 result.country = component.short_name;
22736             }
22737         }
22738         
22739         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
22740         result.addressLine2 = "";
22741         return result;
22742     },
22743     
22744     setZoomLevel: function(zoom)
22745     {
22746         this.gMapContext.map.setZoom(zoom);
22747     },
22748     
22749     show: function()
22750     {
22751         if(!this.el){
22752             return;
22753         }
22754         
22755         this.el.show();
22756         
22757         this.resize();
22758         
22759         this.fireEvent('show', this);
22760     },
22761     
22762     hide: function()
22763     {
22764         if(!this.el){
22765             return;
22766         }
22767         
22768         this.el.hide();
22769         
22770         this.fireEvent('hide', this);
22771     }
22772     
22773 });
22774
22775 Roo.apply(Roo.bootstrap.LocationPicker, {
22776     
22777     OverlayView : function(map, options)
22778     {
22779         options = options || {};
22780         
22781         this.setMap(map);
22782     }
22783     
22784     
22785 });/*
22786  * - LGPL
22787  *
22788  * Alert
22789  * 
22790  */
22791
22792 /**
22793  * @class Roo.bootstrap.Alert
22794  * @extends Roo.bootstrap.Component
22795  * Bootstrap Alert class
22796  * @cfg {String} title The title of alert
22797  * @cfg {String} html The content of alert
22798  * @cfg {String} weight (  success | info | warning | danger )
22799  * @cfg {String} faicon font-awesomeicon
22800  * 
22801  * @constructor
22802  * Create a new alert
22803  * @param {Object} config The config object
22804  */
22805
22806
22807 Roo.bootstrap.Alert = function(config){
22808     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
22809     
22810 };
22811
22812 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
22813     
22814     title: '',
22815     html: '',
22816     weight: false,
22817     faicon: false,
22818     
22819     getAutoCreate : function()
22820     {
22821         
22822         var cfg = {
22823             tag : 'div',
22824             cls : 'alert',
22825             cn : [
22826                 {
22827                     tag : 'i',
22828                     cls : 'roo-alert-icon'
22829                     
22830                 },
22831                 {
22832                     tag : 'b',
22833                     cls : 'roo-alert-title',
22834                     html : this.title
22835                 },
22836                 {
22837                     tag : 'span',
22838                     cls : 'roo-alert-text',
22839                     html : this.html
22840                 }
22841             ]
22842         };
22843         
22844         if(this.faicon){
22845             cfg.cn[0].cls += ' fa ' + this.faicon;
22846         }
22847         
22848         if(this.weight){
22849             cfg.cls += ' alert-' + this.weight;
22850         }
22851         
22852         return cfg;
22853     },
22854     
22855     initEvents: function() 
22856     {
22857         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22858     },
22859     
22860     setTitle : function(str)
22861     {
22862         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
22863     },
22864     
22865     setText : function(str)
22866     {
22867         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
22868     },
22869     
22870     setWeight : function(weight)
22871     {
22872         if(this.weight){
22873             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
22874         }
22875         
22876         this.weight = weight;
22877         
22878         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
22879     },
22880     
22881     setIcon : function(icon)
22882     {
22883         if(this.faicon){
22884             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
22885         }
22886         
22887         this.faicon = icon
22888         
22889         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
22890     },
22891     
22892     hide: function() 
22893     {
22894         this.el.hide();   
22895     },
22896     
22897     show: function() 
22898     {  
22899         this.el.show();   
22900     }
22901     
22902 });
22903
22904