5ac8f71dd361d9de23a35335509cdd1ab873ec3f
[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             
1846             //xy = this.el.adjustForConstraints(xy);
1847         }
1848         //this.el.setXY(xy);
1849         //this.el.show();
1850         this.hideMenuItems();
1851         this.hidden = false;
1852         this.triggerEl.addClass('open');
1853         this.focus();
1854         this.fireEvent("show", this);
1855     },
1856     
1857     focus : function(){
1858         return;
1859         if(!this.hidden){
1860             this.doFocus.defer(50, this);
1861         }
1862     },
1863
1864     doFocus : function(){
1865         if(!this.hidden){
1866             this.focusEl.focus();
1867         }
1868     },
1869
1870     /**
1871      * Hides this menu and optionally all parent menus
1872      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1873      */
1874     hide : function(deep){
1875         
1876         this.hideMenuItems();
1877         if(this.el && this.isVisible()){
1878             this.fireEvent("beforehide", this);
1879             if(this.activeItem){
1880                 this.activeItem.deactivate();
1881                 this.activeItem = null;
1882             }
1883             this.triggerEl.removeClass('open');;
1884             this.hidden = true;
1885             this.fireEvent("hide", this);
1886         }
1887         if(deep === true && this.parentMenu){
1888             this.parentMenu.hide(true);
1889         }
1890     },
1891     
1892     onTriggerPress  : function(e)
1893     {
1894         
1895         Roo.log('trigger press');
1896         //Roo.log(e.getTarget());
1897        // Roo.log(this.triggerEl.dom);
1898         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1899             return;
1900         }
1901         if (this.isVisible()) {
1902             Roo.log('hide');
1903             this.hide();
1904         } else {
1905             this.show(this.triggerEl, false, false);
1906         }
1907         
1908         
1909     },
1910     
1911          
1912        
1913     
1914     hideMenuItems : function()
1915     {
1916         //$(backdrop).remove()
1917         Roo.select('.open',true).each(function(aa) {
1918             
1919             aa.removeClass('open');
1920           //var parent = getParent($(this))
1921           //var relatedTarget = { relatedTarget: this }
1922           
1923            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1924           //if (e.isDefaultPrevented()) return
1925            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1926         })
1927     },
1928     addxtypeChild : function (tree, cntr) {
1929         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1930           
1931         this.menuitems.add(comp);
1932         return comp;
1933
1934     },
1935     getEl : function()
1936     {
1937         Roo.log(this.el);
1938         return this.el;
1939     }
1940 });
1941
1942  
1943  /*
1944  * - LGPL
1945  *
1946  * menu item
1947  * 
1948  */
1949
1950
1951 /**
1952  * @class Roo.bootstrap.MenuItem
1953  * @extends Roo.bootstrap.Component
1954  * Bootstrap MenuItem class
1955  * @cfg {String} html the menu label
1956  * @cfg {String} href the link
1957  * @cfg {Boolean} preventDefault (true | false) default true
1958  * @cfg {Boolean} isContainer (true | false) default false
1959  * 
1960  * 
1961  * @constructor
1962  * Create a new MenuItem
1963  * @param {Object} config The config object
1964  */
1965
1966
1967 Roo.bootstrap.MenuItem = function(config){
1968     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1969     this.addEvents({
1970         // raw events
1971         /**
1972          * @event click
1973          * The raw click event for the entire grid.
1974          * @param {Roo.bootstrap.MenuItem} this
1975          * @param {Roo.EventObject} e
1976          */
1977         "click" : true
1978     });
1979 };
1980
1981 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1982     
1983     href : false,
1984     html : false,
1985     preventDefault: true,
1986     isContainer : false,
1987     
1988     getAutoCreate : function(){
1989         
1990         if(this.isContainer){
1991             return {
1992                 tag: 'li',
1993                 cls: 'dropdown-menu-item'
1994             };
1995         }
1996         
1997         var cfg= {
1998             tag: 'li',
1999             cls: 'dropdown-menu-item',
2000             cn: [
2001                     {
2002                         tag : 'a',
2003                         href : '#',
2004                         html : 'Link'
2005                     }
2006                 ]
2007         };
2008         if (this.parent().type == 'treeview') {
2009             cfg.cls = 'treeview-menu';
2010         }
2011         
2012         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2013         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2014         return cfg;
2015     },
2016     
2017     initEvents: function() {
2018         
2019         //this.el.select('a').on('click', this.onClick, this);
2020         
2021     },
2022     onClick : function(e)
2023     {
2024         Roo.log('item on click ');
2025         //if(this.preventDefault){
2026         //    e.preventDefault();
2027         //}
2028         //this.parent().hideMenuItems();
2029         
2030         this.fireEvent('click', this, e);
2031     },
2032     getEl : function()
2033     {
2034         return this.el;
2035     }
2036 });
2037
2038  
2039
2040  /*
2041  * - LGPL
2042  *
2043  * menu separator
2044  * 
2045  */
2046
2047
2048 /**
2049  * @class Roo.bootstrap.MenuSeparator
2050  * @extends Roo.bootstrap.Component
2051  * Bootstrap MenuSeparator class
2052  * 
2053  * @constructor
2054  * Create a new MenuItem
2055  * @param {Object} config The config object
2056  */
2057
2058
2059 Roo.bootstrap.MenuSeparator = function(config){
2060     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2061 };
2062
2063 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2064     
2065     getAutoCreate : function(){
2066         var cfg = {
2067             cls: 'divider',
2068             tag : 'li'
2069         };
2070         
2071         return cfg;
2072     }
2073    
2074 });
2075
2076  
2077
2078  
2079 /*
2080 * Licence: LGPL
2081 */
2082
2083 /**
2084  * @class Roo.bootstrap.Modal
2085  * @extends Roo.bootstrap.Component
2086  * Bootstrap Modal class
2087  * @cfg {String} title Title of dialog
2088  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2089  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2090  * @cfg {Boolean} specificTitle default false
2091  * @cfg {Array} buttons Array of buttons or standard button set..
2092  * @cfg {String} buttonPosition (left|right|center) default right
2093  * @cfg {Boolean} animate default true
2094  * @cfg {Boolean} allow_close default true
2095  * 
2096  * @constructor
2097  * Create a new Modal Dialog
2098  * @param {Object} config The config object
2099  */
2100
2101 Roo.bootstrap.Modal = function(config){
2102     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2103     this.addEvents({
2104         // raw events
2105         /**
2106          * @event btnclick
2107          * The raw btnclick event for the button
2108          * @param {Roo.EventObject} e
2109          */
2110         "btnclick" : true
2111     });
2112     this.buttons = this.buttons || [];
2113      
2114     if (this.tmpl) {
2115         this.tmpl = Roo.factory(this.tmpl);
2116     }
2117     
2118 };
2119
2120 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2121     
2122     title : 'test dialog',
2123    
2124     buttons : false,
2125     
2126     // set on load...
2127      
2128     html: false,
2129     
2130     tmp: false,
2131     
2132     specificTitle: false,
2133     
2134     buttonPosition: 'right',
2135     
2136     allow_close : true,
2137     
2138     animate : true,
2139     
2140     
2141      // private
2142     bodyEl:  false,
2143     footerEl:  false,
2144     titleEl:  false,
2145     closeEl:  false,
2146     
2147     
2148     onRender : function(ct, position)
2149     {
2150         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2151      
2152         if(!this.el){
2153             var cfg = Roo.apply({},  this.getAutoCreate());
2154             cfg.id = Roo.id();
2155             //if(!cfg.name){
2156             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2157             //}
2158             //if (!cfg.name.length) {
2159             //    delete cfg.name;
2160            // }
2161             if (this.cls) {
2162                 cfg.cls += ' ' + this.cls;
2163             }
2164             if (this.style) {
2165                 cfg.style = this.style;
2166             }
2167             this.el = Roo.get(document.body).createChild(cfg, position);
2168         }
2169         //var type = this.el.dom.type;
2170         
2171         
2172         
2173         
2174         if(this.tabIndex !== undefined){
2175             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2176         }
2177         
2178         
2179         this.bodyEl = this.el.select('.modal-body',true).first();
2180         this.closeEl = this.el.select('.modal-header .close', true).first();
2181         this.footerEl = this.el.select('.modal-footer',true).first();
2182         this.titleEl = this.el.select('.modal-title',true).first();
2183         
2184         
2185          
2186         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2187         this.maskEl.enableDisplayMode("block");
2188         this.maskEl.hide();
2189         //this.el.addClass("x-dlg-modal");
2190     
2191         if (this.buttons.length) {
2192             Roo.each(this.buttons, function(bb) {
2193                 b = Roo.apply({}, bb);
2194                 b.xns = b.xns || Roo.bootstrap;
2195                 b.xtype = b.xtype || 'Button';
2196                 if (typeof(b.listeners) == 'undefined') {
2197                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2198                 }
2199                 
2200                 var btn = Roo.factory(b);
2201                 
2202                 btn.onRender(this.el.select('.modal-footer div').first());
2203                 
2204             },this);
2205         }
2206         // render the children.
2207         var nitems = [];
2208         
2209         if(typeof(this.items) != 'undefined'){
2210             var items = this.items;
2211             delete this.items;
2212
2213             for(var i =0;i < items.length;i++) {
2214                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2215             }
2216         }
2217         
2218         this.items = nitems;
2219         
2220         // where are these used - they used to be body/close/footer
2221         
2222        
2223         this.initEvents();
2224         //this.el.addClass([this.fieldClass, this.cls]);
2225         
2226     },
2227     getAutoCreate : function(){
2228         
2229         
2230         var bdy = {
2231                 cls : 'modal-body',
2232                 html : this.html || ''
2233         };
2234         
2235         var title = {
2236             tag: 'h4',
2237             cls : 'modal-title',
2238             html : this.title
2239         };
2240         
2241         if(this.specificTitle){
2242             title = this.title;
2243             
2244         };
2245         
2246         var header = [];
2247         if (this.allow_close) {
2248             header.push({
2249                 tag: 'button',
2250                 cls : 'close',
2251                 html : '&times'
2252             });
2253         }
2254         header.push(title);
2255         
2256         var modal = {
2257             cls: "modal",
2258             style : 'display: none',
2259             cn : [
2260                 {
2261                     cls: "modal-dialog",
2262                     cn : [
2263                         {
2264                             cls : "modal-content",
2265                             cn : [
2266                                 {
2267                                     cls : 'modal-header',
2268                                     cn : header
2269                                 },
2270                                 bdy,
2271                                 {
2272                                     cls : 'modal-footer',
2273                                     cn : [
2274                                         {
2275                                             tag: 'div',
2276                                             cls: 'btn-' + this.buttonPosition
2277                                         }
2278                                     ]
2279                                     
2280                                 }
2281                                 
2282                                 
2283                             ]
2284                             
2285                         }
2286                     ]
2287                         
2288                 }
2289             ]
2290         };
2291         
2292         if(this.animate){
2293             modal.cls += ' fade';
2294         }
2295         
2296         return modal;
2297           
2298     },
2299     getChildContainer : function() {
2300          
2301          return this.bodyEl;
2302         
2303     },
2304     getButtonContainer : function() {
2305          return this.el.select('.modal-footer div',true).first();
2306         
2307     },
2308     initEvents : function()
2309     {
2310         if (this.allow_close) {
2311             this.closeEl.on('click', this.hide, this);
2312         }
2313
2314     },
2315     show : function() {
2316         
2317         if (!this.rendered) {
2318             this.render();
2319         }
2320         
2321         this.el.setStyle('display', 'block');
2322         
2323         if(this.animate){
2324             var _this = this;
2325             (function(){ _this.el.addClass('in'); }).defer(50);
2326         }else{
2327             this.el.addClass('in');
2328         }
2329         
2330         // not sure how we can show data in here.. 
2331         //if (this.tmpl) {
2332         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2333         //}
2334         
2335         Roo.get(document.body).addClass("x-body-masked");
2336         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2337         this.maskEl.show();
2338         this.el.setStyle('zIndex', '10001');
2339        
2340         this.fireEvent('show', this);
2341         
2342         
2343     },
2344     hide : function()
2345     {
2346         this.maskEl.hide();
2347         Roo.get(document.body).removeClass("x-body-masked");
2348         this.el.removeClass('in');
2349         
2350         if(this.animate){
2351             var _this = this;
2352             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2353         }else{
2354             this.el.setStyle('display', 'none');
2355         }
2356         
2357         this.fireEvent('hide', this);
2358     },
2359     
2360     addButton : function(str, cb)
2361     {
2362          
2363         
2364         var b = Roo.apply({}, { html : str } );
2365         b.xns = b.xns || Roo.bootstrap;
2366         b.xtype = b.xtype || 'Button';
2367         if (typeof(b.listeners) == 'undefined') {
2368             b.listeners = { click : cb.createDelegate(this)  };
2369         }
2370         
2371         var btn = Roo.factory(b);
2372            
2373         btn.onRender(this.el.select('.modal-footer div').first());
2374         
2375         return btn;   
2376        
2377     },
2378     
2379     setDefaultButton : function(btn)
2380     {
2381         //this.el.select('.modal-footer').()
2382     },
2383     resizeTo: function(w,h)
2384     {
2385         // skip..
2386     },
2387     setContentSize  : function(w, h)
2388     {
2389         
2390     },
2391     onButtonClick: function(btn,e)
2392     {
2393         //Roo.log([a,b,c]);
2394         this.fireEvent('btnclick', btn.name, e);
2395     },
2396      /**
2397      * Set the title of the Dialog
2398      * @param {String} str new Title
2399      */
2400     setTitle: function(str) {
2401         this.titleEl.dom.innerHTML = str;    
2402     },
2403     /**
2404      * Set the body of the Dialog
2405      * @param {String} str new Title
2406      */
2407     setBody: function(str) {
2408         this.bodyEl.dom.innerHTML = str;    
2409     },
2410     /**
2411      * Set the body of the Dialog using the template
2412      * @param {Obj} data - apply this data to the template and replace the body contents.
2413      */
2414     applyBody: function(obj)
2415     {
2416         if (!this.tmpl) {
2417             Roo.log("Error - using apply Body without a template");
2418             //code
2419         }
2420         this.tmpl.overwrite(this.bodyEl, obj);
2421     }
2422     
2423 });
2424
2425
2426 Roo.apply(Roo.bootstrap.Modal,  {
2427     /**
2428          * Button config that displays a single OK button
2429          * @type Object
2430          */
2431         OK :  [{
2432             name : 'ok',
2433             weight : 'primary',
2434             html : 'OK'
2435         }], 
2436         /**
2437          * Button config that displays Yes and No buttons
2438          * @type Object
2439          */
2440         YESNO : [
2441             {
2442                 name  : 'no',
2443                 html : 'No'
2444             },
2445             {
2446                 name  :'yes',
2447                 weight : 'primary',
2448                 html : 'Yes'
2449             }
2450         ],
2451         
2452         /**
2453          * Button config that displays OK and Cancel buttons
2454          * @type Object
2455          */
2456         OKCANCEL : [
2457             {
2458                name : 'cancel',
2459                 html : 'Cancel'
2460             },
2461             {
2462                 name : 'ok',
2463                 weight : 'primary',
2464                 html : 'OK'
2465             }
2466         ],
2467         /**
2468          * Button config that displays Yes, No and Cancel buttons
2469          * @type Object
2470          */
2471         YESNOCANCEL : [
2472             {
2473                 name : 'yes',
2474                 weight : 'primary',
2475                 html : 'Yes'
2476             },
2477             {
2478                 name : 'no',
2479                 html : 'No'
2480             },
2481             {
2482                 name : 'cancel',
2483                 html : 'Cancel'
2484             }
2485         ]
2486 });
2487  
2488  /*
2489  * - LGPL
2490  *
2491  * messagebox - can be used as a replace
2492  * 
2493  */
2494 /**
2495  * @class Roo.MessageBox
2496  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2497  * Example usage:
2498  *<pre><code>
2499 // Basic alert:
2500 Roo.Msg.alert('Status', 'Changes saved successfully.');
2501
2502 // Prompt for user data:
2503 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2504     if (btn == 'ok'){
2505         // process text value...
2506     }
2507 });
2508
2509 // Show a dialog using config options:
2510 Roo.Msg.show({
2511    title:'Save Changes?',
2512    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2513    buttons: Roo.Msg.YESNOCANCEL,
2514    fn: processResult,
2515    animEl: 'elId'
2516 });
2517 </code></pre>
2518  * @singleton
2519  */
2520 Roo.bootstrap.MessageBox = function(){
2521     var dlg, opt, mask, waitTimer;
2522     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2523     var buttons, activeTextEl, bwidth;
2524
2525     
2526     // private
2527     var handleButton = function(button){
2528         dlg.hide();
2529         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2530     };
2531
2532     // private
2533     var handleHide = function(){
2534         if(opt && opt.cls){
2535             dlg.el.removeClass(opt.cls);
2536         }
2537         //if(waitTimer){
2538         //    Roo.TaskMgr.stop(waitTimer);
2539         //    waitTimer = null;
2540         //}
2541     };
2542
2543     // private
2544     var updateButtons = function(b){
2545         var width = 0;
2546         if(!b){
2547             buttons["ok"].hide();
2548             buttons["cancel"].hide();
2549             buttons["yes"].hide();
2550             buttons["no"].hide();
2551             //dlg.footer.dom.style.display = 'none';
2552             return width;
2553         }
2554         dlg.footerEl.dom.style.display = '';
2555         for(var k in buttons){
2556             if(typeof buttons[k] != "function"){
2557                 if(b[k]){
2558                     buttons[k].show();
2559                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2560                     width += buttons[k].el.getWidth()+15;
2561                 }else{
2562                     buttons[k].hide();
2563                 }
2564             }
2565         }
2566         return width;
2567     };
2568
2569     // private
2570     var handleEsc = function(d, k, e){
2571         if(opt && opt.closable !== false){
2572             dlg.hide();
2573         }
2574         if(e){
2575             e.stopEvent();
2576         }
2577     };
2578
2579     return {
2580         /**
2581          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2582          * @return {Roo.BasicDialog} The BasicDialog element
2583          */
2584         getDialog : function(){
2585            if(!dlg){
2586                 dlg = new Roo.bootstrap.Modal( {
2587                     //draggable: true,
2588                     //resizable:false,
2589                     //constraintoviewport:false,
2590                     //fixedcenter:true,
2591                     //collapsible : false,
2592                     //shim:true,
2593                     //modal: true,
2594                   //  width:400,
2595                   //  height:100,
2596                     //buttonAlign:"center",
2597                     closeClick : function(){
2598                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2599                             handleButton("no");
2600                         }else{
2601                             handleButton("cancel");
2602                         }
2603                     }
2604                 });
2605                 dlg.render();
2606                 dlg.on("hide", handleHide);
2607                 mask = dlg.mask;
2608                 //dlg.addKeyListener(27, handleEsc);
2609                 buttons = {};
2610                 this.buttons = buttons;
2611                 var bt = this.buttonText;
2612                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2613                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2614                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2615                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2616                 Roo.log(buttons)
2617                 bodyEl = dlg.bodyEl.createChild({
2618
2619                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2620                         '<textarea class="roo-mb-textarea"></textarea>' +
2621                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2622                 });
2623                 msgEl = bodyEl.dom.firstChild;
2624                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2625                 textboxEl.enableDisplayMode();
2626                 textboxEl.addKeyListener([10,13], function(){
2627                     if(dlg.isVisible() && opt && opt.buttons){
2628                         if(opt.buttons.ok){
2629                             handleButton("ok");
2630                         }else if(opt.buttons.yes){
2631                             handleButton("yes");
2632                         }
2633                     }
2634                 });
2635                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2636                 textareaEl.enableDisplayMode();
2637                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2638                 progressEl.enableDisplayMode();
2639                 var pf = progressEl.dom.firstChild;
2640                 if (pf) {
2641                     pp = Roo.get(pf.firstChild);
2642                     pp.setHeight(pf.offsetHeight);
2643                 }
2644                 
2645             }
2646             return dlg;
2647         },
2648
2649         /**
2650          * Updates the message box body text
2651          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2652          * the XHTML-compliant non-breaking space character '&amp;#160;')
2653          * @return {Roo.MessageBox} This message box
2654          */
2655         updateText : function(text){
2656             if(!dlg.isVisible() && !opt.width){
2657                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2658             }
2659             msgEl.innerHTML = text || '&#160;';
2660       
2661             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2662             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2663             var w = Math.max(
2664                     Math.min(opt.width || cw , this.maxWidth), 
2665                     Math.max(opt.minWidth || this.minWidth, bwidth)
2666             );
2667             if(opt.prompt){
2668                 activeTextEl.setWidth(w);
2669             }
2670             if(dlg.isVisible()){
2671                 dlg.fixedcenter = false;
2672             }
2673             // to big, make it scroll. = But as usual stupid IE does not support
2674             // !important..
2675             
2676             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2677                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2678                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2679             } else {
2680                 bodyEl.dom.style.height = '';
2681                 bodyEl.dom.style.overflowY = '';
2682             }
2683             if (cw > w) {
2684                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2685             } else {
2686                 bodyEl.dom.style.overflowX = '';
2687             }
2688             
2689             dlg.setContentSize(w, bodyEl.getHeight());
2690             if(dlg.isVisible()){
2691                 dlg.fixedcenter = true;
2692             }
2693             return this;
2694         },
2695
2696         /**
2697          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2698          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2699          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2700          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2701          * @return {Roo.MessageBox} This message box
2702          */
2703         updateProgress : function(value, text){
2704             if(text){
2705                 this.updateText(text);
2706             }
2707             if (pp) { // weird bug on my firefox - for some reason this is not defined
2708                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2709             }
2710             return this;
2711         },        
2712
2713         /**
2714          * Returns true if the message box is currently displayed
2715          * @return {Boolean} True if the message box is visible, else false
2716          */
2717         isVisible : function(){
2718             return dlg && dlg.isVisible();  
2719         },
2720
2721         /**
2722          * Hides the message box if it is displayed
2723          */
2724         hide : function(){
2725             if(this.isVisible()){
2726                 dlg.hide();
2727             }  
2728         },
2729
2730         /**
2731          * Displays a new message box, or reinitializes an existing message box, based on the config options
2732          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2733          * The following config object properties are supported:
2734          * <pre>
2735 Property    Type             Description
2736 ----------  ---------------  ------------------------------------------------------------------------------------
2737 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2738                                    closes (defaults to undefined)
2739 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2740                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2741 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2742                                    progress and wait dialogs will ignore this property and always hide the
2743                                    close button as they can only be closed programmatically.
2744 cls               String           A custom CSS class to apply to the message box element
2745 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2746                                    displayed (defaults to 75)
2747 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2748                                    function will be btn (the name of the button that was clicked, if applicable,
2749                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2750                                    Progress and wait dialogs will ignore this option since they do not respond to
2751                                    user actions and can only be closed programmatically, so any required function
2752                                    should be called by the same code after it closes the dialog.
2753 icon              String           A CSS class that provides a background image to be used as an icon for
2754                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2755 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2756 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2757 modal             Boolean          False to allow user interaction with the page while the message box is
2758                                    displayed (defaults to true)
2759 msg               String           A string that will replace the existing message box body text (defaults
2760                                    to the XHTML-compliant non-breaking space character '&#160;')
2761 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2762 progress          Boolean          True to display a progress bar (defaults to false)
2763 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2764 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2765 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2766 title             String           The title text
2767 value             String           The string value to set into the active textbox element if displayed
2768 wait              Boolean          True to display a progress bar (defaults to false)
2769 width             Number           The width of the dialog in pixels
2770 </pre>
2771          *
2772          * Example usage:
2773          * <pre><code>
2774 Roo.Msg.show({
2775    title: 'Address',
2776    msg: 'Please enter your address:',
2777    width: 300,
2778    buttons: Roo.MessageBox.OKCANCEL,
2779    multiline: true,
2780    fn: saveAddress,
2781    animEl: 'addAddressBtn'
2782 });
2783 </code></pre>
2784          * @param {Object} config Configuration options
2785          * @return {Roo.MessageBox} This message box
2786          */
2787         show : function(options)
2788         {
2789             
2790             // this causes nightmares if you show one dialog after another
2791             // especially on callbacks..
2792              
2793             if(this.isVisible()){
2794                 
2795                 this.hide();
2796                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2797                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2798                 Roo.log("New Dialog Message:" +  options.msg )
2799                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2800                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2801                 
2802             }
2803             var d = this.getDialog();
2804             opt = options;
2805             d.setTitle(opt.title || "&#160;");
2806             d.closeEl.setDisplayed(opt.closable !== false);
2807             activeTextEl = textboxEl;
2808             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2809             if(opt.prompt){
2810                 if(opt.multiline){
2811                     textboxEl.hide();
2812                     textareaEl.show();
2813                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2814                         opt.multiline : this.defaultTextHeight);
2815                     activeTextEl = textareaEl;
2816                 }else{
2817                     textboxEl.show();
2818                     textareaEl.hide();
2819                 }
2820             }else{
2821                 textboxEl.hide();
2822                 textareaEl.hide();
2823             }
2824             progressEl.setDisplayed(opt.progress === true);
2825             this.updateProgress(0);
2826             activeTextEl.dom.value = opt.value || "";
2827             if(opt.prompt){
2828                 dlg.setDefaultButton(activeTextEl);
2829             }else{
2830                 var bs = opt.buttons;
2831                 var db = null;
2832                 if(bs && bs.ok){
2833                     db = buttons["ok"];
2834                 }else if(bs && bs.yes){
2835                     db = buttons["yes"];
2836                 }
2837                 dlg.setDefaultButton(db);
2838             }
2839             bwidth = updateButtons(opt.buttons);
2840             this.updateText(opt.msg);
2841             if(opt.cls){
2842                 d.el.addClass(opt.cls);
2843             }
2844             d.proxyDrag = opt.proxyDrag === true;
2845             d.modal = opt.modal !== false;
2846             d.mask = opt.modal !== false ? mask : false;
2847             if(!d.isVisible()){
2848                 // force it to the end of the z-index stack so it gets a cursor in FF
2849                 document.body.appendChild(dlg.el.dom);
2850                 d.animateTarget = null;
2851                 d.show(options.animEl);
2852             }
2853             return this;
2854         },
2855
2856         /**
2857          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2858          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2859          * and closing the message box when the process is complete.
2860          * @param {String} title The title bar text
2861          * @param {String} msg The message box body text
2862          * @return {Roo.MessageBox} This message box
2863          */
2864         progress : function(title, msg){
2865             this.show({
2866                 title : title,
2867                 msg : msg,
2868                 buttons: false,
2869                 progress:true,
2870                 closable:false,
2871                 minWidth: this.minProgressWidth,
2872                 modal : true
2873             });
2874             return this;
2875         },
2876
2877         /**
2878          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2879          * If a callback function is passed it will be called after the user clicks the button, and the
2880          * id of the button that was clicked will be passed as the only parameter to the callback
2881          * (could also be the top-right close button).
2882          * @param {String} title The title bar text
2883          * @param {String} msg The message box body text
2884          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2885          * @param {Object} scope (optional) The scope of the callback function
2886          * @return {Roo.MessageBox} This message box
2887          */
2888         alert : function(title, msg, fn, scope){
2889             this.show({
2890                 title : title,
2891                 msg : msg,
2892                 buttons: this.OK,
2893                 fn: fn,
2894                 scope : scope,
2895                 modal : true
2896             });
2897             return this;
2898         },
2899
2900         /**
2901          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2902          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2903          * You are responsible for closing the message box when the process is complete.
2904          * @param {String} msg The message box body text
2905          * @param {String} title (optional) The title bar text
2906          * @return {Roo.MessageBox} This message box
2907          */
2908         wait : function(msg, title){
2909             this.show({
2910                 title : title,
2911                 msg : msg,
2912                 buttons: false,
2913                 closable:false,
2914                 progress:true,
2915                 modal:true,
2916                 width:300,
2917                 wait:true
2918             });
2919             waitTimer = Roo.TaskMgr.start({
2920                 run: function(i){
2921                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2922                 },
2923                 interval: 1000
2924             });
2925             return this;
2926         },
2927
2928         /**
2929          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2930          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2931          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2932          * @param {String} title The title bar text
2933          * @param {String} msg The message box body text
2934          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2935          * @param {Object} scope (optional) The scope of the callback function
2936          * @return {Roo.MessageBox} This message box
2937          */
2938         confirm : function(title, msg, fn, scope){
2939             this.show({
2940                 title : title,
2941                 msg : msg,
2942                 buttons: this.YESNO,
2943                 fn: fn,
2944                 scope : scope,
2945                 modal : true
2946             });
2947             return this;
2948         },
2949
2950         /**
2951          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2952          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2953          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2954          * (could also be the top-right close button) and the text that was entered will be passed as the two
2955          * parameters to the callback.
2956          * @param {String} title The title bar text
2957          * @param {String} msg The message box body text
2958          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2959          * @param {Object} scope (optional) The scope of the callback function
2960          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2961          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2962          * @return {Roo.MessageBox} This message box
2963          */
2964         prompt : function(title, msg, fn, scope, multiline){
2965             this.show({
2966                 title : title,
2967                 msg : msg,
2968                 buttons: this.OKCANCEL,
2969                 fn: fn,
2970                 minWidth:250,
2971                 scope : scope,
2972                 prompt:true,
2973                 multiline: multiline,
2974                 modal : true
2975             });
2976             return this;
2977         },
2978
2979         /**
2980          * Button config that displays a single OK button
2981          * @type Object
2982          */
2983         OK : {ok:true},
2984         /**
2985          * Button config that displays Yes and No buttons
2986          * @type Object
2987          */
2988         YESNO : {yes:true, no:true},
2989         /**
2990          * Button config that displays OK and Cancel buttons
2991          * @type Object
2992          */
2993         OKCANCEL : {ok:true, cancel:true},
2994         /**
2995          * Button config that displays Yes, No and Cancel buttons
2996          * @type Object
2997          */
2998         YESNOCANCEL : {yes:true, no:true, cancel:true},
2999
3000         /**
3001          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3002          * @type Number
3003          */
3004         defaultTextHeight : 75,
3005         /**
3006          * The maximum width in pixels of the message box (defaults to 600)
3007          * @type Number
3008          */
3009         maxWidth : 600,
3010         /**
3011          * The minimum width in pixels of the message box (defaults to 100)
3012          * @type Number
3013          */
3014         minWidth : 100,
3015         /**
3016          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3017          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3018          * @type Number
3019          */
3020         minProgressWidth : 250,
3021         /**
3022          * An object containing the default button text strings that can be overriden for localized language support.
3023          * Supported properties are: ok, cancel, yes and no.
3024          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3025          * @type Object
3026          */
3027         buttonText : {
3028             ok : "OK",
3029             cancel : "Cancel",
3030             yes : "Yes",
3031             no : "No"
3032         }
3033     };
3034 }();
3035
3036 /**
3037  * Shorthand for {@link Roo.MessageBox}
3038  */
3039 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3040 Roo.Msg = Roo.Msg || Roo.MessageBox;
3041 /*
3042  * - LGPL
3043  *
3044  * navbar
3045  * 
3046  */
3047
3048 /**
3049  * @class Roo.bootstrap.Navbar
3050  * @extends Roo.bootstrap.Component
3051  * Bootstrap Navbar class
3052
3053  * @constructor
3054  * Create a new Navbar
3055  * @param {Object} config The config object
3056  */
3057
3058
3059 Roo.bootstrap.Navbar = function(config){
3060     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3061     
3062 };
3063
3064 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3065     
3066     
3067    
3068     // private
3069     navItems : false,
3070     loadMask : false,
3071     
3072     
3073     getAutoCreate : function(){
3074         
3075         
3076         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3077         
3078     },
3079     
3080     initEvents :function ()
3081     {
3082         //Roo.log(this.el.select('.navbar-toggle',true));
3083         this.el.select('.navbar-toggle',true).on('click', function() {
3084            // Roo.log('click');
3085             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3086         }, this);
3087         
3088         var mark = {
3089             tag: "div",
3090             cls:"x-dlg-mask"
3091         }
3092         
3093         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3094         
3095         var size = this.el.getSize();
3096         this.maskEl.setSize(size.width, size.height);
3097         this.maskEl.enableDisplayMode("block");
3098         this.maskEl.hide();
3099         
3100         if(this.loadMask){
3101             this.maskEl.show();
3102         }
3103     },
3104     
3105     
3106     getChildContainer : function()
3107     {
3108         if (this.el.select('.collapse').getCount()) {
3109             return this.el.select('.collapse',true).first();
3110         }
3111         
3112         return this.el;
3113     },
3114     
3115     mask : function()
3116     {
3117         this.maskEl.show();
3118     },
3119     
3120     unmask : function()
3121     {
3122         this.maskEl.hide();
3123     } 
3124     
3125     
3126     
3127     
3128 });
3129
3130
3131
3132  
3133
3134  /*
3135  * - LGPL
3136  *
3137  * navbar
3138  * 
3139  */
3140
3141 /**
3142  * @class Roo.bootstrap.NavSimplebar
3143  * @extends Roo.bootstrap.Navbar
3144  * Bootstrap Sidebar class
3145  *
3146  * @cfg {Boolean} inverse is inverted color
3147  * 
3148  * @cfg {String} type (nav | pills | tabs)
3149  * @cfg {Boolean} arrangement stacked | justified
3150  * @cfg {String} align (left | right) alignment
3151  * 
3152  * @cfg {Boolean} main (true|false) main nav bar? default false
3153  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3154  * 
3155  * @cfg {String} tag (header|footer|nav|div) default is nav 
3156
3157  * 
3158  * 
3159  * 
3160  * @constructor
3161  * Create a new Sidebar
3162  * @param {Object} config The config object
3163  */
3164
3165
3166 Roo.bootstrap.NavSimplebar = function(config){
3167     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3168 };
3169
3170 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3171     
3172     inverse: false,
3173     
3174     type: false,
3175     arrangement: '',
3176     align : false,
3177     
3178     
3179     
3180     main : false,
3181     
3182     
3183     tag : false,
3184     
3185     
3186     getAutoCreate : function(){
3187         
3188         
3189         var cfg = {
3190             tag : this.tag || 'div',
3191             cls : 'navbar'
3192         };
3193           
3194         
3195         cfg.cn = [
3196             {
3197                 cls: 'nav',
3198                 tag : 'ul'
3199             }
3200         ];
3201         
3202          
3203         this.type = this.type || 'nav';
3204         if (['tabs','pills'].indexOf(this.type)!==-1) {
3205             cfg.cn[0].cls += ' nav-' + this.type
3206         
3207         
3208         } else {
3209             if (this.type!=='nav') {
3210                 Roo.log('nav type must be nav/tabs/pills')
3211             }
3212             cfg.cn[0].cls += ' navbar-nav'
3213         }
3214         
3215         
3216         
3217         
3218         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3219             cfg.cn[0].cls += ' nav-' + this.arrangement;
3220         }
3221         
3222         
3223         if (this.align === 'right') {
3224             cfg.cn[0].cls += ' navbar-right';
3225         }
3226         
3227         if (this.inverse) {
3228             cfg.cls += ' navbar-inverse';
3229             
3230         }
3231         
3232         
3233         return cfg;
3234     
3235         
3236     }
3237     
3238     
3239     
3240 });
3241
3242
3243
3244  
3245
3246  
3247        /*
3248  * - LGPL
3249  *
3250  * navbar
3251  * 
3252  */
3253
3254 /**
3255  * @class Roo.bootstrap.NavHeaderbar
3256  * @extends Roo.bootstrap.NavSimplebar
3257  * Bootstrap Sidebar class
3258  *
3259  * @cfg {String} brand what is brand
3260  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3261  * @cfg {String} brand_href href of the brand
3262  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3263  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3264  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3265  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3266  * 
3267  * @constructor
3268  * Create a new Sidebar
3269  * @param {Object} config The config object
3270  */
3271
3272
3273 Roo.bootstrap.NavHeaderbar = function(config){
3274     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3275       
3276 };
3277
3278 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3279     
3280     position: '',
3281     brand: '',
3282     brand_href: false,
3283     srButton : true,
3284     autohide : false,
3285     desktopCenter : false,
3286    
3287     
3288     getAutoCreate : function(){
3289         
3290         var   cfg = {
3291             tag: this.nav || 'nav',
3292             cls: 'navbar',
3293             role: 'navigation',
3294             cn: []
3295         };
3296         
3297         var cn = cfg.cn;
3298         if (this.desktopCenter) {
3299             cn.push({cls : 'container', cn : []});
3300             cn = cn[0].cn;
3301         }
3302         
3303         if(this.srButton){
3304             cn.push({
3305                 tag: 'div',
3306                 cls: 'navbar-header',
3307                 cn: [
3308                     {
3309                         tag: 'button',
3310                         type: 'button',
3311                         cls: 'navbar-toggle',
3312                         'data-toggle': 'collapse',
3313                         cn: [
3314                             {
3315                                 tag: 'span',
3316                                 cls: 'sr-only',
3317                                 html: 'Toggle navigation'
3318                             },
3319                             {
3320                                 tag: 'span',
3321                                 cls: 'icon-bar'
3322                             },
3323                             {
3324                                 tag: 'span',
3325                                 cls: 'icon-bar'
3326                             },
3327                             {
3328                                 tag: 'span',
3329                                 cls: 'icon-bar'
3330                             }
3331                         ]
3332                     }
3333                 ]
3334             });
3335         }
3336         
3337         cn.push({
3338             tag: 'div',
3339             cls: 'collapse navbar-collapse',
3340             cn : []
3341         });
3342         
3343         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3344         
3345         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3346             cfg.cls += ' navbar-' + this.position;
3347             
3348             // tag can override this..
3349             
3350             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3351         }
3352         
3353         if (this.brand !== '') {
3354             cn[0].cn.push({
3355                 tag: 'a',
3356                 href: this.brand_href ? this.brand_href : '#',
3357                 cls: 'navbar-brand',
3358                 cn: [
3359                 this.brand
3360                 ]
3361             });
3362         }
3363         
3364         if(this.main){
3365             cfg.cls += ' main-nav';
3366         }
3367         
3368         
3369         return cfg;
3370
3371         
3372     },
3373     getHeaderChildContainer : function()
3374     {
3375         if (this.el.select('.navbar-header').getCount()) {
3376             return this.el.select('.navbar-header',true).first();
3377         }
3378         
3379         return this.getChildContainer();
3380     },
3381     
3382     
3383     initEvents : function()
3384     {
3385         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3386         
3387         if (this.autohide) {
3388             
3389             var prevScroll = 0;
3390             var ft = this.el;
3391             
3392             Roo.get(document).on('scroll',function(e) {
3393                 var ns = Roo.get(document).getScroll().top;
3394                 var os = prevScroll;
3395                 prevScroll = ns;
3396                 
3397                 if(ns > os){
3398                     ft.removeClass('slideDown');
3399                     ft.addClass('slideUp');
3400                     return;
3401                 }
3402                 ft.removeClass('slideUp');
3403                 ft.addClass('slideDown');
3404                  
3405               
3406           },this);
3407         }
3408     }    
3409           
3410       
3411     
3412     
3413 });
3414
3415
3416
3417  
3418
3419  /*
3420  * - LGPL
3421  *
3422  * navbar
3423  * 
3424  */
3425
3426 /**
3427  * @class Roo.bootstrap.NavSidebar
3428  * @extends Roo.bootstrap.Navbar
3429  * Bootstrap Sidebar class
3430  * 
3431  * @constructor
3432  * Create a new Sidebar
3433  * @param {Object} config The config object
3434  */
3435
3436
3437 Roo.bootstrap.NavSidebar = function(config){
3438     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3439 };
3440
3441 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3442     
3443     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3444     
3445     getAutoCreate : function(){
3446         
3447         
3448         return  {
3449             tag: 'div',
3450             cls: 'sidebar sidebar-nav'
3451         };
3452     
3453         
3454     }
3455     
3456     
3457     
3458 });
3459
3460
3461
3462  
3463
3464  /*
3465  * - LGPL
3466  *
3467  * nav group
3468  * 
3469  */
3470
3471 /**
3472  * @class Roo.bootstrap.NavGroup
3473  * @extends Roo.bootstrap.Component
3474  * Bootstrap NavGroup class
3475  * @cfg {String} align left | right
3476  * @cfg {Boolean} inverse false | true
3477  * @cfg {String} type (nav|pills|tab) default nav
3478  * @cfg {String} navId - reference Id for navbar.
3479
3480  * 
3481  * @constructor
3482  * Create a new nav group
3483  * @param {Object} config The config object
3484  */
3485
3486 Roo.bootstrap.NavGroup = function(config){
3487     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3488     this.navItems = [];
3489    
3490     Roo.bootstrap.NavGroup.register(this);
3491      this.addEvents({
3492         /**
3493              * @event changed
3494              * Fires when the active item changes
3495              * @param {Roo.bootstrap.NavGroup} this
3496              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3497              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3498          */
3499         'changed': true
3500      });
3501     
3502 };
3503
3504 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3505     
3506     align: '',
3507     inverse: false,
3508     form: false,
3509     type: 'nav',
3510     navId : '',
3511     // private
3512     
3513     navItems : false, 
3514     
3515     getAutoCreate : function()
3516     {
3517         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3518         
3519         cfg = {
3520             tag : 'ul',
3521             cls: 'nav' 
3522         }
3523         
3524         if (['tabs','pills'].indexOf(this.type)!==-1) {
3525             cfg.cls += ' nav-' + this.type
3526         } else {
3527             if (this.type!=='nav') {
3528                 Roo.log('nav type must be nav/tabs/pills')
3529             }
3530             cfg.cls += ' navbar-nav'
3531         }
3532         
3533         if (this.parent().sidebar) {
3534             cfg = {
3535                 tag: 'ul',
3536                 cls: 'dashboard-menu sidebar-menu'
3537             }
3538             
3539             return cfg;
3540         }
3541         
3542         if (this.form === true) {
3543             cfg = {
3544                 tag: 'form',
3545                 cls: 'navbar-form'
3546             }
3547             
3548             if (this.align === 'right') {
3549                 cfg.cls += ' navbar-right';
3550             } else {
3551                 cfg.cls += ' navbar-left';
3552             }
3553         }
3554         
3555         if (this.align === 'right') {
3556             cfg.cls += ' navbar-right';
3557         }
3558         
3559         if (this.inverse) {
3560             cfg.cls += ' navbar-inverse';
3561             
3562         }
3563         
3564         
3565         return cfg;
3566     },
3567     /**
3568     * sets the active Navigation item
3569     * @param {Roo.bootstrap.NavItem} the new current navitem
3570     */
3571     setActiveItem : function(item)
3572     {
3573         var prev = false;
3574         Roo.each(this.navItems, function(v){
3575             if (v == item) {
3576                 return ;
3577             }
3578             if (v.isActive()) {
3579                 v.setActive(false, true);
3580                 prev = v;
3581                 
3582             }
3583             
3584         });
3585
3586         item.setActive(true, true);
3587         this.fireEvent('changed', this, item, prev);
3588         
3589         
3590     },
3591     /**
3592     * gets the active Navigation item
3593     * @return {Roo.bootstrap.NavItem} the current navitem
3594     */
3595     getActive : function()
3596     {
3597         
3598         var prev = false;
3599         Roo.each(this.navItems, function(v){
3600             
3601             if (v.isActive()) {
3602                 prev = v;
3603                 
3604             }
3605             
3606         });
3607         return prev;
3608     },
3609     
3610     indexOfNav : function()
3611     {
3612         
3613         var prev = false;
3614         Roo.each(this.navItems, function(v,i){
3615             
3616             if (v.isActive()) {
3617                 prev = i;
3618                 
3619             }
3620             
3621         });
3622         return prev;
3623     },
3624     /**
3625     * adds a Navigation item
3626     * @param {Roo.bootstrap.NavItem} the navitem to add
3627     */
3628     addItem : function(cfg)
3629     {
3630         var cn = new Roo.bootstrap.NavItem(cfg);
3631         this.register(cn);
3632         cn.parentId = this.id;
3633         cn.onRender(this.el, null);
3634         return cn;
3635     },
3636     /**
3637     * register a Navigation item
3638     * @param {Roo.bootstrap.NavItem} the navitem to add
3639     */
3640     register : function(item)
3641     {
3642         this.navItems.push( item);
3643         item.navId = this.navId;
3644     
3645     },
3646     
3647     /**
3648     * clear all the Navigation item
3649     */
3650    
3651     clearAll : function()
3652     {
3653         this.navItems = [];
3654         this.el.dom.innerHTML = '';
3655     },
3656     
3657     getNavItem: function(tabId)
3658     {
3659         var ret = false;
3660         Roo.each(this.navItems, function(e) {
3661             if (e.tabId == tabId) {
3662                ret =  e;
3663                return false;
3664             }
3665             return true;
3666             
3667         });
3668         return ret;
3669     },
3670     
3671     setActiveNext : function()
3672     {
3673         var i = this.indexOfNav(this.getActive());
3674         if (i > this.navItems.length) {
3675             return;
3676         }
3677         this.setActiveItem(this.navItems[i+1]);
3678     },
3679     setActivePrev : function()
3680     {
3681         var i = this.indexOfNav(this.getActive());
3682         if (i  < 1) {
3683             return;
3684         }
3685         this.setActiveItem(this.navItems[i-1]);
3686     },
3687     clearWasActive : function(except) {
3688         Roo.each(this.navItems, function(e) {
3689             if (e.tabId != except.tabId && e.was_active) {
3690                e.was_active = false;
3691                return false;
3692             }
3693             return true;
3694             
3695         });
3696     },
3697     getWasActive : function ()
3698     {
3699         var r = false;
3700         Roo.each(this.navItems, function(e) {
3701             if (e.was_active) {
3702                r = e;
3703                return false;
3704             }
3705             return true;
3706             
3707         });
3708         return r;
3709     }
3710     
3711     
3712 });
3713
3714  
3715 Roo.apply(Roo.bootstrap.NavGroup, {
3716     
3717     groups: {},
3718      /**
3719     * register a Navigation Group
3720     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3721     */
3722     register : function(navgrp)
3723     {
3724         this.groups[navgrp.navId] = navgrp;
3725         
3726     },
3727     /**
3728     * fetch a Navigation Group based on the navigation ID
3729     * @param {string} the navgroup to add
3730     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3731     */
3732     get: function(navId) {
3733         if (typeof(this.groups[navId]) == 'undefined') {
3734             return false;
3735             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3736         }
3737         return this.groups[navId] ;
3738     }
3739     
3740     
3741     
3742 });
3743
3744  /*
3745  * - LGPL
3746  *
3747  * row
3748  * 
3749  */
3750
3751 /**
3752  * @class Roo.bootstrap.NavItem
3753  * @extends Roo.bootstrap.Component
3754  * Bootstrap Navbar.NavItem class
3755  * @cfg {String} href  link to
3756  * @cfg {String} html content of button
3757  * @cfg {String} badge text inside badge
3758  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3759  * @cfg {String} glyphicon name of glyphicon
3760  * @cfg {String} icon name of font awesome icon
3761  * @cfg {Boolean} active Is item active
3762  * @cfg {Boolean} disabled Is item disabled
3763  
3764  * @cfg {Boolean} preventDefault (true | false) default false
3765  * @cfg {String} tabId the tab that this item activates.
3766  * @cfg {String} tagtype (a|span) render as a href or span?
3767  * @cfg {Boolean} animateRef (true|false) link to element default false
3768   
3769  * @constructor
3770  * Create a new Navbar Item
3771  * @param {Object} config The config object
3772  */
3773 Roo.bootstrap.NavItem = function(config){
3774     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3775     this.addEvents({
3776         // raw events
3777         /**
3778          * @event click
3779          * The raw click event for the entire grid.
3780          * @param {Roo.EventObject} e
3781          */
3782         "click" : true,
3783          /**
3784             * @event changed
3785             * Fires when the active item active state changes
3786             * @param {Roo.bootstrap.NavItem} this
3787             * @param {boolean} state the new state
3788              
3789          */
3790         'changed': true,
3791         /**
3792             * @event scrollto
3793             * Fires when scroll to element
3794             * @param {Roo.bootstrap.NavItem} this
3795             * @param {Object} options
3796             * @param {Roo.EventObject} e
3797              
3798          */
3799         'scrollto': true
3800     });
3801    
3802 };
3803
3804 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3805     
3806     href: false,
3807     html: '',
3808     badge: '',
3809     icon: false,
3810     glyphicon: false,
3811     active: false,
3812     preventDefault : false,
3813     tabId : false,
3814     tagtype : 'a',
3815     disabled : false,
3816     animateRef : false,
3817     was_active : false,
3818     
3819     getAutoCreate : function(){
3820          
3821         var cfg = {
3822             tag: 'li',
3823             cls: 'nav-item'
3824             
3825         }
3826         if (this.active) {
3827             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3828         }
3829         if (this.disabled) {
3830             cfg.cls += ' disabled';
3831         }
3832         
3833         if (this.href || this.html || this.glyphicon || this.icon) {
3834             cfg.cn = [
3835                 {
3836                     tag: this.tagtype,
3837                     href : this.href || "#",
3838                     html: this.html || ''
3839                 }
3840             ];
3841             
3842             if (this.icon) {
3843                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3844             }
3845
3846             if(this.glyphicon) {
3847                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3848             }
3849             
3850             if (this.menu) {
3851                 
3852                 cfg.cn[0].html += " <span class='caret'></span>";
3853              
3854             }
3855             
3856             if (this.badge !== '') {
3857                  
3858                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3859             }
3860         }
3861         
3862         
3863         
3864         return cfg;
3865     },
3866     initEvents: function() 
3867     {
3868         if (typeof (this.menu) != 'undefined') {
3869             this.menu.parentType = this.xtype;
3870             this.menu.triggerEl = this.el;
3871             this.menu = this.addxtype(Roo.apply({}, this.menu));
3872         }
3873         
3874         this.el.select('a',true).on('click', this.onClick, this);
3875         
3876         if(this.tagtype == 'span'){
3877             this.el.select('span',true).on('click', this.onClick, this);
3878         }
3879        
3880         // at this point parent should be available..
3881         this.parent().register(this);
3882     },
3883     
3884     onClick : function(e)
3885     {
3886         if(
3887                 this.preventDefault || 
3888                 this.href == '#' ||
3889                 (this.animateRef && this.href.charAt(0) == '#')
3890         ){
3891             e.preventDefault();
3892         }
3893         
3894         if (this.disabled) {
3895             return;
3896         }
3897         
3898         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3899         if (tg && tg.transition) {
3900             Roo.log("waiting for the transitionend");
3901             return;
3902         }
3903         
3904         Roo.log("fire event clicked");
3905         if(this.fireEvent('click', this, e) === false){
3906             return;
3907         };
3908         
3909         if(this.tagtype == 'span'){
3910             return;
3911         }
3912         
3913         if(this.animateRef && this.href.charAt(0) == '#'){
3914             this.scrollToElement(e);
3915             return;
3916         }
3917         
3918         var p = this.parent();
3919         if (['tabs','pills'].indexOf(p.type)!==-1) {
3920             if (typeof(p.setActiveItem) !== 'undefined') {
3921                 p.setActiveItem(this);
3922             }
3923         }
3924         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3925         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3926             // remove the collapsed menu expand...
3927             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3928         }
3929         
3930     },
3931     
3932     isActive: function () {
3933         return this.active
3934     },
3935     setActive : function(state, fire, is_was_active)
3936     {
3937         if (this.active && !state & this.navId) {
3938             this.was_active = true;
3939             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3940             if (nv) {
3941                 nv.clearWasActive(this);
3942             }
3943             
3944         }
3945         this.active = state;
3946         
3947         if (!state ) {
3948             this.el.removeClass('active');
3949         } else if (!this.el.hasClass('active')) {
3950             this.el.addClass('active');
3951         }
3952         if (fire) {
3953             this.fireEvent('changed', this, state);
3954         }
3955         
3956         // show a panel if it's registered and related..
3957         
3958         if (!this.navId || !this.tabId || !state || is_was_active) {
3959             return;
3960         }
3961         
3962         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3963         if (!tg) {
3964             return;
3965         }
3966         var pan = tg.getPanelByName(this.tabId);
3967         if (!pan) {
3968             return;
3969         }
3970         // if we can not flip to new panel - go back to old nav highlight..
3971         if (false == tg.showPanel(pan)) {
3972             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3973             if (nv) {
3974                 var onav = nv.getWasActive();
3975                 if (onav) {
3976                     onav.setActive(true, false, true);
3977                 }
3978             }
3979             
3980         }
3981         
3982         
3983         
3984     },
3985      // this should not be here...
3986     setDisabled : function(state)
3987     {
3988         this.disabled = state;
3989         if (!state ) {
3990             this.el.removeClass('disabled');
3991         } else if (!this.el.hasClass('disabled')) {
3992             this.el.addClass('disabled');
3993         }
3994         
3995     },
3996     
3997     /**
3998      * Fetch the element to display the tooltip on.
3999      * @return {Roo.Element} defaults to this.el
4000      */
4001     tooltipEl : function()
4002     {
4003         return this.el.select('' + this.tagtype + '', true).first();
4004     },
4005     
4006     scrollToElement : function(e)
4007     {
4008         var c = document.body;
4009         
4010         var target = Roo.get(c).select('a[name=' + this.href.replace('#', '') +']', true).first();
4011         
4012         if(!target){
4013             return;
4014         }
4015
4016         var o = target.calcOffsetsTo(c);
4017         
4018         var options = {
4019             target : target,
4020             value : o[1]
4021         }
4022         
4023         this.fireEvent('scrollto', this, options, e);
4024         
4025         Roo.get(c).scrollTo('top', options.value, true);
4026         
4027         return;
4028     }
4029 });
4030  
4031
4032  /*
4033  * - LGPL
4034  *
4035  * sidebar item
4036  *
4037  *  li
4038  *    <span> icon </span>
4039  *    <span> text </span>
4040  *    <span>badge </span>
4041  */
4042
4043 /**
4044  * @class Roo.bootstrap.NavSidebarItem
4045  * @extends Roo.bootstrap.NavItem
4046  * Bootstrap Navbar.NavSidebarItem class
4047  * @constructor
4048  * Create a new Navbar Button
4049  * @param {Object} config The config object
4050  */
4051 Roo.bootstrap.NavSidebarItem = function(config){
4052     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4053     this.addEvents({
4054         // raw events
4055         /**
4056          * @event click
4057          * The raw click event for the entire grid.
4058          * @param {Roo.EventObject} e
4059          */
4060         "click" : true,
4061          /**
4062             * @event changed
4063             * Fires when the active item active state changes
4064             * @param {Roo.bootstrap.NavSidebarItem} this
4065             * @param {boolean} state the new state
4066              
4067          */
4068         'changed': true
4069     });
4070    
4071 };
4072
4073 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4074     
4075     
4076     getAutoCreate : function(){
4077         
4078         
4079         var a = {
4080                 tag: 'a',
4081                 href : this.href || '#',
4082                 cls: '',
4083                 html : '',
4084                 cn : []
4085         };
4086         var cfg = {
4087             tag: 'li',
4088             cls: '',
4089             cn: [ a ]
4090         }
4091         var span = {
4092             tag: 'span',
4093             html : this.html || ''
4094         }
4095         
4096         
4097         if (this.active) {
4098             cfg.cls += ' active';
4099         }
4100         
4101         // left icon..
4102         if (this.glyphicon || this.icon) {
4103             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4104             a.cn.push({ tag : 'i', cls : c }) ;
4105         }
4106         // html..
4107         a.cn.push(span);
4108         // then badge..
4109         if (this.badge !== '') {
4110             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4111         }
4112         // fi
4113         if (this.menu) {
4114             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4115             a.cls += 'dropdown-toggle treeview' ;
4116             
4117         }
4118         
4119         
4120         
4121         return cfg;
4122          
4123            
4124     }
4125    
4126      
4127  
4128 });
4129  
4130
4131  /*
4132  * - LGPL
4133  *
4134  * row
4135  * 
4136  */
4137
4138 /**
4139  * @class Roo.bootstrap.Row
4140  * @extends Roo.bootstrap.Component
4141  * Bootstrap Row class (contains columns...)
4142  * 
4143  * @constructor
4144  * Create a new Row
4145  * @param {Object} config The config object
4146  */
4147
4148 Roo.bootstrap.Row = function(config){
4149     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4150 };
4151
4152 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4153     
4154     getAutoCreate : function(){
4155        return {
4156             cls: 'row clearfix'
4157        };
4158     }
4159     
4160     
4161 });
4162
4163  
4164
4165  /*
4166  * - LGPL
4167  *
4168  * element
4169  * 
4170  */
4171
4172 /**
4173  * @class Roo.bootstrap.Element
4174  * @extends Roo.bootstrap.Component
4175  * Bootstrap Element class
4176  * @cfg {String} html contents of the element
4177  * @cfg {String} tag tag of the element
4178  * @cfg {String} cls class of the element
4179  * 
4180  * @constructor
4181  * Create a new Element
4182  * @param {Object} config The config object
4183  */
4184
4185 Roo.bootstrap.Element = function(config){
4186     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4187 };
4188
4189 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4190     
4191     tag: 'div',
4192     cls: '',
4193     html: '',
4194      
4195     
4196     getAutoCreate : function(){
4197         
4198         var cfg = {
4199             tag: this.tag,
4200             cls: this.cls,
4201             html: this.html
4202         }
4203         
4204         
4205         
4206         return cfg;
4207     },
4208     
4209     getValue : function()
4210     {
4211         return this.el.dom.innerHTML;
4212     },
4213     
4214     setValue : function(value)
4215     {
4216         this.el.dom.innerHTML = value;
4217     }
4218    
4219 });
4220
4221  
4222
4223  /*
4224  * - LGPL
4225  *
4226  * pagination
4227  * 
4228  */
4229
4230 /**
4231  * @class Roo.bootstrap.Pagination
4232  * @extends Roo.bootstrap.Component
4233  * Bootstrap Pagination class
4234  * @cfg {String} size xs | sm | md | lg
4235  * @cfg {Boolean} inverse false | true
4236  * 
4237  * @constructor
4238  * Create a new Pagination
4239  * @param {Object} config The config object
4240  */
4241
4242 Roo.bootstrap.Pagination = function(config){
4243     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4244 };
4245
4246 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4247     
4248     cls: false,
4249     size: false,
4250     inverse: false,
4251     
4252     getAutoCreate : function(){
4253         var cfg = {
4254             tag: 'ul',
4255                 cls: 'pagination'
4256         };
4257         if (this.inverse) {
4258             cfg.cls += ' inverse';
4259         }
4260         if (this.html) {
4261             cfg.html=this.html;
4262         }
4263         if (this.cls) {
4264             cfg.cls += " " + this.cls;
4265         }
4266         return cfg;
4267     }
4268    
4269 });
4270
4271  
4272
4273  /*
4274  * - LGPL
4275  *
4276  * Pagination item
4277  * 
4278  */
4279
4280
4281 /**
4282  * @class Roo.bootstrap.PaginationItem
4283  * @extends Roo.bootstrap.Component
4284  * Bootstrap PaginationItem class
4285  * @cfg {String} html text
4286  * @cfg {String} href the link
4287  * @cfg {Boolean} preventDefault (true | false) default true
4288  * @cfg {Boolean} active (true | false) default false
4289  * @cfg {Boolean} disabled default false
4290  * 
4291  * 
4292  * @constructor
4293  * Create a new PaginationItem
4294  * @param {Object} config The config object
4295  */
4296
4297
4298 Roo.bootstrap.PaginationItem = function(config){
4299     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4300     this.addEvents({
4301         // raw events
4302         /**
4303          * @event click
4304          * The raw click event for the entire grid.
4305          * @param {Roo.EventObject} e
4306          */
4307         "click" : true
4308     });
4309 };
4310
4311 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4312     
4313     href : false,
4314     html : false,
4315     preventDefault: true,
4316     active : false,
4317     cls : false,
4318     disabled: false,
4319     
4320     getAutoCreate : function(){
4321         var cfg= {
4322             tag: 'li',
4323             cn: [
4324                 {
4325                     tag : 'a',
4326                     href : this.href ? this.href : '#',
4327                     html : this.html ? this.html : ''
4328                 }
4329             ]
4330         };
4331         
4332         if(this.cls){
4333             cfg.cls = this.cls;
4334         }
4335         
4336         if(this.disabled){
4337             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4338         }
4339         
4340         if(this.active){
4341             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4342         }
4343         
4344         return cfg;
4345     },
4346     
4347     initEvents: function() {
4348         
4349         this.el.on('click', this.onClick, this);
4350         
4351     },
4352     onClick : function(e)
4353     {
4354         Roo.log('PaginationItem on click ');
4355         if(this.preventDefault){
4356             e.preventDefault();
4357         }
4358         
4359         if(this.disabled){
4360             return;
4361         }
4362         
4363         this.fireEvent('click', this, e);
4364     }
4365    
4366 });
4367
4368  
4369
4370  /*
4371  * - LGPL
4372  *
4373  * slider
4374  * 
4375  */
4376
4377
4378 /**
4379  * @class Roo.bootstrap.Slider
4380  * @extends Roo.bootstrap.Component
4381  * Bootstrap Slider class
4382  *    
4383  * @constructor
4384  * Create a new Slider
4385  * @param {Object} config The config object
4386  */
4387
4388 Roo.bootstrap.Slider = function(config){
4389     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4390 };
4391
4392 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4393     
4394     getAutoCreate : function(){
4395         
4396         var cfg = {
4397             tag: 'div',
4398             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4399             cn: [
4400                 {
4401                     tag: 'a',
4402                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4403                 }
4404             ]
4405         }
4406         
4407         return cfg;
4408     }
4409    
4410 });
4411
4412  /*
4413  * Based on:
4414  * Ext JS Library 1.1.1
4415  * Copyright(c) 2006-2007, Ext JS, LLC.
4416  *
4417  * Originally Released Under LGPL - original licence link has changed is not relivant.
4418  *
4419  * Fork - LGPL
4420  * <script type="text/javascript">
4421  */
4422  
4423
4424 /**
4425  * @class Roo.grid.ColumnModel
4426  * @extends Roo.util.Observable
4427  * This is the default implementation of a ColumnModel used by the Grid. It defines
4428  * the columns in the grid.
4429  * <br>Usage:<br>
4430  <pre><code>
4431  var colModel = new Roo.grid.ColumnModel([
4432         {header: "Ticker", width: 60, sortable: true, locked: true},
4433         {header: "Company Name", width: 150, sortable: true},
4434         {header: "Market Cap.", width: 100, sortable: true},
4435         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4436         {header: "Employees", width: 100, sortable: true, resizable: false}
4437  ]);
4438  </code></pre>
4439  * <p>
4440  
4441  * The config options listed for this class are options which may appear in each
4442  * individual column definition.
4443  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4444  * @constructor
4445  * @param {Object} config An Array of column config objects. See this class's
4446  * config objects for details.
4447 */
4448 Roo.grid.ColumnModel = function(config){
4449         /**
4450      * The config passed into the constructor
4451      */
4452     this.config = config;
4453     this.lookup = {};
4454
4455     // if no id, create one
4456     // if the column does not have a dataIndex mapping,
4457     // map it to the order it is in the config
4458     for(var i = 0, len = config.length; i < len; i++){
4459         var c = config[i];
4460         if(typeof c.dataIndex == "undefined"){
4461             c.dataIndex = i;
4462         }
4463         if(typeof c.renderer == "string"){
4464             c.renderer = Roo.util.Format[c.renderer];
4465         }
4466         if(typeof c.id == "undefined"){
4467             c.id = Roo.id();
4468         }
4469         if(c.editor && c.editor.xtype){
4470             c.editor  = Roo.factory(c.editor, Roo.grid);
4471         }
4472         if(c.editor && c.editor.isFormField){
4473             c.editor = new Roo.grid.GridEditor(c.editor);
4474         }
4475         this.lookup[c.id] = c;
4476     }
4477
4478     /**
4479      * The width of columns which have no width specified (defaults to 100)
4480      * @type Number
4481      */
4482     this.defaultWidth = 100;
4483
4484     /**
4485      * Default sortable of columns which have no sortable specified (defaults to false)
4486      * @type Boolean
4487      */
4488     this.defaultSortable = false;
4489
4490     this.addEvents({
4491         /**
4492              * @event widthchange
4493              * Fires when the width of a column changes.
4494              * @param {ColumnModel} this
4495              * @param {Number} columnIndex The column index
4496              * @param {Number} newWidth The new width
4497              */
4498             "widthchange": true,
4499         /**
4500              * @event headerchange
4501              * Fires when the text of a header changes.
4502              * @param {ColumnModel} this
4503              * @param {Number} columnIndex The column index
4504              * @param {Number} newText The new header text
4505              */
4506             "headerchange": true,
4507         /**
4508              * @event hiddenchange
4509              * Fires when a column is hidden or "unhidden".
4510              * @param {ColumnModel} this
4511              * @param {Number} columnIndex The column index
4512              * @param {Boolean} hidden true if hidden, false otherwise
4513              */
4514             "hiddenchange": true,
4515             /**
4516          * @event columnmoved
4517          * Fires when a column is moved.
4518          * @param {ColumnModel} this
4519          * @param {Number} oldIndex
4520          * @param {Number} newIndex
4521          */
4522         "columnmoved" : true,
4523         /**
4524          * @event columlockchange
4525          * Fires when a column's locked state is changed
4526          * @param {ColumnModel} this
4527          * @param {Number} colIndex
4528          * @param {Boolean} locked true if locked
4529          */
4530         "columnlockchange" : true
4531     });
4532     Roo.grid.ColumnModel.superclass.constructor.call(this);
4533 };
4534 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4535     /**
4536      * @cfg {String} header The header text to display in the Grid view.
4537      */
4538     /**
4539      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4540      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4541      * specified, the column's index is used as an index into the Record's data Array.
4542      */
4543     /**
4544      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4545      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4546      */
4547     /**
4548      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4549      * Defaults to the value of the {@link #defaultSortable} property.
4550      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4551      */
4552     /**
4553      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4554      */
4555     /**
4556      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4557      */
4558     /**
4559      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4560      */
4561     /**
4562      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4563      */
4564     /**
4565      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4566      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4567      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4568      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4569      */
4570        /**
4571      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4572      */
4573     /**
4574      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4575      */
4576     /**
4577      * @cfg {String} cursor (Optional)
4578      */
4579     /**
4580      * @cfg {String} tooltip (Optional)
4581      */
4582     /**
4583      * Returns the id of the column at the specified index.
4584      * @param {Number} index The column index
4585      * @return {String} the id
4586      */
4587     getColumnId : function(index){
4588         return this.config[index].id;
4589     },
4590
4591     /**
4592      * Returns the column for a specified id.
4593      * @param {String} id The column id
4594      * @return {Object} the column
4595      */
4596     getColumnById : function(id){
4597         return this.lookup[id];
4598     },
4599
4600     
4601     /**
4602      * Returns the column for a specified dataIndex.
4603      * @param {String} dataIndex The column dataIndex
4604      * @return {Object|Boolean} the column or false if not found
4605      */
4606     getColumnByDataIndex: function(dataIndex){
4607         var index = this.findColumnIndex(dataIndex);
4608         return index > -1 ? this.config[index] : false;
4609     },
4610     
4611     /**
4612      * Returns the index for a specified column id.
4613      * @param {String} id The column id
4614      * @return {Number} the index, or -1 if not found
4615      */
4616     getIndexById : function(id){
4617         for(var i = 0, len = this.config.length; i < len; i++){
4618             if(this.config[i].id == id){
4619                 return i;
4620             }
4621         }
4622         return -1;
4623     },
4624     
4625     /**
4626      * Returns the index for a specified column dataIndex.
4627      * @param {String} dataIndex The column dataIndex
4628      * @return {Number} the index, or -1 if not found
4629      */
4630     
4631     findColumnIndex : function(dataIndex){
4632         for(var i = 0, len = this.config.length; i < len; i++){
4633             if(this.config[i].dataIndex == dataIndex){
4634                 return i;
4635             }
4636         }
4637         return -1;
4638     },
4639     
4640     
4641     moveColumn : function(oldIndex, newIndex){
4642         var c = this.config[oldIndex];
4643         this.config.splice(oldIndex, 1);
4644         this.config.splice(newIndex, 0, c);
4645         this.dataMap = null;
4646         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4647     },
4648
4649     isLocked : function(colIndex){
4650         return this.config[colIndex].locked === true;
4651     },
4652
4653     setLocked : function(colIndex, value, suppressEvent){
4654         if(this.isLocked(colIndex) == value){
4655             return;
4656         }
4657         this.config[colIndex].locked = value;
4658         if(!suppressEvent){
4659             this.fireEvent("columnlockchange", this, colIndex, value);
4660         }
4661     },
4662
4663     getTotalLockedWidth : function(){
4664         var totalWidth = 0;
4665         for(var i = 0; i < this.config.length; i++){
4666             if(this.isLocked(i) && !this.isHidden(i)){
4667                 this.totalWidth += this.getColumnWidth(i);
4668             }
4669         }
4670         return totalWidth;
4671     },
4672
4673     getLockedCount : function(){
4674         for(var i = 0, len = this.config.length; i < len; i++){
4675             if(!this.isLocked(i)){
4676                 return i;
4677             }
4678         }
4679     },
4680
4681     /**
4682      * Returns the number of columns.
4683      * @return {Number}
4684      */
4685     getColumnCount : function(visibleOnly){
4686         if(visibleOnly === true){
4687             var c = 0;
4688             for(var i = 0, len = this.config.length; i < len; i++){
4689                 if(!this.isHidden(i)){
4690                     c++;
4691                 }
4692             }
4693             return c;
4694         }
4695         return this.config.length;
4696     },
4697
4698     /**
4699      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4700      * @param {Function} fn
4701      * @param {Object} scope (optional)
4702      * @return {Array} result
4703      */
4704     getColumnsBy : function(fn, scope){
4705         var r = [];
4706         for(var i = 0, len = this.config.length; i < len; i++){
4707             var c = this.config[i];
4708             if(fn.call(scope||this, c, i) === true){
4709                 r[r.length] = c;
4710             }
4711         }
4712         return r;
4713     },
4714
4715     /**
4716      * Returns true if the specified column is sortable.
4717      * @param {Number} col The column index
4718      * @return {Boolean}
4719      */
4720     isSortable : function(col){
4721         if(typeof this.config[col].sortable == "undefined"){
4722             return this.defaultSortable;
4723         }
4724         return this.config[col].sortable;
4725     },
4726
4727     /**
4728      * Returns the rendering (formatting) function defined for the column.
4729      * @param {Number} col The column index.
4730      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4731      */
4732     getRenderer : function(col){
4733         if(!this.config[col].renderer){
4734             return Roo.grid.ColumnModel.defaultRenderer;
4735         }
4736         return this.config[col].renderer;
4737     },
4738
4739     /**
4740      * Sets the rendering (formatting) function for a column.
4741      * @param {Number} col The column index
4742      * @param {Function} fn The function to use to process the cell's raw data
4743      * to return HTML markup for the grid view. The render function is called with
4744      * the following parameters:<ul>
4745      * <li>Data value.</li>
4746      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4747      * <li>css A CSS style string to apply to the table cell.</li>
4748      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4749      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4750      * <li>Row index</li>
4751      * <li>Column index</li>
4752      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4753      */
4754     setRenderer : function(col, fn){
4755         this.config[col].renderer = fn;
4756     },
4757
4758     /**
4759      * Returns the width for the specified column.
4760      * @param {Number} col The column index
4761      * @return {Number}
4762      */
4763     getColumnWidth : function(col){
4764         return this.config[col].width * 1 || this.defaultWidth;
4765     },
4766
4767     /**
4768      * Sets the width for a column.
4769      * @param {Number} col The column index
4770      * @param {Number} width The new width
4771      */
4772     setColumnWidth : function(col, width, suppressEvent){
4773         this.config[col].width = width;
4774         this.totalWidth = null;
4775         if(!suppressEvent){
4776              this.fireEvent("widthchange", this, col, width);
4777         }
4778     },
4779
4780     /**
4781      * Returns the total width of all columns.
4782      * @param {Boolean} includeHidden True to include hidden column widths
4783      * @return {Number}
4784      */
4785     getTotalWidth : function(includeHidden){
4786         if(!this.totalWidth){
4787             this.totalWidth = 0;
4788             for(var i = 0, len = this.config.length; i < len; i++){
4789                 if(includeHidden || !this.isHidden(i)){
4790                     this.totalWidth += this.getColumnWidth(i);
4791                 }
4792             }
4793         }
4794         return this.totalWidth;
4795     },
4796
4797     /**
4798      * Returns the header for the specified column.
4799      * @param {Number} col The column index
4800      * @return {String}
4801      */
4802     getColumnHeader : function(col){
4803         return this.config[col].header;
4804     },
4805
4806     /**
4807      * Sets the header for a column.
4808      * @param {Number} col The column index
4809      * @param {String} header The new header
4810      */
4811     setColumnHeader : function(col, header){
4812         this.config[col].header = header;
4813         this.fireEvent("headerchange", this, col, header);
4814     },
4815
4816     /**
4817      * Returns the tooltip for the specified column.
4818      * @param {Number} col The column index
4819      * @return {String}
4820      */
4821     getColumnTooltip : function(col){
4822             return this.config[col].tooltip;
4823     },
4824     /**
4825      * Sets the tooltip for a column.
4826      * @param {Number} col The column index
4827      * @param {String} tooltip The new tooltip
4828      */
4829     setColumnTooltip : function(col, tooltip){
4830             this.config[col].tooltip = tooltip;
4831     },
4832
4833     /**
4834      * Returns the dataIndex for the specified column.
4835      * @param {Number} col The column index
4836      * @return {Number}
4837      */
4838     getDataIndex : function(col){
4839         return this.config[col].dataIndex;
4840     },
4841
4842     /**
4843      * Sets the dataIndex for a column.
4844      * @param {Number} col The column index
4845      * @param {Number} dataIndex The new dataIndex
4846      */
4847     setDataIndex : function(col, dataIndex){
4848         this.config[col].dataIndex = dataIndex;
4849     },
4850
4851     
4852     
4853     /**
4854      * Returns true if the cell is editable.
4855      * @param {Number} colIndex The column index
4856      * @param {Number} rowIndex The row index
4857      * @return {Boolean}
4858      */
4859     isCellEditable : function(colIndex, rowIndex){
4860         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4861     },
4862
4863     /**
4864      * Returns the editor defined for the cell/column.
4865      * return false or null to disable editing.
4866      * @param {Number} colIndex The column index
4867      * @param {Number} rowIndex The row index
4868      * @return {Object}
4869      */
4870     getCellEditor : function(colIndex, rowIndex){
4871         return this.config[colIndex].editor;
4872     },
4873
4874     /**
4875      * Sets if a column is editable.
4876      * @param {Number} col The column index
4877      * @param {Boolean} editable True if the column is editable
4878      */
4879     setEditable : function(col, editable){
4880         this.config[col].editable = editable;
4881     },
4882
4883
4884     /**
4885      * Returns true if the column is hidden.
4886      * @param {Number} colIndex The column index
4887      * @return {Boolean}
4888      */
4889     isHidden : function(colIndex){
4890         return this.config[colIndex].hidden;
4891     },
4892
4893
4894     /**
4895      * Returns true if the column width cannot be changed
4896      */
4897     isFixed : function(colIndex){
4898         return this.config[colIndex].fixed;
4899     },
4900
4901     /**
4902      * Returns true if the column can be resized
4903      * @return {Boolean}
4904      */
4905     isResizable : function(colIndex){
4906         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4907     },
4908     /**
4909      * Sets if a column is hidden.
4910      * @param {Number} colIndex The column index
4911      * @param {Boolean} hidden True if the column is hidden
4912      */
4913     setHidden : function(colIndex, hidden){
4914         this.config[colIndex].hidden = hidden;
4915         this.totalWidth = null;
4916         this.fireEvent("hiddenchange", this, colIndex, hidden);
4917     },
4918
4919     /**
4920      * Sets the editor for a column.
4921      * @param {Number} col The column index
4922      * @param {Object} editor The editor object
4923      */
4924     setEditor : function(col, editor){
4925         this.config[col].editor = editor;
4926     }
4927 });
4928
4929 Roo.grid.ColumnModel.defaultRenderer = function(value){
4930         if(typeof value == "string" && value.length < 1){
4931             return "&#160;";
4932         }
4933         return value;
4934 };
4935
4936 // Alias for backwards compatibility
4937 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4938 /*
4939  * Based on:
4940  * Ext JS Library 1.1.1
4941  * Copyright(c) 2006-2007, Ext JS, LLC.
4942  *
4943  * Originally Released Under LGPL - original licence link has changed is not relivant.
4944  *
4945  * Fork - LGPL
4946  * <script type="text/javascript">
4947  */
4948  
4949 /**
4950  * @class Roo.LoadMask
4951  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4952  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4953  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4954  * element's UpdateManager load indicator and will be destroyed after the initial load.
4955  * @constructor
4956  * Create a new LoadMask
4957  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4958  * @param {Object} config The config object
4959  */
4960 Roo.LoadMask = function(el, config){
4961     this.el = Roo.get(el);
4962     Roo.apply(this, config);
4963     if(this.store){
4964         this.store.on('beforeload', this.onBeforeLoad, this);
4965         this.store.on('load', this.onLoad, this);
4966         this.store.on('loadexception', this.onLoadException, this);
4967         this.removeMask = false;
4968     }else{
4969         var um = this.el.getUpdateManager();
4970         um.showLoadIndicator = false; // disable the default indicator
4971         um.on('beforeupdate', this.onBeforeLoad, this);
4972         um.on('update', this.onLoad, this);
4973         um.on('failure', this.onLoad, this);
4974         this.removeMask = true;
4975     }
4976 };
4977
4978 Roo.LoadMask.prototype = {
4979     /**
4980      * @cfg {Boolean} removeMask
4981      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4982      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4983      */
4984     /**
4985      * @cfg {String} msg
4986      * The text to display in a centered loading message box (defaults to 'Loading...')
4987      */
4988     msg : 'Loading...',
4989     /**
4990      * @cfg {String} msgCls
4991      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4992      */
4993     msgCls : 'x-mask-loading',
4994
4995     /**
4996      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4997      * @type Boolean
4998      */
4999     disabled: false,
5000
5001     /**
5002      * Disables the mask to prevent it from being displayed
5003      */
5004     disable : function(){
5005        this.disabled = true;
5006     },
5007
5008     /**
5009      * Enables the mask so that it can be displayed
5010      */
5011     enable : function(){
5012         this.disabled = false;
5013     },
5014     
5015     onLoadException : function()
5016     {
5017         Roo.log(arguments);
5018         
5019         if (typeof(arguments[3]) != 'undefined') {
5020             Roo.MessageBox.alert("Error loading",arguments[3]);
5021         } 
5022         /*
5023         try {
5024             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5025                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5026             }   
5027         } catch(e) {
5028             
5029         }
5030         */
5031     
5032         
5033         
5034         this.el.unmask(this.removeMask);
5035     },
5036     // private
5037     onLoad : function()
5038     {
5039         this.el.unmask(this.removeMask);
5040     },
5041
5042     // private
5043     onBeforeLoad : function(){
5044         if(!this.disabled){
5045             this.el.mask(this.msg, this.msgCls);
5046         }
5047     },
5048
5049     // private
5050     destroy : function(){
5051         if(this.store){
5052             this.store.un('beforeload', this.onBeforeLoad, this);
5053             this.store.un('load', this.onLoad, this);
5054             this.store.un('loadexception', this.onLoadException, this);
5055         }else{
5056             var um = this.el.getUpdateManager();
5057             um.un('beforeupdate', this.onBeforeLoad, this);
5058             um.un('update', this.onLoad, this);
5059             um.un('failure', this.onLoad, this);
5060         }
5061     }
5062 };/*
5063  * - LGPL
5064  *
5065  * table
5066  * 
5067  */
5068
5069 /**
5070  * @class Roo.bootstrap.Table
5071  * @extends Roo.bootstrap.Component
5072  * Bootstrap Table class
5073  * @cfg {String} cls table class
5074  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5075  * @cfg {String} bgcolor Specifies the background color for a table
5076  * @cfg {Number} border Specifies whether the table cells should have borders or not
5077  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5078  * @cfg {Number} cellspacing Specifies the space between cells
5079  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5080  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5081  * @cfg {String} sortable Specifies that the table should be sortable
5082  * @cfg {String} summary Specifies a summary of the content of a table
5083  * @cfg {Number} width Specifies the width of a table
5084  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5085  * 
5086  * @cfg {boolean} striped Should the rows be alternative striped
5087  * @cfg {boolean} bordered Add borders to the table
5088  * @cfg {boolean} hover Add hover highlighting
5089  * @cfg {boolean} condensed Format condensed
5090  * @cfg {boolean} responsive Format condensed
5091  * @cfg {Boolean} loadMask (true|false) default false
5092  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5093  * @cfg {Boolean} thead (true|false) generate thead, default true
5094  * @cfg {Boolean} RowSelection (true|false) default false
5095  * @cfg {Boolean} CellSelection (true|false) default false
5096  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5097  
5098  * 
5099  * @constructor
5100  * Create a new Table
5101  * @param {Object} config The config object
5102  */
5103
5104 Roo.bootstrap.Table = function(config){
5105     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5106     
5107     if (this.sm) {
5108         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5109         this.sm = this.selModel;
5110         this.sm.xmodule = this.xmodule || false;
5111     }
5112     if (this.cm && typeof(this.cm.config) == 'undefined') {
5113         this.colModel = new Roo.grid.ColumnModel(this.cm);
5114         this.cm = this.colModel;
5115         this.cm.xmodule = this.xmodule || false;
5116     }
5117     if (this.store) {
5118         this.store= Roo.factory(this.store, Roo.data);
5119         this.ds = this.store;
5120         this.ds.xmodule = this.xmodule || false;
5121          
5122     }
5123     if (this.footer && this.store) {
5124         this.footer.dataSource = this.ds;
5125         this.footer = Roo.factory(this.footer);
5126     }
5127     
5128     /** @private */
5129     this.addEvents({
5130         /**
5131          * @event cellclick
5132          * Fires when a cell is clicked
5133          * @param {Roo.bootstrap.Table} this
5134          * @param {Roo.Element} el
5135          * @param {Number} rowIndex
5136          * @param {Number} columnIndex
5137          * @param {Roo.EventObject} e
5138          */
5139         "cellclick" : true,
5140         /**
5141          * @event celldblclick
5142          * Fires when a cell is double clicked
5143          * @param {Roo.bootstrap.Table} this
5144          * @param {Roo.Element} el
5145          * @param {Number} rowIndex
5146          * @param {Number} columnIndex
5147          * @param {Roo.EventObject} e
5148          */
5149         "celldblclick" : true,
5150         /**
5151          * @event rowclick
5152          * Fires when a row is clicked
5153          * @param {Roo.bootstrap.Table} this
5154          * @param {Roo.Element} el
5155          * @param {Number} rowIndex
5156          * @param {Roo.EventObject} e
5157          */
5158         "rowclick" : true,
5159         /**
5160          * @event rowdblclick
5161          * Fires when a row is double clicked
5162          * @param {Roo.bootstrap.Table} this
5163          * @param {Roo.Element} el
5164          * @param {Number} rowIndex
5165          * @param {Roo.EventObject} e
5166          */
5167         "rowdblclick" : true,
5168         /**
5169          * @event mouseover
5170          * Fires when a mouseover occur
5171          * @param {Roo.bootstrap.Table} this
5172          * @param {Roo.Element} el
5173          * @param {Number} rowIndex
5174          * @param {Number} columnIndex
5175          * @param {Roo.EventObject} e
5176          */
5177         "mouseover" : true,
5178         /**
5179          * @event mouseout
5180          * Fires when a mouseout occur
5181          * @param {Roo.bootstrap.Table} this
5182          * @param {Roo.Element} el
5183          * @param {Number} rowIndex
5184          * @param {Number} columnIndex
5185          * @param {Roo.EventObject} e
5186          */
5187         "mouseout" : true,
5188         /**
5189          * @event rowclass
5190          * Fires when a row is rendered, so you can change add a style to it.
5191          * @param {Roo.bootstrap.Table} this
5192          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5193          */
5194         'rowclass' : true,
5195           /**
5196          * @event rowsrendered
5197          * Fires when all the  rows have been rendered
5198          * @param {Roo.bootstrap.Table} this
5199          */
5200         'rowsrendered' : true
5201         
5202     });
5203 };
5204
5205 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5206     
5207     cls: false,
5208     align: false,
5209     bgcolor: false,
5210     border: false,
5211     cellpadding: false,
5212     cellspacing: false,
5213     frame: false,
5214     rules: false,
5215     sortable: false,
5216     summary: false,
5217     width: false,
5218     striped : false,
5219     bordered: false,
5220     hover:  false,
5221     condensed : false,
5222     responsive : false,
5223     sm : false,
5224     cm : false,
5225     store : false,
5226     loadMask : false,
5227     tfoot : true,
5228     thead : true,
5229     RowSelection : false,
5230     CellSelection : false,
5231     layout : false,
5232     
5233     // Roo.Element - the tbody
5234     mainBody: false, 
5235     
5236     getAutoCreate : function(){
5237         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5238         
5239         cfg = {
5240             tag: 'table',
5241             cls : 'table',
5242             cn : []
5243         }
5244             
5245         if (this.striped) {
5246             cfg.cls += ' table-striped';
5247         }
5248         
5249         if (this.hover) {
5250             cfg.cls += ' table-hover';
5251         }
5252         if (this.bordered) {
5253             cfg.cls += ' table-bordered';
5254         }
5255         if (this.condensed) {
5256             cfg.cls += ' table-condensed';
5257         }
5258         if (this.responsive) {
5259             cfg.cls += ' table-responsive';
5260         }
5261         
5262         if (this.cls) {
5263             cfg.cls+=  ' ' +this.cls;
5264         }
5265         
5266         // this lot should be simplifed...
5267         
5268         if (this.align) {
5269             cfg.align=this.align;
5270         }
5271         if (this.bgcolor) {
5272             cfg.bgcolor=this.bgcolor;
5273         }
5274         if (this.border) {
5275             cfg.border=this.border;
5276         }
5277         if (this.cellpadding) {
5278             cfg.cellpadding=this.cellpadding;
5279         }
5280         if (this.cellspacing) {
5281             cfg.cellspacing=this.cellspacing;
5282         }
5283         if (this.frame) {
5284             cfg.frame=this.frame;
5285         }
5286         if (this.rules) {
5287             cfg.rules=this.rules;
5288         }
5289         if (this.sortable) {
5290             cfg.sortable=this.sortable;
5291         }
5292         if (this.summary) {
5293             cfg.summary=this.summary;
5294         }
5295         if (this.width) {
5296             cfg.width=this.width;
5297         }
5298         if (this.layout) {
5299             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5300         }
5301         
5302         if(this.store || this.cm){
5303             if(this.thead){
5304                 cfg.cn.push(this.renderHeader());
5305             }
5306             
5307             cfg.cn.push(this.renderBody());
5308             
5309             if(this.tfoot){
5310                 cfg.cn.push(this.renderFooter());
5311             }
5312             
5313             cfg.cls+=  ' TableGrid';
5314         }
5315         
5316         return { cn : [ cfg ] };
5317     },
5318     
5319     initEvents : function()
5320     {   
5321         if(!this.store || !this.cm){
5322             return;
5323         }
5324         
5325         //Roo.log('initEvents with ds!!!!');
5326         
5327         this.mainBody = this.el.select('tbody', true).first();
5328         
5329         
5330         var _this = this;
5331         
5332         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5333             e.on('click', _this.sort, _this);
5334         });
5335         
5336         this.el.on("click", this.onClick, this);
5337         this.el.on("dblclick", this.onDblClick, this);
5338         
5339         // why is this done????? = it breaks dialogs??
5340         //this.parent().el.setStyle('position', 'relative');
5341         
5342         
5343         if (this.footer) {
5344             this.footer.parentId = this.id;
5345             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5346         }
5347         
5348         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5349         
5350         this.store.on('load', this.onLoad, this);
5351         this.store.on('beforeload', this.onBeforeLoad, this);
5352         this.store.on('update', this.onUpdate, this);
5353         this.store.on('add', this.onAdd, this);
5354         
5355     },
5356     
5357     onMouseover : function(e, el)
5358     {
5359         var cell = Roo.get(el);
5360         
5361         if(!cell){
5362             return;
5363         }
5364         
5365         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5366             cell = cell.findParent('td', false, true);
5367         }
5368         
5369         var row = cell.findParent('tr', false, true);
5370         var cellIndex = cell.dom.cellIndex;
5371         var rowIndex = row.dom.rowIndex - 1; // start from 0
5372         
5373         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5374         
5375     },
5376     
5377     onMouseout : function(e, el)
5378     {
5379         var cell = Roo.get(el);
5380         
5381         if(!cell){
5382             return;
5383         }
5384         
5385         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5386             cell = cell.findParent('td', false, true);
5387         }
5388         
5389         var row = cell.findParent('tr', false, true);
5390         var cellIndex = cell.dom.cellIndex;
5391         var rowIndex = row.dom.rowIndex - 1; // start from 0
5392         
5393         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5394         
5395     },
5396     
5397     onClick : function(e, el)
5398     {
5399         var cell = Roo.get(el);
5400         
5401         if(!cell || (!this.CellSelection && !this.RowSelection)){
5402             return;
5403         }
5404         
5405         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5406             cell = cell.findParent('td', false, true);
5407         }
5408         
5409         if(!cell || typeof(cell) == 'undefined'){
5410             return;
5411         }
5412         
5413         var row = cell.findParent('tr', false, true);
5414         
5415         if(!row || typeof(row) == 'undefined'){
5416             return;
5417         }
5418         
5419         var cellIndex = cell.dom.cellIndex;
5420         var rowIndex = this.getRowIndex(row);
5421         
5422         if(this.CellSelection){
5423             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5424         }
5425         
5426         if(this.RowSelection){
5427             this.fireEvent('rowclick', this, row, rowIndex, e);
5428         }
5429         
5430         
5431     },
5432     
5433     onDblClick : function(e,el)
5434     {
5435         var cell = Roo.get(el);
5436         
5437         if(!cell || (!this.CellSelection && !this.RowSelection)){
5438             return;
5439         }
5440         
5441         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5442             cell = cell.findParent('td', false, true);
5443         }
5444         
5445         if(!cell || typeof(cell) == 'undefined'){
5446             return;
5447         }
5448         
5449         var row = cell.findParent('tr', false, true);
5450         
5451         if(!row || typeof(row) == 'undefined'){
5452             return;
5453         }
5454         
5455         var cellIndex = cell.dom.cellIndex;
5456         var rowIndex = this.getRowIndex(row);
5457         
5458         if(this.CellSelection){
5459             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5460         }
5461         
5462         if(this.RowSelection){
5463             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5464         }
5465     },
5466     
5467     sort : function(e,el)
5468     {
5469         var col = Roo.get(el);
5470         
5471         if(!col.hasClass('sortable')){
5472             return;
5473         }
5474         
5475         var sort = col.attr('sort');
5476         var dir = 'ASC';
5477         
5478         if(col.hasClass('glyphicon-arrow-up')){
5479             dir = 'DESC';
5480         }
5481         
5482         this.store.sortInfo = {field : sort, direction : dir};
5483         
5484         if (this.footer) {
5485             Roo.log("calling footer first");
5486             this.footer.onClick('first');
5487         } else {
5488         
5489             this.store.load({ params : { start : 0 } });
5490         }
5491     },
5492     
5493     renderHeader : function()
5494     {
5495         var header = {
5496             tag: 'thead',
5497             cn : []
5498         };
5499         
5500         var cm = this.cm;
5501         
5502         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5503             
5504             var config = cm.config[i];
5505                     
5506             var c = {
5507                 tag: 'th',
5508                 style : '',
5509                 html: cm.getColumnHeader(i)
5510             };
5511             
5512             if(typeof(config.tooltip) != 'undefined'){
5513                 c.tooltip = config.tooltip;
5514             }
5515             
5516             if(typeof(config.colspan) != 'undefined'){
5517                 c.colspan = config.colspan;
5518             }
5519             
5520             if(typeof(config.hidden) != 'undefined' && config.hidden){
5521                 c.style += ' display:none;';
5522             }
5523             
5524             if(typeof(config.dataIndex) != 'undefined'){
5525                 c.sort = config.dataIndex;
5526             }
5527             
5528             if(typeof(config.sortable) != 'undefined' && config.sortable){
5529                 c.cls = 'sortable';
5530             }
5531             
5532             if(typeof(config.align) != 'undefined' && config.align.length){
5533                 c.style += ' text-align:' + config.align + ';';
5534             }
5535             
5536             if(typeof(config.width) != 'undefined'){
5537                 c.style += ' width:' + config.width + 'px;';
5538             }
5539             
5540             header.cn.push(c)
5541         }
5542         
5543         return header;
5544     },
5545     
5546     renderBody : function()
5547     {
5548         var body = {
5549             tag: 'tbody',
5550             cn : [
5551                 {
5552                     tag: 'tr',
5553                     cn : [
5554                         {
5555                             tag : 'td',
5556                             colspan :  this.cm.getColumnCount()
5557                         }
5558                     ]
5559                 }
5560             ]
5561         };
5562         
5563         return body;
5564     },
5565     
5566     renderFooter : function()
5567     {
5568         var footer = {
5569             tag: 'tfoot',
5570             cn : [
5571                 {
5572                     tag: 'tr',
5573                     cn : [
5574                         {
5575                             tag : 'td',
5576                             colspan :  this.cm.getColumnCount()
5577                         }
5578                     ]
5579                 }
5580             ]
5581         };
5582         
5583         return footer;
5584     },
5585     
5586     
5587     
5588     onLoad : function()
5589     {
5590         Roo.log('ds onload');
5591         this.clear();
5592         
5593         var _this = this;
5594         var cm = this.cm;
5595         var ds = this.store;
5596         
5597         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5598             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5599             
5600             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5601                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5602             }
5603             
5604             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5605                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5606             }
5607         });
5608         
5609         var tbody =  this.mainBody;
5610               
5611         if(ds.getCount() > 0){
5612             ds.data.each(function(d,rowIndex){
5613                 var row =  this.renderRow(cm, ds, rowIndex);
5614                 
5615                 tbody.createChild(row);
5616                 
5617                 var _this = this;
5618                 
5619                 if(row.cellObjects.length){
5620                     Roo.each(row.cellObjects, function(r){
5621                         _this.renderCellObject(r);
5622                     })
5623                 }
5624                 
5625             }, this);
5626         }
5627         
5628         Roo.each(this.el.select('tbody td', true).elements, function(e){
5629             e.on('mouseover', _this.onMouseover, _this);
5630         });
5631         
5632         Roo.each(this.el.select('tbody td', true).elements, function(e){
5633             e.on('mouseout', _this.onMouseout, _this);
5634         });
5635         this.fireEvent('rowsrendered', this);
5636         //if(this.loadMask){
5637         //    this.maskEl.hide();
5638         //}
5639     },
5640     
5641     
5642     onUpdate : function(ds,record)
5643     {
5644         this.refreshRow(record);
5645     },
5646     
5647     onRemove : function(ds, record, index, isUpdate){
5648         if(isUpdate !== true){
5649             this.fireEvent("beforerowremoved", this, index, record);
5650         }
5651         var bt = this.mainBody.dom;
5652         
5653         var rows = this.el.select('tbody > tr', true).elements;
5654         
5655         if(typeof(rows[index]) != 'undefined'){
5656             bt.removeChild(rows[index].dom);
5657         }
5658         
5659 //        if(bt.rows[index]){
5660 //            bt.removeChild(bt.rows[index]);
5661 //        }
5662         
5663         if(isUpdate !== true){
5664             //this.stripeRows(index);
5665             //this.syncRowHeights(index, index);
5666             //this.layout();
5667             this.fireEvent("rowremoved", this, index, record);
5668         }
5669     },
5670     
5671     onAdd : function(ds, records, rowIndex)
5672     {
5673         //Roo.log('on Add called');
5674         // - note this does not handle multiple adding very well..
5675         var bt = this.mainBody.dom;
5676         for (var i =0 ; i < records.length;i++) {
5677             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5678             //Roo.log(records[i]);
5679             //Roo.log(this.store.getAt(rowIndex+i));
5680             this.insertRow(this.store, rowIndex + i, false);
5681             return;
5682         }
5683         
5684     },
5685     
5686     
5687     refreshRow : function(record){
5688         var ds = this.store, index;
5689         if(typeof record == 'number'){
5690             index = record;
5691             record = ds.getAt(index);
5692         }else{
5693             index = ds.indexOf(record);
5694         }
5695         this.insertRow(ds, index, true);
5696         this.onRemove(ds, record, index+1, true);
5697         //this.syncRowHeights(index, index);
5698         //this.layout();
5699         this.fireEvent("rowupdated", this, index, record);
5700     },
5701     
5702     insertRow : function(dm, rowIndex, isUpdate){
5703         
5704         if(!isUpdate){
5705             this.fireEvent("beforerowsinserted", this, rowIndex);
5706         }
5707             //var s = this.getScrollState();
5708         var row = this.renderRow(this.cm, this.store, rowIndex);
5709         // insert before rowIndex..
5710         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5711         
5712         var _this = this;
5713                 
5714         if(row.cellObjects.length){
5715             Roo.each(row.cellObjects, function(r){
5716                 _this.renderCellObject(r);
5717             })
5718         }
5719             
5720         if(!isUpdate){
5721             this.fireEvent("rowsinserted", this, rowIndex);
5722             //this.syncRowHeights(firstRow, lastRow);
5723             //this.stripeRows(firstRow);
5724             //this.layout();
5725         }
5726         
5727     },
5728     
5729     
5730     getRowDom : function(rowIndex)
5731     {
5732         var rows = this.el.select('tbody > tr', true).elements;
5733         
5734         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
5735         
5736     },
5737     // returns the object tree for a tr..
5738   
5739     
5740     renderRow : function(cm, ds, rowIndex) 
5741     {
5742         
5743         var d = ds.getAt(rowIndex);
5744         
5745         var row = {
5746             tag : 'tr',
5747             cn : []
5748         };
5749             
5750         var cellObjects = [];
5751         
5752         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5753             var config = cm.config[i];
5754             
5755             var renderer = cm.getRenderer(i);
5756             var value = '';
5757             var id = false;
5758             
5759             if(typeof(renderer) !== 'undefined'){
5760                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5761             }
5762             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5763             // and are rendered into the cells after the row is rendered - using the id for the element.
5764             
5765             if(typeof(value) === 'object'){
5766                 id = Roo.id();
5767                 cellObjects.push({
5768                     container : id,
5769                     cfg : value 
5770                 })
5771             }
5772             
5773             var rowcfg = {
5774                 record: d,
5775                 rowIndex : rowIndex,
5776                 colIndex : i,
5777                 rowClass : ''
5778             }
5779
5780             this.fireEvent('rowclass', this, rowcfg);
5781             
5782             var td = {
5783                 tag: 'td',
5784                 cls : rowcfg.rowClass,
5785                 style: '',
5786                 html: (typeof(value) === 'object') ? '' : value
5787             };
5788             
5789             if (id) {
5790                 td.id = id;
5791             }
5792             
5793             if(typeof(config.colspan) != 'undefined'){
5794                 td.colspan = config.colspan;
5795             }
5796             
5797             if(typeof(config.hidden) != 'undefined' && config.hidden){
5798                 td.style += ' display:none;';
5799             }
5800             
5801             if(typeof(config.align) != 'undefined' && config.align.length){
5802                 td.style += ' text-align:' + config.align + ';';
5803             }
5804             
5805             if(typeof(config.width) != 'undefined'){
5806                 td.style += ' width:' +  config.width + 'px;';
5807             }
5808             
5809             if(typeof(config.cursor) != 'undefined'){
5810                 td.style += ' cursor:' +  config.cursor + ';';
5811             }
5812              
5813             row.cn.push(td);
5814            
5815         }
5816         
5817         row.cellObjects = cellObjects;
5818         
5819         return row;
5820           
5821     },
5822     
5823     
5824     
5825     onBeforeLoad : function()
5826     {
5827         //Roo.log('ds onBeforeLoad');
5828         
5829         //this.clear();
5830         
5831         //if(this.loadMask){
5832         //    this.maskEl.show();
5833         //}
5834     },
5835      /**
5836      * Remove all rows
5837      */
5838     clear : function()
5839     {
5840         this.el.select('tbody', true).first().dom.innerHTML = '';
5841     },
5842     /**
5843      * Show or hide a row.
5844      * @param {Number} rowIndex to show or hide
5845      * @param {Boolean} state hide
5846      */
5847     setRowVisibility : function(rowIndex, state)
5848     {
5849         var bt = this.mainBody.dom;
5850         
5851         var rows = this.el.select('tbody > tr', true).elements;
5852         
5853         if(typeof(rows[rowIndex]) == 'undefined'){
5854             return;
5855         }
5856         rows[rowIndex].dom.style.display = state ? '' : 'none';
5857     },
5858     
5859     
5860     getSelectionModel : function(){
5861         if(!this.selModel){
5862             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5863         }
5864         return this.selModel;
5865     },
5866     /*
5867      * Render the Roo.bootstrap object from renderder
5868      */
5869     renderCellObject : function(r)
5870     {
5871         var _this = this;
5872         
5873         var t = r.cfg.render(r.container);
5874         
5875         if(r.cfg.cn){
5876             Roo.each(r.cfg.cn, function(c){
5877                 var child = {
5878                     container: t.getChildContainer(),
5879                     cfg: c
5880                 }
5881                 _this.renderCellObject(child);
5882             })
5883         }
5884     },
5885     
5886     getRowIndex : function(row)
5887     {
5888         var rowIndex = -1;
5889         
5890         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
5891             if(el != row){
5892                 return;
5893             }
5894             
5895             rowIndex = index;
5896         });
5897         
5898         return rowIndex;
5899     }
5900    
5901 });
5902
5903  
5904
5905  /*
5906  * - LGPL
5907  *
5908  * table cell
5909  * 
5910  */
5911
5912 /**
5913  * @class Roo.bootstrap.TableCell
5914  * @extends Roo.bootstrap.Component
5915  * Bootstrap TableCell class
5916  * @cfg {String} html cell contain text
5917  * @cfg {String} cls cell class
5918  * @cfg {String} tag cell tag (td|th) default td
5919  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5920  * @cfg {String} align Aligns the content in a cell
5921  * @cfg {String} axis Categorizes cells
5922  * @cfg {String} bgcolor Specifies the background color of a cell
5923  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5924  * @cfg {Number} colspan Specifies the number of columns a cell should span
5925  * @cfg {String} headers Specifies one or more header cells a cell is related to
5926  * @cfg {Number} height Sets the height of a cell
5927  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5928  * @cfg {Number} rowspan Sets the number of rows a cell should span
5929  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5930  * @cfg {String} valign Vertical aligns the content in a cell
5931  * @cfg {Number} width Specifies the width of a cell
5932  * 
5933  * @constructor
5934  * Create a new TableCell
5935  * @param {Object} config The config object
5936  */
5937
5938 Roo.bootstrap.TableCell = function(config){
5939     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5940 };
5941
5942 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5943     
5944     html: false,
5945     cls: false,
5946     tag: false,
5947     abbr: false,
5948     align: false,
5949     axis: false,
5950     bgcolor: false,
5951     charoff: false,
5952     colspan: false,
5953     headers: false,
5954     height: false,
5955     nowrap: false,
5956     rowspan: false,
5957     scope: false,
5958     valign: false,
5959     width: false,
5960     
5961     
5962     getAutoCreate : function(){
5963         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5964         
5965         cfg = {
5966             tag: 'td'
5967         }
5968         
5969         if(this.tag){
5970             cfg.tag = this.tag;
5971         }
5972         
5973         if (this.html) {
5974             cfg.html=this.html
5975         }
5976         if (this.cls) {
5977             cfg.cls=this.cls
5978         }
5979         if (this.abbr) {
5980             cfg.abbr=this.abbr
5981         }
5982         if (this.align) {
5983             cfg.align=this.align
5984         }
5985         if (this.axis) {
5986             cfg.axis=this.axis
5987         }
5988         if (this.bgcolor) {
5989             cfg.bgcolor=this.bgcolor
5990         }
5991         if (this.charoff) {
5992             cfg.charoff=this.charoff
5993         }
5994         if (this.colspan) {
5995             cfg.colspan=this.colspan
5996         }
5997         if (this.headers) {
5998             cfg.headers=this.headers
5999         }
6000         if (this.height) {
6001             cfg.height=this.height
6002         }
6003         if (this.nowrap) {
6004             cfg.nowrap=this.nowrap
6005         }
6006         if (this.rowspan) {
6007             cfg.rowspan=this.rowspan
6008         }
6009         if (this.scope) {
6010             cfg.scope=this.scope
6011         }
6012         if (this.valign) {
6013             cfg.valign=this.valign
6014         }
6015         if (this.width) {
6016             cfg.width=this.width
6017         }
6018         
6019         
6020         return cfg;
6021     }
6022    
6023 });
6024
6025  
6026
6027  /*
6028  * - LGPL
6029  *
6030  * table row
6031  * 
6032  */
6033
6034 /**
6035  * @class Roo.bootstrap.TableRow
6036  * @extends Roo.bootstrap.Component
6037  * Bootstrap TableRow class
6038  * @cfg {String} cls row class
6039  * @cfg {String} align Aligns the content in a table row
6040  * @cfg {String} bgcolor Specifies a background color for a table row
6041  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6042  * @cfg {String} valign Vertical aligns the content in a table row
6043  * 
6044  * @constructor
6045  * Create a new TableRow
6046  * @param {Object} config The config object
6047  */
6048
6049 Roo.bootstrap.TableRow = function(config){
6050     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6051 };
6052
6053 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6054     
6055     cls: false,
6056     align: false,
6057     bgcolor: false,
6058     charoff: false,
6059     valign: false,
6060     
6061     getAutoCreate : function(){
6062         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6063         
6064         cfg = {
6065             tag: 'tr'
6066         }
6067             
6068         if(this.cls){
6069             cfg.cls = this.cls;
6070         }
6071         if(this.align){
6072             cfg.align = this.align;
6073         }
6074         if(this.bgcolor){
6075             cfg.bgcolor = this.bgcolor;
6076         }
6077         if(this.charoff){
6078             cfg.charoff = this.charoff;
6079         }
6080         if(this.valign){
6081             cfg.valign = this.valign;
6082         }
6083         
6084         return cfg;
6085     }
6086    
6087 });
6088
6089  
6090
6091  /*
6092  * - LGPL
6093  *
6094  * table body
6095  * 
6096  */
6097
6098 /**
6099  * @class Roo.bootstrap.TableBody
6100  * @extends Roo.bootstrap.Component
6101  * Bootstrap TableBody class
6102  * @cfg {String} cls element class
6103  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6104  * @cfg {String} align Aligns the content inside the element
6105  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6106  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6107  * 
6108  * @constructor
6109  * Create a new TableBody
6110  * @param {Object} config The config object
6111  */
6112
6113 Roo.bootstrap.TableBody = function(config){
6114     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6115 };
6116
6117 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6118     
6119     cls: false,
6120     tag: false,
6121     align: false,
6122     charoff: false,
6123     valign: false,
6124     
6125     getAutoCreate : function(){
6126         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6127         
6128         cfg = {
6129             tag: 'tbody'
6130         }
6131             
6132         if (this.cls) {
6133             cfg.cls=this.cls
6134         }
6135         if(this.tag){
6136             cfg.tag = this.tag;
6137         }
6138         
6139         if(this.align){
6140             cfg.align = this.align;
6141         }
6142         if(this.charoff){
6143             cfg.charoff = this.charoff;
6144         }
6145         if(this.valign){
6146             cfg.valign = this.valign;
6147         }
6148         
6149         return cfg;
6150     }
6151     
6152     
6153 //    initEvents : function()
6154 //    {
6155 //        
6156 //        if(!this.store){
6157 //            return;
6158 //        }
6159 //        
6160 //        this.store = Roo.factory(this.store, Roo.data);
6161 //        this.store.on('load', this.onLoad, this);
6162 //        
6163 //        this.store.load();
6164 //        
6165 //    },
6166 //    
6167 //    onLoad: function () 
6168 //    {   
6169 //        this.fireEvent('load', this);
6170 //    }
6171 //    
6172 //   
6173 });
6174
6175  
6176
6177  /*
6178  * Based on:
6179  * Ext JS Library 1.1.1
6180  * Copyright(c) 2006-2007, Ext JS, LLC.
6181  *
6182  * Originally Released Under LGPL - original licence link has changed is not relivant.
6183  *
6184  * Fork - LGPL
6185  * <script type="text/javascript">
6186  */
6187
6188 // as we use this in bootstrap.
6189 Roo.namespace('Roo.form');
6190  /**
6191  * @class Roo.form.Action
6192  * Internal Class used to handle form actions
6193  * @constructor
6194  * @param {Roo.form.BasicForm} el The form element or its id
6195  * @param {Object} config Configuration options
6196  */
6197
6198  
6199  
6200 // define the action interface
6201 Roo.form.Action = function(form, options){
6202     this.form = form;
6203     this.options = options || {};
6204 };
6205 /**
6206  * Client Validation Failed
6207  * @const 
6208  */
6209 Roo.form.Action.CLIENT_INVALID = 'client';
6210 /**
6211  * Server Validation Failed
6212  * @const 
6213  */
6214 Roo.form.Action.SERVER_INVALID = 'server';
6215  /**
6216  * Connect to Server Failed
6217  * @const 
6218  */
6219 Roo.form.Action.CONNECT_FAILURE = 'connect';
6220 /**
6221  * Reading Data from Server Failed
6222  * @const 
6223  */
6224 Roo.form.Action.LOAD_FAILURE = 'load';
6225
6226 Roo.form.Action.prototype = {
6227     type : 'default',
6228     failureType : undefined,
6229     response : undefined,
6230     result : undefined,
6231
6232     // interface method
6233     run : function(options){
6234
6235     },
6236
6237     // interface method
6238     success : function(response){
6239
6240     },
6241
6242     // interface method
6243     handleResponse : function(response){
6244
6245     },
6246
6247     // default connection failure
6248     failure : function(response){
6249         
6250         this.response = response;
6251         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6252         this.form.afterAction(this, false);
6253     },
6254
6255     processResponse : function(response){
6256         this.response = response;
6257         if(!response.responseText){
6258             return true;
6259         }
6260         this.result = this.handleResponse(response);
6261         return this.result;
6262     },
6263
6264     // utility functions used internally
6265     getUrl : function(appendParams){
6266         var url = this.options.url || this.form.url || this.form.el.dom.action;
6267         if(appendParams){
6268             var p = this.getParams();
6269             if(p){
6270                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6271             }
6272         }
6273         return url;
6274     },
6275
6276     getMethod : function(){
6277         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6278     },
6279
6280     getParams : function(){
6281         var bp = this.form.baseParams;
6282         var p = this.options.params;
6283         if(p){
6284             if(typeof p == "object"){
6285                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6286             }else if(typeof p == 'string' && bp){
6287                 p += '&' + Roo.urlEncode(bp);
6288             }
6289         }else if(bp){
6290             p = Roo.urlEncode(bp);
6291         }
6292         return p;
6293     },
6294
6295     createCallback : function(){
6296         return {
6297             success: this.success,
6298             failure: this.failure,
6299             scope: this,
6300             timeout: (this.form.timeout*1000),
6301             upload: this.form.fileUpload ? this.success : undefined
6302         };
6303     }
6304 };
6305
6306 Roo.form.Action.Submit = function(form, options){
6307     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6308 };
6309
6310 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6311     type : 'submit',
6312
6313     haveProgress : false,
6314     uploadComplete : false,
6315     
6316     // uploadProgress indicator.
6317     uploadProgress : function()
6318     {
6319         if (!this.form.progressUrl) {
6320             return;
6321         }
6322         
6323         if (!this.haveProgress) {
6324             Roo.MessageBox.progress("Uploading", "Uploading");
6325         }
6326         if (this.uploadComplete) {
6327            Roo.MessageBox.hide();
6328            return;
6329         }
6330         
6331         this.haveProgress = true;
6332    
6333         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6334         
6335         var c = new Roo.data.Connection();
6336         c.request({
6337             url : this.form.progressUrl,
6338             params: {
6339                 id : uid
6340             },
6341             method: 'GET',
6342             success : function(req){
6343                //console.log(data);
6344                 var rdata = false;
6345                 var edata;
6346                 try  {
6347                    rdata = Roo.decode(req.responseText)
6348                 } catch (e) {
6349                     Roo.log("Invalid data from server..");
6350                     Roo.log(edata);
6351                     return;
6352                 }
6353                 if (!rdata || !rdata.success) {
6354                     Roo.log(rdata);
6355                     Roo.MessageBox.alert(Roo.encode(rdata));
6356                     return;
6357                 }
6358                 var data = rdata.data;
6359                 
6360                 if (this.uploadComplete) {
6361                    Roo.MessageBox.hide();
6362                    return;
6363                 }
6364                    
6365                 if (data){
6366                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6367                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6368                     );
6369                 }
6370                 this.uploadProgress.defer(2000,this);
6371             },
6372        
6373             failure: function(data) {
6374                 Roo.log('progress url failed ');
6375                 Roo.log(data);
6376             },
6377             scope : this
6378         });
6379            
6380     },
6381     
6382     
6383     run : function()
6384     {
6385         // run get Values on the form, so it syncs any secondary forms.
6386         this.form.getValues();
6387         
6388         var o = this.options;
6389         var method = this.getMethod();
6390         var isPost = method == 'POST';
6391         if(o.clientValidation === false || this.form.isValid()){
6392             
6393             if (this.form.progressUrl) {
6394                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6395                     (new Date() * 1) + '' + Math.random());
6396                     
6397             } 
6398             
6399             
6400             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6401                 form:this.form.el.dom,
6402                 url:this.getUrl(!isPost),
6403                 method: method,
6404                 params:isPost ? this.getParams() : null,
6405                 isUpload: this.form.fileUpload
6406             }));
6407             
6408             this.uploadProgress();
6409
6410         }else if (o.clientValidation !== false){ // client validation failed
6411             this.failureType = Roo.form.Action.CLIENT_INVALID;
6412             this.form.afterAction(this, false);
6413         }
6414     },
6415
6416     success : function(response)
6417     {
6418         this.uploadComplete= true;
6419         if (this.haveProgress) {
6420             Roo.MessageBox.hide();
6421         }
6422         
6423         
6424         var result = this.processResponse(response);
6425         if(result === true || result.success){
6426             this.form.afterAction(this, true);
6427             return;
6428         }
6429         if(result.errors){
6430             this.form.markInvalid(result.errors);
6431             this.failureType = Roo.form.Action.SERVER_INVALID;
6432         }
6433         this.form.afterAction(this, false);
6434     },
6435     failure : function(response)
6436     {
6437         this.uploadComplete= true;
6438         if (this.haveProgress) {
6439             Roo.MessageBox.hide();
6440         }
6441         
6442         this.response = response;
6443         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6444         this.form.afterAction(this, false);
6445     },
6446     
6447     handleResponse : function(response){
6448         if(this.form.errorReader){
6449             var rs = this.form.errorReader.read(response);
6450             var errors = [];
6451             if(rs.records){
6452                 for(var i = 0, len = rs.records.length; i < len; i++) {
6453                     var r = rs.records[i];
6454                     errors[i] = r.data;
6455                 }
6456             }
6457             if(errors.length < 1){
6458                 errors = null;
6459             }
6460             return {
6461                 success : rs.success,
6462                 errors : errors
6463             };
6464         }
6465         var ret = false;
6466         try {
6467             ret = Roo.decode(response.responseText);
6468         } catch (e) {
6469             ret = {
6470                 success: false,
6471                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6472                 errors : []
6473             };
6474         }
6475         return ret;
6476         
6477     }
6478 });
6479
6480
6481 Roo.form.Action.Load = function(form, options){
6482     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6483     this.reader = this.form.reader;
6484 };
6485
6486 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6487     type : 'load',
6488
6489     run : function(){
6490         
6491         Roo.Ajax.request(Roo.apply(
6492                 this.createCallback(), {
6493                     method:this.getMethod(),
6494                     url:this.getUrl(false),
6495                     params:this.getParams()
6496         }));
6497     },
6498
6499     success : function(response){
6500         
6501         var result = this.processResponse(response);
6502         if(result === true || !result.success || !result.data){
6503             this.failureType = Roo.form.Action.LOAD_FAILURE;
6504             this.form.afterAction(this, false);
6505             return;
6506         }
6507         this.form.clearInvalid();
6508         this.form.setValues(result.data);
6509         this.form.afterAction(this, true);
6510     },
6511
6512     handleResponse : function(response){
6513         if(this.form.reader){
6514             var rs = this.form.reader.read(response);
6515             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6516             return {
6517                 success : rs.success,
6518                 data : data
6519             };
6520         }
6521         return Roo.decode(response.responseText);
6522     }
6523 });
6524
6525 Roo.form.Action.ACTION_TYPES = {
6526     'load' : Roo.form.Action.Load,
6527     'submit' : Roo.form.Action.Submit
6528 };/*
6529  * - LGPL
6530  *
6531  * form
6532  * 
6533  */
6534
6535 /**
6536  * @class Roo.bootstrap.Form
6537  * @extends Roo.bootstrap.Component
6538  * Bootstrap Form class
6539  * @cfg {String} method  GET | POST (default POST)
6540  * @cfg {String} labelAlign top | left (default top)
6541  * @cfg {String} align left  | right - for navbars
6542  * @cfg {Boolean} loadMask load mask when submit (default true)
6543
6544  * 
6545  * @constructor
6546  * Create a new Form
6547  * @param {Object} config The config object
6548  */
6549
6550
6551 Roo.bootstrap.Form = function(config){
6552     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6553     this.addEvents({
6554         /**
6555          * @event clientvalidation
6556          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6557          * @param {Form} this
6558          * @param {Boolean} valid true if the form has passed client-side validation
6559          */
6560         clientvalidation: true,
6561         /**
6562          * @event beforeaction
6563          * Fires before any action is performed. Return false to cancel the action.
6564          * @param {Form} this
6565          * @param {Action} action The action to be performed
6566          */
6567         beforeaction: true,
6568         /**
6569          * @event actionfailed
6570          * Fires when an action fails.
6571          * @param {Form} this
6572          * @param {Action} action The action that failed
6573          */
6574         actionfailed : true,
6575         /**
6576          * @event actioncomplete
6577          * Fires when an action is completed.
6578          * @param {Form} this
6579          * @param {Action} action The action that completed
6580          */
6581         actioncomplete : true
6582     });
6583     
6584 };
6585
6586 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6587       
6588      /**
6589      * @cfg {String} method
6590      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6591      */
6592     method : 'POST',
6593     /**
6594      * @cfg {String} url
6595      * The URL to use for form actions if one isn't supplied in the action options.
6596      */
6597     /**
6598      * @cfg {Boolean} fileUpload
6599      * Set to true if this form is a file upload.
6600      */
6601      
6602     /**
6603      * @cfg {Object} baseParams
6604      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6605      */
6606       
6607     /**
6608      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6609      */
6610     timeout: 30,
6611     /**
6612      * @cfg {Sting} align (left|right) for navbar forms
6613      */
6614     align : 'left',
6615
6616     // private
6617     activeAction : null,
6618  
6619     /**
6620      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6621      * element by passing it or its id or mask the form itself by passing in true.
6622      * @type Mixed
6623      */
6624     waitMsgTarget : false,
6625     
6626     loadMask : true,
6627     
6628     getAutoCreate : function(){
6629         
6630         var cfg = {
6631             tag: 'form',
6632             method : this.method || 'POST',
6633             id : this.id || Roo.id(),
6634             cls : ''
6635         }
6636         if (this.parent().xtype.match(/^Nav/)) {
6637             cfg.cls = 'navbar-form navbar-' + this.align;
6638             
6639         }
6640         
6641         if (this.labelAlign == 'left' ) {
6642             cfg.cls += ' form-horizontal';
6643         }
6644         
6645         
6646         return cfg;
6647     },
6648     initEvents : function()
6649     {
6650         this.el.on('submit', this.onSubmit, this);
6651         // this was added as random key presses on the form where triggering form submit.
6652         this.el.on('keypress', function(e) {
6653             if (e.getCharCode() != 13) {
6654                 return true;
6655             }
6656             // we might need to allow it for textareas.. and some other items.
6657             // check e.getTarget().
6658             
6659             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6660                 return true;
6661             }
6662         
6663             Roo.log("keypress blocked");
6664             
6665             e.preventDefault();
6666             return false;
6667         });
6668         
6669     },
6670     // private
6671     onSubmit : function(e){
6672         e.stopEvent();
6673     },
6674     
6675      /**
6676      * Returns true if client-side validation on the form is successful.
6677      * @return Boolean
6678      */
6679     isValid : function(){
6680         var items = this.getItems();
6681         var valid = true;
6682         items.each(function(f){
6683            if(!f.validate()){
6684                valid = false;
6685                
6686            }
6687         });
6688         return valid;
6689     },
6690     /**
6691      * Returns true if any fields in this form have changed since their original load.
6692      * @return Boolean
6693      */
6694     isDirty : function(){
6695         var dirty = false;
6696         var items = this.getItems();
6697         items.each(function(f){
6698            if(f.isDirty()){
6699                dirty = true;
6700                return false;
6701            }
6702            return true;
6703         });
6704         return dirty;
6705     },
6706      /**
6707      * Performs a predefined action (submit or load) or custom actions you define on this form.
6708      * @param {String} actionName The name of the action type
6709      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6710      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6711      * accept other config options):
6712      * <pre>
6713 Property          Type             Description
6714 ----------------  ---------------  ----------------------------------------------------------------------------------
6715 url               String           The url for the action (defaults to the form's url)
6716 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6717 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6718 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6719                                    validate the form on the client (defaults to false)
6720      * </pre>
6721      * @return {BasicForm} this
6722      */
6723     doAction : function(action, options){
6724         if(typeof action == 'string'){
6725             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6726         }
6727         if(this.fireEvent('beforeaction', this, action) !== false){
6728             this.beforeAction(action);
6729             action.run.defer(100, action);
6730         }
6731         return this;
6732     },
6733     
6734     // private
6735     beforeAction : function(action){
6736         var o = action.options;
6737         
6738         if(this.loadMask){
6739             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6740         }
6741         // not really supported yet.. ??
6742         
6743         //if(this.waitMsgTarget === true){
6744         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6745         //}else if(this.waitMsgTarget){
6746         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6747         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6748         //}else {
6749         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6750        // }
6751          
6752     },
6753
6754     // private
6755     afterAction : function(action, success){
6756         this.activeAction = null;
6757         var o = action.options;
6758         
6759         //if(this.waitMsgTarget === true){
6760             this.el.unmask();
6761         //}else if(this.waitMsgTarget){
6762         //    this.waitMsgTarget.unmask();
6763         //}else{
6764         //    Roo.MessageBox.updateProgress(1);
6765         //    Roo.MessageBox.hide();
6766        // }
6767         // 
6768         if(success){
6769             if(o.reset){
6770                 this.reset();
6771             }
6772             Roo.callback(o.success, o.scope, [this, action]);
6773             this.fireEvent('actioncomplete', this, action);
6774             
6775         }else{
6776             
6777             // failure condition..
6778             // we have a scenario where updates need confirming.
6779             // eg. if a locking scenario exists..
6780             // we look for { errors : { needs_confirm : true }} in the response.
6781             if (
6782                 (typeof(action.result) != 'undefined')  &&
6783                 (typeof(action.result.errors) != 'undefined')  &&
6784                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6785            ){
6786                 var _t = this;
6787                 Roo.log("not supported yet");
6788                  /*
6789                 
6790                 Roo.MessageBox.confirm(
6791                     "Change requires confirmation",
6792                     action.result.errorMsg,
6793                     function(r) {
6794                         if (r != 'yes') {
6795                             return;
6796                         }
6797                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6798                     }
6799                     
6800                 );
6801                 */
6802                 
6803                 
6804                 return;
6805             }
6806             
6807             Roo.callback(o.failure, o.scope, [this, action]);
6808             // show an error message if no failed handler is set..
6809             if (!this.hasListener('actionfailed')) {
6810                 Roo.log("need to add dialog support");
6811                 /*
6812                 Roo.MessageBox.alert("Error",
6813                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6814                         action.result.errorMsg :
6815                         "Saving Failed, please check your entries or try again"
6816                 );
6817                 */
6818             }
6819             
6820             this.fireEvent('actionfailed', this, action);
6821         }
6822         
6823     },
6824     /**
6825      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6826      * @param {String} id The value to search for
6827      * @return Field
6828      */
6829     findField : function(id){
6830         var items = this.getItems();
6831         var field = items.get(id);
6832         if(!field){
6833              items.each(function(f){
6834                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6835                     field = f;
6836                     return false;
6837                 }
6838                 return true;
6839             });
6840         }
6841         return field || null;
6842     },
6843      /**
6844      * Mark fields in this form invalid in bulk.
6845      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6846      * @return {BasicForm} this
6847      */
6848     markInvalid : function(errors){
6849         if(errors instanceof Array){
6850             for(var i = 0, len = errors.length; i < len; i++){
6851                 var fieldError = errors[i];
6852                 var f = this.findField(fieldError.id);
6853                 if(f){
6854                     f.markInvalid(fieldError.msg);
6855                 }
6856             }
6857         }else{
6858             var field, id;
6859             for(id in errors){
6860                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6861                     field.markInvalid(errors[id]);
6862                 }
6863             }
6864         }
6865         //Roo.each(this.childForms || [], function (f) {
6866         //    f.markInvalid(errors);
6867         //});
6868         
6869         return this;
6870     },
6871
6872     /**
6873      * Set values for fields in this form in bulk.
6874      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6875      * @return {BasicForm} this
6876      */
6877     setValues : function(values){
6878         if(values instanceof Array){ // array of objects
6879             for(var i = 0, len = values.length; i < len; i++){
6880                 var v = values[i];
6881                 var f = this.findField(v.id);
6882                 if(f){
6883                     f.setValue(v.value);
6884                     if(this.trackResetOnLoad){
6885                         f.originalValue = f.getValue();
6886                     }
6887                 }
6888             }
6889         }else{ // object hash
6890             var field, id;
6891             for(id in values){
6892                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6893                     
6894                     if (field.setFromData && 
6895                         field.valueField && 
6896                         field.displayField &&
6897                         // combos' with local stores can 
6898                         // be queried via setValue()
6899                         // to set their value..
6900                         (field.store && !field.store.isLocal)
6901                         ) {
6902                         // it's a combo
6903                         var sd = { };
6904                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6905                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6906                         field.setFromData(sd);
6907                         
6908                     } else {
6909                         field.setValue(values[id]);
6910                     }
6911                     
6912                     
6913                     if(this.trackResetOnLoad){
6914                         field.originalValue = field.getValue();
6915                     }
6916                 }
6917             }
6918         }
6919          
6920         //Roo.each(this.childForms || [], function (f) {
6921         //    f.setValues(values);
6922         //});
6923                 
6924         return this;
6925     },
6926
6927     /**
6928      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6929      * they are returned as an array.
6930      * @param {Boolean} asString
6931      * @return {Object}
6932      */
6933     getValues : function(asString){
6934         //if (this.childForms) {
6935             // copy values from the child forms
6936         //    Roo.each(this.childForms, function (f) {
6937         //        this.setValues(f.getValues());
6938         //    }, this);
6939         //}
6940         
6941         
6942         
6943         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6944         if(asString === true){
6945             return fs;
6946         }
6947         return Roo.urlDecode(fs);
6948     },
6949     
6950     /**
6951      * Returns the fields in this form as an object with key/value pairs. 
6952      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6953      * @return {Object}
6954      */
6955     getFieldValues : function(with_hidden)
6956     {
6957         var items = this.getItems();
6958         var ret = {};
6959         items.each(function(f){
6960             if (!f.getName()) {
6961                 return;
6962             }
6963             var v = f.getValue();
6964             if (f.inputType =='radio') {
6965                 if (typeof(ret[f.getName()]) == 'undefined') {
6966                     ret[f.getName()] = ''; // empty..
6967                 }
6968                 
6969                 if (!f.el.dom.checked) {
6970                     return;
6971                     
6972                 }
6973                 v = f.el.dom.value;
6974                 
6975             }
6976             
6977             // not sure if this supported any more..
6978             if ((typeof(v) == 'object') && f.getRawValue) {
6979                 v = f.getRawValue() ; // dates..
6980             }
6981             // combo boxes where name != hiddenName...
6982             if (f.name != f.getName()) {
6983                 ret[f.name] = f.getRawValue();
6984             }
6985             ret[f.getName()] = v;
6986         });
6987         
6988         return ret;
6989     },
6990
6991     /**
6992      * Clears all invalid messages in this form.
6993      * @return {BasicForm} this
6994      */
6995     clearInvalid : function(){
6996         var items = this.getItems();
6997         
6998         items.each(function(f){
6999            f.clearInvalid();
7000         });
7001         
7002         
7003         
7004         return this;
7005     },
7006
7007     /**
7008      * Resets this form.
7009      * @return {BasicForm} this
7010      */
7011     reset : function(){
7012         var items = this.getItems();
7013         items.each(function(f){
7014             f.reset();
7015         });
7016         
7017         Roo.each(this.childForms || [], function (f) {
7018             f.reset();
7019         });
7020        
7021         
7022         return this;
7023     },
7024     getItems : function()
7025     {
7026         var r=new Roo.util.MixedCollection(false, function(o){
7027             return o.id || (o.id = Roo.id());
7028         });
7029         var iter = function(el) {
7030             if (el.inputEl) {
7031                 r.add(el);
7032             }
7033             if (!el.items) {
7034                 return;
7035             }
7036             Roo.each(el.items,function(e) {
7037                 iter(e);
7038             });
7039             
7040             
7041         };
7042         
7043         iter(this);
7044         return r;
7045         
7046         
7047         
7048         
7049     }
7050     
7051 });
7052
7053  
7054 /*
7055  * Based on:
7056  * Ext JS Library 1.1.1
7057  * Copyright(c) 2006-2007, Ext JS, LLC.
7058  *
7059  * Originally Released Under LGPL - original licence link has changed is not relivant.
7060  *
7061  * Fork - LGPL
7062  * <script type="text/javascript">
7063  */
7064 /**
7065  * @class Roo.form.VTypes
7066  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7067  * @singleton
7068  */
7069 Roo.form.VTypes = function(){
7070     // closure these in so they are only created once.
7071     var alpha = /^[a-zA-Z_]+$/;
7072     var alphanum = /^[a-zA-Z0-9_]+$/;
7073     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7074     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7075
7076     // All these messages and functions are configurable
7077     return {
7078         /**
7079          * The function used to validate email addresses
7080          * @param {String} value The email address
7081          */
7082         'email' : function(v){
7083             return email.test(v);
7084         },
7085         /**
7086          * The error text to display when the email validation function returns false
7087          * @type String
7088          */
7089         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7090         /**
7091          * The keystroke filter mask to be applied on email input
7092          * @type RegExp
7093          */
7094         'emailMask' : /[a-z0-9_\.\-@]/i,
7095
7096         /**
7097          * The function used to validate URLs
7098          * @param {String} value The URL
7099          */
7100         'url' : function(v){
7101             return url.test(v);
7102         },
7103         /**
7104          * The error text to display when the url validation function returns false
7105          * @type String
7106          */
7107         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7108         
7109         /**
7110          * The function used to validate alpha values
7111          * @param {String} value The value
7112          */
7113         'alpha' : function(v){
7114             return alpha.test(v);
7115         },
7116         /**
7117          * The error text to display when the alpha validation function returns false
7118          * @type String
7119          */
7120         'alphaText' : 'This field should only contain letters and _',
7121         /**
7122          * The keystroke filter mask to be applied on alpha input
7123          * @type RegExp
7124          */
7125         'alphaMask' : /[a-z_]/i,
7126
7127         /**
7128          * The function used to validate alphanumeric values
7129          * @param {String} value The value
7130          */
7131         'alphanum' : function(v){
7132             return alphanum.test(v);
7133         },
7134         /**
7135          * The error text to display when the alphanumeric validation function returns false
7136          * @type String
7137          */
7138         'alphanumText' : 'This field should only contain letters, numbers and _',
7139         /**
7140          * The keystroke filter mask to be applied on alphanumeric input
7141          * @type RegExp
7142          */
7143         'alphanumMask' : /[a-z0-9_]/i
7144     };
7145 }();/*
7146  * - LGPL
7147  *
7148  * Input
7149  * 
7150  */
7151
7152 /**
7153  * @class Roo.bootstrap.Input
7154  * @extends Roo.bootstrap.Component
7155  * Bootstrap Input class
7156  * @cfg {Boolean} disabled is it disabled
7157  * @cfg {String} fieldLabel - the label associated
7158  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7159  * @cfg {String} name name of the input
7160  * @cfg {string} fieldLabel - the label associated
7161  * @cfg {string}  inputType - input / file submit ...
7162  * @cfg {string} placeholder - placeholder to put in text.
7163  * @cfg {string}  before - input group add on before
7164  * @cfg {string} after - input group add on after
7165  * @cfg {string} size - (lg|sm) or leave empty..
7166  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7167  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7168  * @cfg {Number} md colspan out of 12 for computer-sized screens
7169  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7170  * @cfg {string} value default value of the input
7171  * @cfg {Number} labelWidth set the width of label (0-12)
7172  * @cfg {String} labelAlign (top|left)
7173  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7174  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7175
7176  * @cfg {String} align (left|center|right) Default left
7177  * 
7178  * 
7179  * 
7180  * @constructor
7181  * Create a new Input
7182  * @param {Object} config The config object
7183  */
7184
7185 Roo.bootstrap.Input = function(config){
7186     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7187    
7188         this.addEvents({
7189             /**
7190              * @event focus
7191              * Fires when this field receives input focus.
7192              * @param {Roo.form.Field} this
7193              */
7194             focus : true,
7195             /**
7196              * @event blur
7197              * Fires when this field loses input focus.
7198              * @param {Roo.form.Field} this
7199              */
7200             blur : true,
7201             /**
7202              * @event specialkey
7203              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7204              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7205              * @param {Roo.form.Field} this
7206              * @param {Roo.EventObject} e The event object
7207              */
7208             specialkey : true,
7209             /**
7210              * @event change
7211              * Fires just before the field blurs if the field value has changed.
7212              * @param {Roo.form.Field} this
7213              * @param {Mixed} newValue The new value
7214              * @param {Mixed} oldValue The original value
7215              */
7216             change : true,
7217             /**
7218              * @event invalid
7219              * Fires after the field has been marked as invalid.
7220              * @param {Roo.form.Field} this
7221              * @param {String} msg The validation message
7222              */
7223             invalid : true,
7224             /**
7225              * @event valid
7226              * Fires after the field has been validated with no errors.
7227              * @param {Roo.form.Field} this
7228              */
7229             valid : true,
7230              /**
7231              * @event keyup
7232              * Fires after the key up
7233              * @param {Roo.form.Field} this
7234              * @param {Roo.EventObject}  e The event Object
7235              */
7236             keyup : true
7237         });
7238 };
7239
7240 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7241      /**
7242      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7243       automatic validation (defaults to "keyup").
7244      */
7245     validationEvent : "keyup",
7246      /**
7247      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7248      */
7249     validateOnBlur : true,
7250     /**
7251      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7252      */
7253     validationDelay : 250,
7254      /**
7255      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7256      */
7257     focusClass : "x-form-focus",  // not needed???
7258     
7259        
7260     /**
7261      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7262      */
7263     invalidClass : "has-warning",
7264     
7265     /**
7266      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7267      */
7268     validClass : "has-success",
7269     
7270     /**
7271      * @cfg {Boolean} hasFeedback (true|false) default true
7272      */
7273     hasFeedback : true,
7274     
7275     /**
7276      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7277      */
7278     invalidFeedbackClass : "glyphicon-warning-sign",
7279     
7280     /**
7281      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7282      */
7283     validFeedbackClass : "glyphicon-ok",
7284     
7285     /**
7286      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7287      */
7288     selectOnFocus : false,
7289     
7290      /**
7291      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7292      */
7293     maskRe : null,
7294        /**
7295      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7296      */
7297     vtype : null,
7298     
7299       /**
7300      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7301      */
7302     disableKeyFilter : false,
7303     
7304        /**
7305      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7306      */
7307     disabled : false,
7308      /**
7309      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7310      */
7311     allowBlank : true,
7312     /**
7313      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7314      */
7315     blankText : "This field is required",
7316     
7317      /**
7318      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7319      */
7320     minLength : 0,
7321     /**
7322      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7323      */
7324     maxLength : Number.MAX_VALUE,
7325     /**
7326      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7327      */
7328     minLengthText : "The minimum length for this field is {0}",
7329     /**
7330      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7331      */
7332     maxLengthText : "The maximum length for this field is {0}",
7333   
7334     
7335     /**
7336      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7337      * If available, this function will be called only after the basic validators all return true, and will be passed the
7338      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7339      */
7340     validator : null,
7341     /**
7342      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7343      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7344      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7345      */
7346     regex : null,
7347     /**
7348      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7349      */
7350     regexText : "",
7351     
7352     autocomplete: false,
7353     
7354     
7355     fieldLabel : '',
7356     inputType : 'text',
7357     
7358     name : false,
7359     placeholder: false,
7360     before : false,
7361     after : false,
7362     size : false,
7363     hasFocus : false,
7364     preventMark: false,
7365     isFormField : true,
7366     value : '',
7367     labelWidth : 2,
7368     labelAlign : false,
7369     readOnly : false,
7370     align : false,
7371     formatedValue : false,
7372     
7373     parentLabelAlign : function()
7374     {
7375         var parent = this;
7376         while (parent.parent()) {
7377             parent = parent.parent();
7378             if (typeof(parent.labelAlign) !='undefined') {
7379                 return parent.labelAlign;
7380             }
7381         }
7382         return 'left';
7383         
7384     },
7385     
7386     getAutoCreate : function(){
7387         
7388         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7389         
7390         var id = Roo.id();
7391         
7392         var cfg = {};
7393         
7394         if(this.inputType != 'hidden'){
7395             cfg.cls = 'form-group' //input-group
7396         }
7397         
7398         var input =  {
7399             tag: 'input',
7400             id : id,
7401             type : this.inputType,
7402             value : this.value,
7403             cls : 'form-control',
7404             placeholder : this.placeholder || '',
7405             autocomplete : this.autocomplete || 'new-password'
7406         };
7407         
7408         
7409         if(this.align){
7410             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7411         }
7412         
7413         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7414             input.maxLength = this.maxLength;
7415         }
7416         
7417         if (this.disabled) {
7418             input.disabled=true;
7419         }
7420         
7421         if (this.readOnly) {
7422             input.readonly=true;
7423         }
7424         
7425         if (this.name) {
7426             input.name = this.name;
7427         }
7428         if (this.size) {
7429             input.cls += ' input-' + this.size;
7430         }
7431         var settings=this;
7432         ['xs','sm','md','lg'].map(function(size){
7433             if (settings[size]) {
7434                 cfg.cls += ' col-' + size + '-' + settings[size];
7435             }
7436         });
7437         
7438         var inputblock = input;
7439         
7440         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7441             
7442             var feedback = {
7443                 tag: 'span',
7444                 cls: 'glyphicon form-control-feedback'
7445             };
7446
7447             inputblock = {
7448                 cls : 'has-feedback',
7449                 cn :  [
7450                     input,
7451                     feedback
7452                 ] 
7453             };  
7454         }
7455          
7456 //        var inputblock = input;
7457         
7458         if (this.before || this.after) {
7459             
7460             inputblock = {
7461                 cls : 'input-group',
7462                 cn :  [] 
7463             };
7464             
7465             if (this.before && typeof(this.before) == 'string') {
7466                 
7467                 inputblock.cn.push({
7468                     tag :'span',
7469                     cls : 'roo-input-before input-group-addon',
7470                     html : this.before
7471                 });
7472             }
7473             if (this.before && typeof(this.before) == 'object') {
7474                 this.before = Roo.factory(this.before);
7475                 Roo.log(this.before);
7476                 inputblock.cn.push({
7477                     tag :'span',
7478                     cls : 'roo-input-before input-group-' +
7479                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7480                 });
7481             }
7482             
7483             inputblock.cn.push(input);
7484             
7485             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7486                 inputblock.cls += ' has-feedback';
7487                 inputblock.cn.push(feedback);
7488             }
7489             
7490             if (this.after && typeof(this.after) == 'string') {
7491                 inputblock.cn.push({
7492                     tag :'span',
7493                     cls : 'roo-input-after input-group-addon',
7494                     html : this.after
7495                 });
7496             }
7497             if (this.after && typeof(this.after) == 'object') {
7498                 this.after = Roo.factory(this.after);
7499                 Roo.log(this.after);
7500                 inputblock.cn.push({
7501                     tag :'span',
7502                     cls : 'roo-input-after input-group-' +
7503                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7504                 });
7505             }
7506         };
7507         
7508         if (align ==='left' && this.fieldLabel.length) {
7509                 Roo.log("left and has label");
7510                 cfg.cn = [
7511                     
7512                     {
7513                         tag: 'label',
7514                         'for' :  id,
7515                         cls : 'control-label col-sm-' + this.labelWidth,
7516                         html : this.fieldLabel
7517                         
7518                     },
7519                     {
7520                         cls : "col-sm-" + (12 - this.labelWidth), 
7521                         cn: [
7522                             inputblock
7523                         ]
7524                     }
7525                     
7526                 ];
7527         } else if ( this.fieldLabel.length) {
7528                 Roo.log(" label");
7529                  cfg.cn = [
7530                    
7531                     {
7532                         tag: 'label',
7533                         //cls : 'input-group-addon',
7534                         html : this.fieldLabel
7535                         
7536                     },
7537                     
7538                     inputblock
7539                     
7540                 ];
7541
7542         } else {
7543             
7544                 Roo.log(" no label && no align");
7545                 cfg.cn = [
7546                     
7547                         inputblock
7548                     
7549                 ];
7550                 
7551                 
7552         };
7553         Roo.log('input-parentType: ' + this.parentType);
7554         
7555         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7556            cfg.cls += ' navbar-form';
7557            Roo.log(cfg);
7558         }
7559         
7560         return cfg;
7561         
7562     },
7563     /**
7564      * return the real input element.
7565      */
7566     inputEl: function ()
7567     {
7568         return this.el.select('input.form-control',true).first();
7569     },
7570     
7571     tooltipEl : function()
7572     {
7573         return this.inputEl();
7574     },
7575     
7576     setDisabled : function(v)
7577     {
7578         var i  = this.inputEl().dom;
7579         if (!v) {
7580             i.removeAttribute('disabled');
7581             return;
7582             
7583         }
7584         i.setAttribute('disabled','true');
7585     },
7586     initEvents : function()
7587     {
7588           
7589         this.inputEl().on("keydown" , this.fireKey,  this);
7590         this.inputEl().on("focus", this.onFocus,  this);
7591         this.inputEl().on("blur", this.onBlur,  this);
7592         
7593         this.inputEl().relayEvent('keyup', this);
7594
7595         // reference to original value for reset
7596         this.originalValue = this.getValue();
7597         //Roo.form.TextField.superclass.initEvents.call(this);
7598         if(this.validationEvent == 'keyup'){
7599             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7600             this.inputEl().on('keyup', this.filterValidation, this);
7601         }
7602         else if(this.validationEvent !== false){
7603             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7604         }
7605         
7606         if(this.selectOnFocus){
7607             this.on("focus", this.preFocus, this);
7608             
7609         }
7610         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7611             this.inputEl().on("keypress", this.filterKeys, this);
7612         }
7613        /* if(this.grow){
7614             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7615             this.el.on("click", this.autoSize,  this);
7616         }
7617         */
7618         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7619             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7620         }
7621         
7622         if (typeof(this.before) == 'object') {
7623             this.before.render(this.el.select('.roo-input-before',true).first());
7624         }
7625         if (typeof(this.after) == 'object') {
7626             this.after.render(this.el.select('.roo-input-after',true).first());
7627         }
7628         
7629         
7630     },
7631     filterValidation : function(e){
7632         if(!e.isNavKeyPress()){
7633             this.validationTask.delay(this.validationDelay);
7634         }
7635     },
7636      /**
7637      * Validates the field value
7638      * @return {Boolean} True if the value is valid, else false
7639      */
7640     validate : function(){
7641         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7642         if(this.disabled || this.validateValue(this.getRawValue())){
7643             this.markValid();
7644             return true;
7645         }
7646         
7647         this.markInvalid();
7648         return false;
7649     },
7650     
7651     
7652     /**
7653      * Validates a value according to the field's validation rules and marks the field as invalid
7654      * if the validation fails
7655      * @param {Mixed} value The value to validate
7656      * @return {Boolean} True if the value is valid, else false
7657      */
7658     validateValue : function(value){
7659         if(value.length < 1)  { // if it's blank
7660             if(this.allowBlank){
7661                 return true;
7662             }
7663             return false;
7664         }
7665         
7666         if(value.length < this.minLength){
7667             return false;
7668         }
7669         if(value.length > this.maxLength){
7670             return false;
7671         }
7672         if(this.vtype){
7673             var vt = Roo.form.VTypes;
7674             if(!vt[this.vtype](value, this)){
7675                 return false;
7676             }
7677         }
7678         if(typeof this.validator == "function"){
7679             var msg = this.validator(value);
7680             if(msg !== true){
7681                 return false;
7682             }
7683         }
7684         
7685         if(this.regex && !this.regex.test(value)){
7686             return false;
7687         }
7688         
7689         return true;
7690     },
7691
7692     
7693     
7694      // private
7695     fireKey : function(e){
7696         //Roo.log('field ' + e.getKey());
7697         if(e.isNavKeyPress()){
7698             this.fireEvent("specialkey", this, e);
7699         }
7700     },
7701     focus : function (selectText){
7702         if(this.rendered){
7703             this.inputEl().focus();
7704             if(selectText === true){
7705                 this.inputEl().dom.select();
7706             }
7707         }
7708         return this;
7709     } ,
7710     
7711     onFocus : function(){
7712         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7713            // this.el.addClass(this.focusClass);
7714         }
7715         if(!this.hasFocus){
7716             this.hasFocus = true;
7717             this.startValue = this.getValue();
7718             this.fireEvent("focus", this);
7719         }
7720     },
7721     
7722     beforeBlur : Roo.emptyFn,
7723
7724     
7725     // private
7726     onBlur : function(){
7727         this.beforeBlur();
7728         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7729             //this.el.removeClass(this.focusClass);
7730         }
7731         this.hasFocus = false;
7732         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7733             this.validate();
7734         }
7735         var v = this.getValue();
7736         if(String(v) !== String(this.startValue)){
7737             this.fireEvent('change', this, v, this.startValue);
7738         }
7739         this.fireEvent("blur", this);
7740     },
7741     
7742     /**
7743      * Resets the current field value to the originally loaded value and clears any validation messages
7744      */
7745     reset : function(){
7746         this.setValue(this.originalValue);
7747         this.validate();
7748     },
7749      /**
7750      * Returns the name of the field
7751      * @return {Mixed} name The name field
7752      */
7753     getName: function(){
7754         return this.name;
7755     },
7756      /**
7757      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7758      * @return {Mixed} value The field value
7759      */
7760     getValue : function(){
7761         
7762         var v = this.inputEl().getValue();
7763         
7764         return v;
7765     },
7766     /**
7767      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7768      * @return {Mixed} value The field value
7769      */
7770     getRawValue : function(){
7771         var v = this.inputEl().getValue();
7772         
7773         return v;
7774     },
7775     
7776     /**
7777      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7778      * @param {Mixed} value The value to set
7779      */
7780     setRawValue : function(v){
7781         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7782     },
7783     
7784     selectText : function(start, end){
7785         var v = this.getRawValue();
7786         if(v.length > 0){
7787             start = start === undefined ? 0 : start;
7788             end = end === undefined ? v.length : end;
7789             var d = this.inputEl().dom;
7790             if(d.setSelectionRange){
7791                 d.setSelectionRange(start, end);
7792             }else if(d.createTextRange){
7793                 var range = d.createTextRange();
7794                 range.moveStart("character", start);
7795                 range.moveEnd("character", v.length-end);
7796                 range.select();
7797             }
7798         }
7799     },
7800     
7801     /**
7802      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7803      * @param {Mixed} value The value to set
7804      */
7805     setValue : function(v){
7806         this.value = v;
7807         if(this.rendered){
7808             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7809             this.validate();
7810         }
7811     },
7812     
7813     /*
7814     processValue : function(value){
7815         if(this.stripCharsRe){
7816             var newValue = value.replace(this.stripCharsRe, '');
7817             if(newValue !== value){
7818                 this.setRawValue(newValue);
7819                 return newValue;
7820             }
7821         }
7822         return value;
7823     },
7824   */
7825     preFocus : function(){
7826         
7827         if(this.selectOnFocus){
7828             this.inputEl().dom.select();
7829         }
7830     },
7831     filterKeys : function(e){
7832         var k = e.getKey();
7833         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7834             return;
7835         }
7836         var c = e.getCharCode(), cc = String.fromCharCode(c);
7837         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7838             return;
7839         }
7840         if(!this.maskRe.test(cc)){
7841             e.stopEvent();
7842         }
7843     },
7844      /**
7845      * Clear any invalid styles/messages for this field
7846      */
7847     clearInvalid : function(){
7848         
7849         if(!this.el || this.preventMark){ // not rendered
7850             return;
7851         }
7852         this.el.removeClass(this.invalidClass);
7853         
7854         this.fireEvent('valid', this);
7855     },
7856     
7857      /**
7858      * Mark this field as valid
7859      */
7860     markValid : function(){
7861         if(!this.el  || this.preventMark){ // not rendered
7862             return;
7863         }
7864         
7865         this.el.removeClass([this.invalidClass, this.validClass]);
7866         
7867         if(this.disabled || this.allowBlank){
7868             return;
7869         }
7870         
7871         this.el.addClass(this.validClass);
7872         
7873         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
7874             
7875             var feedback = this.el.select('.form-control-feedback', true).first();
7876             
7877             if(feedback){
7878                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
7879                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
7880             }
7881             
7882         }
7883         
7884         this.fireEvent('valid', this);
7885     },
7886     
7887      /**
7888      * Mark this field as invalid
7889      * @param {String} msg The validation message
7890      */
7891     markInvalid : function(msg){
7892         if(!this.el  || this.preventMark){ // not rendered
7893             return;
7894         }
7895         
7896         this.el.removeClass([this.invalidClass, this.validClass]);
7897         
7898         if(this.disabled || this.allowBlank){
7899             return;
7900         }
7901         
7902         this.el.addClass(this.invalidClass);
7903         
7904         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7905             
7906             var feedback = this.el.select('.form-control-feedback', true).first();
7907             
7908             if(feedback){
7909                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
7910                 
7911                 if(this.getValue().length){
7912                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
7913                 }
7914                 
7915             }
7916             
7917         }
7918         
7919         this.fireEvent('invalid', this, msg);
7920     },
7921     // private
7922     SafariOnKeyDown : function(event)
7923     {
7924         // this is a workaround for a password hang bug on chrome/ webkit.
7925         
7926         var isSelectAll = false;
7927         
7928         if(this.inputEl().dom.selectionEnd > 0){
7929             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7930         }
7931         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7932             event.preventDefault();
7933             this.setValue('');
7934             return;
7935         }
7936         
7937         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
7938             
7939             event.preventDefault();
7940             // this is very hacky as keydown always get's upper case.
7941             //
7942             var cc = String.fromCharCode(event.getCharCode());
7943             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7944             
7945         }
7946     },
7947     adjustWidth : function(tag, w){
7948         tag = tag.toLowerCase();
7949         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7950             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7951                 if(tag == 'input'){
7952                     return w + 2;
7953                 }
7954                 if(tag == 'textarea'){
7955                     return w-2;
7956                 }
7957             }else if(Roo.isOpera){
7958                 if(tag == 'input'){
7959                     return w + 2;
7960                 }
7961                 if(tag == 'textarea'){
7962                     return w-2;
7963                 }
7964             }
7965         }
7966         return w;
7967     }
7968     
7969 });
7970
7971  
7972 /*
7973  * - LGPL
7974  *
7975  * Input
7976  * 
7977  */
7978
7979 /**
7980  * @class Roo.bootstrap.TextArea
7981  * @extends Roo.bootstrap.Input
7982  * Bootstrap TextArea class
7983  * @cfg {Number} cols Specifies the visible width of a text area
7984  * @cfg {Number} rows Specifies the visible number of lines in a text area
7985  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7986  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7987  * @cfg {string} html text
7988  * 
7989  * @constructor
7990  * Create a new TextArea
7991  * @param {Object} config The config object
7992  */
7993
7994 Roo.bootstrap.TextArea = function(config){
7995     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7996    
7997 };
7998
7999 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8000      
8001     cols : false,
8002     rows : 5,
8003     readOnly : false,
8004     warp : 'soft',
8005     resize : false,
8006     value: false,
8007     html: false,
8008     
8009     getAutoCreate : function(){
8010         
8011         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8012         
8013         var id = Roo.id();
8014         
8015         var cfg = {};
8016         
8017         var input =  {
8018             tag: 'textarea',
8019             id : id,
8020             warp : this.warp,
8021             rows : this.rows,
8022             value : this.value || '',
8023             html: this.html || '',
8024             cls : 'form-control',
8025             placeholder : this.placeholder || '' 
8026             
8027         };
8028         
8029         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8030             input.maxLength = this.maxLength;
8031         }
8032         
8033         if(this.resize){
8034             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8035         }
8036         
8037         if(this.cols){
8038             input.cols = this.cols;
8039         }
8040         
8041         if (this.readOnly) {
8042             input.readonly = true;
8043         }
8044         
8045         if (this.name) {
8046             input.name = this.name;
8047         }
8048         
8049         if (this.size) {
8050             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8051         }
8052         
8053         var settings=this;
8054         ['xs','sm','md','lg'].map(function(size){
8055             if (settings[size]) {
8056                 cfg.cls += ' col-' + size + '-' + settings[size];
8057             }
8058         });
8059         
8060         var inputblock = input;
8061         
8062         if(this.hasFeedback && !this.allowBlank){
8063             
8064             var feedback = {
8065                 tag: 'span',
8066                 cls: 'glyphicon form-control-feedback'
8067             };
8068
8069             inputblock = {
8070                 cls : 'has-feedback',
8071                 cn :  [
8072                     input,
8073                     feedback
8074                 ] 
8075             };  
8076         }
8077         
8078         
8079         if (this.before || this.after) {
8080             
8081             inputblock = {
8082                 cls : 'input-group',
8083                 cn :  [] 
8084             };
8085             if (this.before) {
8086                 inputblock.cn.push({
8087                     tag :'span',
8088                     cls : 'input-group-addon',
8089                     html : this.before
8090                 });
8091             }
8092             
8093             inputblock.cn.push(input);
8094             
8095             if(this.hasFeedback && !this.allowBlank){
8096                 inputblock.cls += ' has-feedback';
8097                 inputblock.cn.push(feedback);
8098             }
8099             
8100             if (this.after) {
8101                 inputblock.cn.push({
8102                     tag :'span',
8103                     cls : 'input-group-addon',
8104                     html : this.after
8105                 });
8106             }
8107             
8108         }
8109         
8110         if (align ==='left' && this.fieldLabel.length) {
8111                 Roo.log("left and has label");
8112                 cfg.cn = [
8113                     
8114                     {
8115                         tag: 'label',
8116                         'for' :  id,
8117                         cls : 'control-label col-sm-' + this.labelWidth,
8118                         html : this.fieldLabel
8119                         
8120                     },
8121                     {
8122                         cls : "col-sm-" + (12 - this.labelWidth), 
8123                         cn: [
8124                             inputblock
8125                         ]
8126                     }
8127                     
8128                 ];
8129         } else if ( this.fieldLabel.length) {
8130                 Roo.log(" label");
8131                  cfg.cn = [
8132                    
8133                     {
8134                         tag: 'label',
8135                         //cls : 'input-group-addon',
8136                         html : this.fieldLabel
8137                         
8138                     },
8139                     
8140                     inputblock
8141                     
8142                 ];
8143
8144         } else {
8145             
8146                    Roo.log(" no label && no align");
8147                 cfg.cn = [
8148                     
8149                         inputblock
8150                     
8151                 ];
8152                 
8153                 
8154         }
8155         
8156         if (this.disabled) {
8157             input.disabled=true;
8158         }
8159         
8160         return cfg;
8161         
8162     },
8163     /**
8164      * return the real textarea element.
8165      */
8166     inputEl: function ()
8167     {
8168         return this.el.select('textarea.form-control',true).first();
8169     }
8170 });
8171
8172  
8173 /*
8174  * - LGPL
8175  *
8176  * trigger field - base class for combo..
8177  * 
8178  */
8179  
8180 /**
8181  * @class Roo.bootstrap.TriggerField
8182  * @extends Roo.bootstrap.Input
8183  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8184  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8185  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8186  * for which you can provide a custom implementation.  For example:
8187  * <pre><code>
8188 var trigger = new Roo.bootstrap.TriggerField();
8189 trigger.onTriggerClick = myTriggerFn;
8190 trigger.applyTo('my-field');
8191 </code></pre>
8192  *
8193  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8194  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8195  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8196  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8197  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8198
8199  * @constructor
8200  * Create a new TriggerField.
8201  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8202  * to the base TextField)
8203  */
8204 Roo.bootstrap.TriggerField = function(config){
8205     this.mimicing = false;
8206     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8207 };
8208
8209 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8210     /**
8211      * @cfg {String} triggerClass A CSS class to apply to the trigger
8212      */
8213      /**
8214      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8215      */
8216     hideTrigger:false,
8217
8218     /** @cfg {Boolean} grow @hide */
8219     /** @cfg {Number} growMin @hide */
8220     /** @cfg {Number} growMax @hide */
8221
8222     /**
8223      * @hide 
8224      * @method
8225      */
8226     autoSize: Roo.emptyFn,
8227     // private
8228     monitorTab : true,
8229     // private
8230     deferHeight : true,
8231
8232     
8233     actionMode : 'wrap',
8234     
8235     caret : false,
8236     
8237     
8238     getAutoCreate : function(){
8239        
8240         var align = this.labelAlign || this.parentLabelAlign();
8241         
8242         var id = Roo.id();
8243         
8244         var cfg = {
8245             cls: 'form-group' //input-group
8246         };
8247         
8248         
8249         var input =  {
8250             tag: 'input',
8251             id : id,
8252             type : this.inputType,
8253             cls : 'form-control',
8254             autocomplete: 'new-password',
8255             placeholder : this.placeholder || '' 
8256             
8257         };
8258         if (this.name) {
8259             input.name = this.name;
8260         }
8261         if (this.size) {
8262             input.cls += ' input-' + this.size;
8263         }
8264         
8265         if (this.disabled) {
8266             input.disabled=true;
8267         }
8268         
8269         var inputblock = input;
8270         
8271         if(this.hasFeedback && !this.allowBlank){
8272             
8273             var feedback = {
8274                 tag: 'span',
8275                 cls: 'glyphicon form-control-feedback'
8276             };
8277
8278             inputblock = {
8279                 cls : 'has-feedback',
8280                 cn :  [
8281                     input,
8282                     feedback
8283                 ] 
8284             };  
8285         }
8286         
8287         if (this.before || this.after) {
8288             
8289             inputblock = {
8290                 cls : 'input-group',
8291                 cn :  [] 
8292             };
8293             if (this.before) {
8294                 inputblock.cn.push({
8295                     tag :'span',
8296                     cls : 'input-group-addon',
8297                     html : this.before
8298                 });
8299             }
8300             
8301             inputblock.cn.push(input);
8302             
8303             if(this.hasFeedback && !this.allowBlank){
8304                 inputblock.cls += ' has-feedback';
8305                 inputblock.cn.push(feedback);
8306             }
8307             
8308             if (this.after) {
8309                 inputblock.cn.push({
8310                     tag :'span',
8311                     cls : 'input-group-addon',
8312                     html : this.after
8313                 });
8314             }
8315             
8316         };
8317         
8318         var box = {
8319             tag: 'div',
8320             cn: [
8321                 {
8322                     tag: 'input',
8323                     type : 'hidden',
8324                     cls: 'form-hidden-field'
8325                 },
8326                 inputblock
8327             ]
8328             
8329         };
8330         
8331         if(this.multiple){
8332             Roo.log('multiple');
8333             
8334             box = {
8335                 tag: 'div',
8336                 cn: [
8337                     {
8338                         tag: 'input',
8339                         type : 'hidden',
8340                         cls: 'form-hidden-field'
8341                     },
8342                     {
8343                         tag: 'ul',
8344                         cls: 'select2-choices',
8345                         cn:[
8346                             {
8347                                 tag: 'li',
8348                                 cls: 'select2-search-field',
8349                                 cn: [
8350
8351                                     inputblock
8352                                 ]
8353                             }
8354                         ]
8355                     }
8356                 ]
8357             }
8358         };
8359         
8360         var combobox = {
8361             cls: 'select2-container input-group',
8362             cn: [
8363                 box
8364 //                {
8365 //                    tag: 'ul',
8366 //                    cls: 'typeahead typeahead-long dropdown-menu',
8367 //                    style: 'display:none'
8368 //                }
8369             ]
8370         };
8371         
8372         if(!this.multiple && this.showToggleBtn){
8373             
8374             var caret = {
8375                         tag: 'span',
8376                         cls: 'caret'
8377              };
8378             if (this.caret != false) {
8379                 caret = {
8380                      tag: 'i',
8381                      cls: 'fa fa-' + this.caret
8382                 };
8383                 
8384             }
8385             
8386             combobox.cn.push({
8387                 tag :'span',
8388                 cls : 'input-group-addon btn dropdown-toggle',
8389                 cn : [
8390                     caret,
8391                     {
8392                         tag: 'span',
8393                         cls: 'combobox-clear',
8394                         cn  : [
8395                             {
8396                                 tag : 'i',
8397                                 cls: 'icon-remove'
8398                             }
8399                         ]
8400                     }
8401                 ]
8402
8403             })
8404         }
8405         
8406         if(this.multiple){
8407             combobox.cls += ' select2-container-multi';
8408         }
8409         
8410         if (align ==='left' && this.fieldLabel.length) {
8411             
8412                 Roo.log("left and has label");
8413                 cfg.cn = [
8414                     
8415                     {
8416                         tag: 'label',
8417                         'for' :  id,
8418                         cls : 'control-label col-sm-' + this.labelWidth,
8419                         html : this.fieldLabel
8420                         
8421                     },
8422                     {
8423                         cls : "col-sm-" + (12 - this.labelWidth), 
8424                         cn: [
8425                             combobox
8426                         ]
8427                     }
8428                     
8429                 ];
8430         } else if ( this.fieldLabel.length) {
8431                 Roo.log(" label");
8432                  cfg.cn = [
8433                    
8434                     {
8435                         tag: 'label',
8436                         //cls : 'input-group-addon',
8437                         html : this.fieldLabel
8438                         
8439                     },
8440                     
8441                     combobox
8442                     
8443                 ];
8444
8445         } else {
8446             
8447                 Roo.log(" no label && no align");
8448                 cfg = combobox
8449                      
8450                 
8451         }
8452          
8453         var settings=this;
8454         ['xs','sm','md','lg'].map(function(size){
8455             if (settings[size]) {
8456                 cfg.cls += ' col-' + size + '-' + settings[size];
8457             }
8458         });
8459         
8460         return cfg;
8461         
8462     },
8463     
8464     
8465     
8466     // private
8467     onResize : function(w, h){
8468 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8469 //        if(typeof w == 'number'){
8470 //            var x = w - this.trigger.getWidth();
8471 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8472 //            this.trigger.setStyle('left', x+'px');
8473 //        }
8474     },
8475
8476     // private
8477     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8478
8479     // private
8480     getResizeEl : function(){
8481         return this.inputEl();
8482     },
8483
8484     // private
8485     getPositionEl : function(){
8486         return this.inputEl();
8487     },
8488
8489     // private
8490     alignErrorIcon : function(){
8491         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8492     },
8493
8494     // private
8495     initEvents : function(){
8496         
8497         this.createList();
8498         
8499         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8500         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8501         if(!this.multiple && this.showToggleBtn){
8502             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8503             if(this.hideTrigger){
8504                 this.trigger.setDisplayed(false);
8505             }
8506             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8507         }
8508         
8509         if(this.multiple){
8510             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8511         }
8512         
8513         //this.trigger.addClassOnOver('x-form-trigger-over');
8514         //this.trigger.addClassOnClick('x-form-trigger-click');
8515         
8516         //if(!this.width){
8517         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8518         //}
8519     },
8520     
8521     createList : function()
8522     {
8523         this.list = Roo.get(document.body).createChild({
8524             tag: 'ul',
8525             cls: 'typeahead typeahead-long dropdown-menu',
8526             style: 'display:none'
8527         });
8528         
8529         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8530         
8531     },
8532
8533     // private
8534     initTrigger : function(){
8535        
8536     },
8537
8538     // private
8539     onDestroy : function(){
8540         if(this.trigger){
8541             this.trigger.removeAllListeners();
8542           //  this.trigger.remove();
8543         }
8544         //if(this.wrap){
8545         //    this.wrap.remove();
8546         //}
8547         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8548     },
8549
8550     // private
8551     onFocus : function(){
8552         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8553         /*
8554         if(!this.mimicing){
8555             this.wrap.addClass('x-trigger-wrap-focus');
8556             this.mimicing = true;
8557             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8558             if(this.monitorTab){
8559                 this.el.on("keydown", this.checkTab, this);
8560             }
8561         }
8562         */
8563     },
8564
8565     // private
8566     checkTab : function(e){
8567         if(e.getKey() == e.TAB){
8568             this.triggerBlur();
8569         }
8570     },
8571
8572     // private
8573     onBlur : function(){
8574         // do nothing
8575     },
8576
8577     // private
8578     mimicBlur : function(e, t){
8579         /*
8580         if(!this.wrap.contains(t) && this.validateBlur()){
8581             this.triggerBlur();
8582         }
8583         */
8584     },
8585
8586     // private
8587     triggerBlur : function(){
8588         this.mimicing = false;
8589         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8590         if(this.monitorTab){
8591             this.el.un("keydown", this.checkTab, this);
8592         }
8593         //this.wrap.removeClass('x-trigger-wrap-focus');
8594         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8595     },
8596
8597     // private
8598     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8599     validateBlur : function(e, t){
8600         return true;
8601     },
8602
8603     // private
8604     onDisable : function(){
8605         this.inputEl().dom.disabled = true;
8606         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8607         //if(this.wrap){
8608         //    this.wrap.addClass('x-item-disabled');
8609         //}
8610     },
8611
8612     // private
8613     onEnable : function(){
8614         this.inputEl().dom.disabled = false;
8615         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8616         //if(this.wrap){
8617         //    this.el.removeClass('x-item-disabled');
8618         //}
8619     },
8620
8621     // private
8622     onShow : function(){
8623         var ae = this.getActionEl();
8624         
8625         if(ae){
8626             ae.dom.style.display = '';
8627             ae.dom.style.visibility = 'visible';
8628         }
8629     },
8630
8631     // private
8632     
8633     onHide : function(){
8634         var ae = this.getActionEl();
8635         ae.dom.style.display = 'none';
8636     },
8637
8638     /**
8639      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8640      * by an implementing function.
8641      * @method
8642      * @param {EventObject} e
8643      */
8644     onTriggerClick : Roo.emptyFn
8645 });
8646  /*
8647  * Based on:
8648  * Ext JS Library 1.1.1
8649  * Copyright(c) 2006-2007, Ext JS, LLC.
8650  *
8651  * Originally Released Under LGPL - original licence link has changed is not relivant.
8652  *
8653  * Fork - LGPL
8654  * <script type="text/javascript">
8655  */
8656
8657
8658 /**
8659  * @class Roo.data.SortTypes
8660  * @singleton
8661  * Defines the default sorting (casting?) comparison functions used when sorting data.
8662  */
8663 Roo.data.SortTypes = {
8664     /**
8665      * Default sort that does nothing
8666      * @param {Mixed} s The value being converted
8667      * @return {Mixed} The comparison value
8668      */
8669     none : function(s){
8670         return s;
8671     },
8672     
8673     /**
8674      * The regular expression used to strip tags
8675      * @type {RegExp}
8676      * @property
8677      */
8678     stripTagsRE : /<\/?[^>]+>/gi,
8679     
8680     /**
8681      * Strips all HTML tags to sort on text only
8682      * @param {Mixed} s The value being converted
8683      * @return {String} The comparison value
8684      */
8685     asText : function(s){
8686         return String(s).replace(this.stripTagsRE, "");
8687     },
8688     
8689     /**
8690      * Strips all HTML tags to sort on text only - Case insensitive
8691      * @param {Mixed} s The value being converted
8692      * @return {String} The comparison value
8693      */
8694     asUCText : function(s){
8695         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8696     },
8697     
8698     /**
8699      * Case insensitive string
8700      * @param {Mixed} s The value being converted
8701      * @return {String} The comparison value
8702      */
8703     asUCString : function(s) {
8704         return String(s).toUpperCase();
8705     },
8706     
8707     /**
8708      * Date sorting
8709      * @param {Mixed} s The value being converted
8710      * @return {Number} The comparison value
8711      */
8712     asDate : function(s) {
8713         if(!s){
8714             return 0;
8715         }
8716         if(s instanceof Date){
8717             return s.getTime();
8718         }
8719         return Date.parse(String(s));
8720     },
8721     
8722     /**
8723      * Float sorting
8724      * @param {Mixed} s The value being converted
8725      * @return {Float} The comparison value
8726      */
8727     asFloat : function(s) {
8728         var val = parseFloat(String(s).replace(/,/g, ""));
8729         if(isNaN(val)) val = 0;
8730         return val;
8731     },
8732     
8733     /**
8734      * Integer sorting
8735      * @param {Mixed} s The value being converted
8736      * @return {Number} The comparison value
8737      */
8738     asInt : function(s) {
8739         var val = parseInt(String(s).replace(/,/g, ""));
8740         if(isNaN(val)) val = 0;
8741         return val;
8742     }
8743 };/*
8744  * Based on:
8745  * Ext JS Library 1.1.1
8746  * Copyright(c) 2006-2007, Ext JS, LLC.
8747  *
8748  * Originally Released Under LGPL - original licence link has changed is not relivant.
8749  *
8750  * Fork - LGPL
8751  * <script type="text/javascript">
8752  */
8753
8754 /**
8755 * @class Roo.data.Record
8756  * Instances of this class encapsulate both record <em>definition</em> information, and record
8757  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8758  * to access Records cached in an {@link Roo.data.Store} object.<br>
8759  * <p>
8760  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8761  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8762  * objects.<br>
8763  * <p>
8764  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8765  * @constructor
8766  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8767  * {@link #create}. The parameters are the same.
8768  * @param {Array} data An associative Array of data values keyed by the field name.
8769  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8770  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8771  * not specified an integer id is generated.
8772  */
8773 Roo.data.Record = function(data, id){
8774     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8775     this.data = data;
8776 };
8777
8778 /**
8779  * Generate a constructor for a specific record layout.
8780  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8781  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8782  * Each field definition object may contain the following properties: <ul>
8783  * <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,
8784  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8785  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8786  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8787  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8788  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8789  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8790  * this may be omitted.</p></li>
8791  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8792  * <ul><li>auto (Default, implies no conversion)</li>
8793  * <li>string</li>
8794  * <li>int</li>
8795  * <li>float</li>
8796  * <li>boolean</li>
8797  * <li>date</li></ul></p></li>
8798  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8799  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8800  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8801  * by the Reader into an object that will be stored in the Record. It is passed the
8802  * following parameters:<ul>
8803  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8804  * </ul></p></li>
8805  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8806  * </ul>
8807  * <br>usage:<br><pre><code>
8808 var TopicRecord = Roo.data.Record.create(
8809     {name: 'title', mapping: 'topic_title'},
8810     {name: 'author', mapping: 'username'},
8811     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8812     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8813     {name: 'lastPoster', mapping: 'user2'},
8814     {name: 'excerpt', mapping: 'post_text'}
8815 );
8816
8817 var myNewRecord = new TopicRecord({
8818     title: 'Do my job please',
8819     author: 'noobie',
8820     totalPosts: 1,
8821     lastPost: new Date(),
8822     lastPoster: 'Animal',
8823     excerpt: 'No way dude!'
8824 });
8825 myStore.add(myNewRecord);
8826 </code></pre>
8827  * @method create
8828  * @static
8829  */
8830 Roo.data.Record.create = function(o){
8831     var f = function(){
8832         f.superclass.constructor.apply(this, arguments);
8833     };
8834     Roo.extend(f, Roo.data.Record);
8835     var p = f.prototype;
8836     p.fields = new Roo.util.MixedCollection(false, function(field){
8837         return field.name;
8838     });
8839     for(var i = 0, len = o.length; i < len; i++){
8840         p.fields.add(new Roo.data.Field(o[i]));
8841     }
8842     f.getField = function(name){
8843         return p.fields.get(name);  
8844     };
8845     return f;
8846 };
8847
8848 Roo.data.Record.AUTO_ID = 1000;
8849 Roo.data.Record.EDIT = 'edit';
8850 Roo.data.Record.REJECT = 'reject';
8851 Roo.data.Record.COMMIT = 'commit';
8852
8853 Roo.data.Record.prototype = {
8854     /**
8855      * Readonly flag - true if this record has been modified.
8856      * @type Boolean
8857      */
8858     dirty : false,
8859     editing : false,
8860     error: null,
8861     modified: null,
8862
8863     // private
8864     join : function(store){
8865         this.store = store;
8866     },
8867
8868     /**
8869      * Set the named field to the specified value.
8870      * @param {String} name The name of the field to set.
8871      * @param {Object} value The value to set the field to.
8872      */
8873     set : function(name, value){
8874         if(this.data[name] == value){
8875             return;
8876         }
8877         this.dirty = true;
8878         if(!this.modified){
8879             this.modified = {};
8880         }
8881         if(typeof this.modified[name] == 'undefined'){
8882             this.modified[name] = this.data[name];
8883         }
8884         this.data[name] = value;
8885         if(!this.editing && this.store){
8886             this.store.afterEdit(this);
8887         }       
8888     },
8889
8890     /**
8891      * Get the value of the named field.
8892      * @param {String} name The name of the field to get the value of.
8893      * @return {Object} The value of the field.
8894      */
8895     get : function(name){
8896         return this.data[name]; 
8897     },
8898
8899     // private
8900     beginEdit : function(){
8901         this.editing = true;
8902         this.modified = {}; 
8903     },
8904
8905     // private
8906     cancelEdit : function(){
8907         this.editing = false;
8908         delete this.modified;
8909     },
8910
8911     // private
8912     endEdit : function(){
8913         this.editing = false;
8914         if(this.dirty && this.store){
8915             this.store.afterEdit(this);
8916         }
8917     },
8918
8919     /**
8920      * Usually called by the {@link Roo.data.Store} which owns the Record.
8921      * Rejects all changes made to the Record since either creation, or the last commit operation.
8922      * Modified fields are reverted to their original values.
8923      * <p>
8924      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8925      * of reject operations.
8926      */
8927     reject : function(){
8928         var m = this.modified;
8929         for(var n in m){
8930             if(typeof m[n] != "function"){
8931                 this.data[n] = m[n];
8932             }
8933         }
8934         this.dirty = false;
8935         delete this.modified;
8936         this.editing = false;
8937         if(this.store){
8938             this.store.afterReject(this);
8939         }
8940     },
8941
8942     /**
8943      * Usually called by the {@link Roo.data.Store} which owns the Record.
8944      * Commits all changes made to the Record since either creation, or the last commit operation.
8945      * <p>
8946      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8947      * of commit operations.
8948      */
8949     commit : function(){
8950         this.dirty = false;
8951         delete this.modified;
8952         this.editing = false;
8953         if(this.store){
8954             this.store.afterCommit(this);
8955         }
8956     },
8957
8958     // private
8959     hasError : function(){
8960         return this.error != null;
8961     },
8962
8963     // private
8964     clearError : function(){
8965         this.error = null;
8966     },
8967
8968     /**
8969      * Creates a copy of this record.
8970      * @param {String} id (optional) A new record id if you don't want to use this record's id
8971      * @return {Record}
8972      */
8973     copy : function(newId) {
8974         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8975     }
8976 };/*
8977  * Based on:
8978  * Ext JS Library 1.1.1
8979  * Copyright(c) 2006-2007, Ext JS, LLC.
8980  *
8981  * Originally Released Under LGPL - original licence link has changed is not relivant.
8982  *
8983  * Fork - LGPL
8984  * <script type="text/javascript">
8985  */
8986
8987
8988
8989 /**
8990  * @class Roo.data.Store
8991  * @extends Roo.util.Observable
8992  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8993  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8994  * <p>
8995  * 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
8996  * has no knowledge of the format of the data returned by the Proxy.<br>
8997  * <p>
8998  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8999  * instances from the data object. These records are cached and made available through accessor functions.
9000  * @constructor
9001  * Creates a new Store.
9002  * @param {Object} config A config object containing the objects needed for the Store to access data,
9003  * and read the data into Records.
9004  */
9005 Roo.data.Store = function(config){
9006     this.data = new Roo.util.MixedCollection(false);
9007     this.data.getKey = function(o){
9008         return o.id;
9009     };
9010     this.baseParams = {};
9011     // private
9012     this.paramNames = {
9013         "start" : "start",
9014         "limit" : "limit",
9015         "sort" : "sort",
9016         "dir" : "dir",
9017         "multisort" : "_multisort"
9018     };
9019
9020     if(config && config.data){
9021         this.inlineData = config.data;
9022         delete config.data;
9023     }
9024
9025     Roo.apply(this, config);
9026     
9027     if(this.reader){ // reader passed
9028         this.reader = Roo.factory(this.reader, Roo.data);
9029         this.reader.xmodule = this.xmodule || false;
9030         if(!this.recordType){
9031             this.recordType = this.reader.recordType;
9032         }
9033         if(this.reader.onMetaChange){
9034             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9035         }
9036     }
9037
9038     if(this.recordType){
9039         this.fields = this.recordType.prototype.fields;
9040     }
9041     this.modified = [];
9042
9043     this.addEvents({
9044         /**
9045          * @event datachanged
9046          * Fires when the data cache has changed, and a widget which is using this Store
9047          * as a Record cache should refresh its view.
9048          * @param {Store} this
9049          */
9050         datachanged : true,
9051         /**
9052          * @event metachange
9053          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9054          * @param {Store} this
9055          * @param {Object} meta The JSON metadata
9056          */
9057         metachange : true,
9058         /**
9059          * @event add
9060          * Fires when Records have been added to the Store
9061          * @param {Store} this
9062          * @param {Roo.data.Record[]} records The array of Records added
9063          * @param {Number} index The index at which the record(s) were added
9064          */
9065         add : true,
9066         /**
9067          * @event remove
9068          * Fires when a Record has been removed from the Store
9069          * @param {Store} this
9070          * @param {Roo.data.Record} record The Record that was removed
9071          * @param {Number} index The index at which the record was removed
9072          */
9073         remove : true,
9074         /**
9075          * @event update
9076          * Fires when a Record has been updated
9077          * @param {Store} this
9078          * @param {Roo.data.Record} record The Record that was updated
9079          * @param {String} operation The update operation being performed.  Value may be one of:
9080          * <pre><code>
9081  Roo.data.Record.EDIT
9082  Roo.data.Record.REJECT
9083  Roo.data.Record.COMMIT
9084          * </code></pre>
9085          */
9086         update : true,
9087         /**
9088          * @event clear
9089          * Fires when the data cache has been cleared.
9090          * @param {Store} this
9091          */
9092         clear : true,
9093         /**
9094          * @event beforeload
9095          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9096          * the load action will be canceled.
9097          * @param {Store} this
9098          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9099          */
9100         beforeload : true,
9101         /**
9102          * @event beforeloadadd
9103          * Fires after a new set of Records has been loaded.
9104          * @param {Store} this
9105          * @param {Roo.data.Record[]} records The Records that were loaded
9106          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9107          */
9108         beforeloadadd : true,
9109         /**
9110          * @event load
9111          * Fires after a new set of Records has been loaded, before they are added to the store.
9112          * @param {Store} this
9113          * @param {Roo.data.Record[]} records The Records that were loaded
9114          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9115          * @params {Object} return from reader
9116          */
9117         load : true,
9118         /**
9119          * @event loadexception
9120          * Fires if an exception occurs in the Proxy during loading.
9121          * Called with the signature of the Proxy's "loadexception" event.
9122          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9123          * 
9124          * @param {Proxy} 
9125          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9126          * @param {Object} load options 
9127          * @param {Object} jsonData from your request (normally this contains the Exception)
9128          */
9129         loadexception : true
9130     });
9131     
9132     if(this.proxy){
9133         this.proxy = Roo.factory(this.proxy, Roo.data);
9134         this.proxy.xmodule = this.xmodule || false;
9135         this.relayEvents(this.proxy,  ["loadexception"]);
9136     }
9137     this.sortToggle = {};
9138     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9139
9140     Roo.data.Store.superclass.constructor.call(this);
9141
9142     if(this.inlineData){
9143         this.loadData(this.inlineData);
9144         delete this.inlineData;
9145     }
9146 };
9147
9148 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9149      /**
9150     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9151     * without a remote query - used by combo/forms at present.
9152     */
9153     
9154     /**
9155     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9156     */
9157     /**
9158     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9159     */
9160     /**
9161     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9162     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9163     */
9164     /**
9165     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9166     * on any HTTP request
9167     */
9168     /**
9169     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9170     */
9171     /**
9172     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9173     */
9174     multiSort: false,
9175     /**
9176     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9177     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9178     */
9179     remoteSort : false,
9180
9181     /**
9182     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9183      * loaded or when a record is removed. (defaults to false).
9184     */
9185     pruneModifiedRecords : false,
9186
9187     // private
9188     lastOptions : null,
9189
9190     /**
9191      * Add Records to the Store and fires the add event.
9192      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9193      */
9194     add : function(records){
9195         records = [].concat(records);
9196         for(var i = 0, len = records.length; i < len; i++){
9197             records[i].join(this);
9198         }
9199         var index = this.data.length;
9200         this.data.addAll(records);
9201         this.fireEvent("add", this, records, index);
9202     },
9203
9204     /**
9205      * Remove a Record from the Store and fires the remove event.
9206      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9207      */
9208     remove : function(record){
9209         var index = this.data.indexOf(record);
9210         this.data.removeAt(index);
9211         if(this.pruneModifiedRecords){
9212             this.modified.remove(record);
9213         }
9214         this.fireEvent("remove", this, record, index);
9215     },
9216
9217     /**
9218      * Remove all Records from the Store and fires the clear event.
9219      */
9220     removeAll : function(){
9221         this.data.clear();
9222         if(this.pruneModifiedRecords){
9223             this.modified = [];
9224         }
9225         this.fireEvent("clear", this);
9226     },
9227
9228     /**
9229      * Inserts Records to the Store at the given index and fires the add event.
9230      * @param {Number} index The start index at which to insert the passed Records.
9231      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9232      */
9233     insert : function(index, records){
9234         records = [].concat(records);
9235         for(var i = 0, len = records.length; i < len; i++){
9236             this.data.insert(index, records[i]);
9237             records[i].join(this);
9238         }
9239         this.fireEvent("add", this, records, index);
9240     },
9241
9242     /**
9243      * Get the index within the cache of the passed Record.
9244      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9245      * @return {Number} The index of the passed Record. Returns -1 if not found.
9246      */
9247     indexOf : function(record){
9248         return this.data.indexOf(record);
9249     },
9250
9251     /**
9252      * Get the index within the cache of the Record with the passed id.
9253      * @param {String} id The id of the Record to find.
9254      * @return {Number} The index of the Record. Returns -1 if not found.
9255      */
9256     indexOfId : function(id){
9257         return this.data.indexOfKey(id);
9258     },
9259
9260     /**
9261      * Get the Record with the specified id.
9262      * @param {String} id The id of the Record to find.
9263      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9264      */
9265     getById : function(id){
9266         return this.data.key(id);
9267     },
9268
9269     /**
9270      * Get the Record at the specified index.
9271      * @param {Number} index The index of the Record to find.
9272      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9273      */
9274     getAt : function(index){
9275         return this.data.itemAt(index);
9276     },
9277
9278     /**
9279      * Returns a range of Records between specified indices.
9280      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9281      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9282      * @return {Roo.data.Record[]} An array of Records
9283      */
9284     getRange : function(start, end){
9285         return this.data.getRange(start, end);
9286     },
9287
9288     // private
9289     storeOptions : function(o){
9290         o = Roo.apply({}, o);
9291         delete o.callback;
9292         delete o.scope;
9293         this.lastOptions = o;
9294     },
9295
9296     /**
9297      * Loads the Record cache from the configured Proxy using the configured Reader.
9298      * <p>
9299      * If using remote paging, then the first load call must specify the <em>start</em>
9300      * and <em>limit</em> properties in the options.params property to establish the initial
9301      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9302      * <p>
9303      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9304      * and this call will return before the new data has been loaded. Perform any post-processing
9305      * in a callback function, or in a "load" event handler.</strong>
9306      * <p>
9307      * @param {Object} options An object containing properties which control loading options:<ul>
9308      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9309      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9310      * passed the following arguments:<ul>
9311      * <li>r : Roo.data.Record[]</li>
9312      * <li>options: Options object from the load call</li>
9313      * <li>success: Boolean success indicator</li></ul></li>
9314      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9315      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9316      * </ul>
9317      */
9318     load : function(options){
9319         options = options || {};
9320         if(this.fireEvent("beforeload", this, options) !== false){
9321             this.storeOptions(options);
9322             var p = Roo.apply(options.params || {}, this.baseParams);
9323             // if meta was not loaded from remote source.. try requesting it.
9324             if (!this.reader.metaFromRemote) {
9325                 p._requestMeta = 1;
9326             }
9327             if(this.sortInfo && this.remoteSort){
9328                 var pn = this.paramNames;
9329                 p[pn["sort"]] = this.sortInfo.field;
9330                 p[pn["dir"]] = this.sortInfo.direction;
9331             }
9332             if (this.multiSort) {
9333                 var pn = this.paramNames;
9334                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9335             }
9336             
9337             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9338         }
9339     },
9340
9341     /**
9342      * Reloads the Record cache from the configured Proxy using the configured Reader and
9343      * the options from the last load operation performed.
9344      * @param {Object} options (optional) An object containing properties which may override the options
9345      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9346      * the most recently used options are reused).
9347      */
9348     reload : function(options){
9349         this.load(Roo.applyIf(options||{}, this.lastOptions));
9350     },
9351
9352     // private
9353     // Called as a callback by the Reader during a load operation.
9354     loadRecords : function(o, options, success){
9355         if(!o || success === false){
9356             if(success !== false){
9357                 this.fireEvent("load", this, [], options, o);
9358             }
9359             if(options.callback){
9360                 options.callback.call(options.scope || this, [], options, false);
9361             }
9362             return;
9363         }
9364         // if data returned failure - throw an exception.
9365         if (o.success === false) {
9366             // show a message if no listener is registered.
9367             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9368                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9369             }
9370             // loadmask wil be hooked into this..
9371             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9372             return;
9373         }
9374         var r = o.records, t = o.totalRecords || r.length;
9375         
9376         this.fireEvent("beforeloadadd", this, r, options, o);
9377         
9378         if(!options || options.add !== true){
9379             if(this.pruneModifiedRecords){
9380                 this.modified = [];
9381             }
9382             for(var i = 0, len = r.length; i < len; i++){
9383                 r[i].join(this);
9384             }
9385             if(this.snapshot){
9386                 this.data = this.snapshot;
9387                 delete this.snapshot;
9388             }
9389             this.data.clear();
9390             this.data.addAll(r);
9391             this.totalLength = t;
9392             this.applySort();
9393             this.fireEvent("datachanged", this);
9394         }else{
9395             this.totalLength = Math.max(t, this.data.length+r.length);
9396             this.add(r);
9397         }
9398         this.fireEvent("load", this, r, options, o);
9399         if(options.callback){
9400             options.callback.call(options.scope || this, r, options, true);
9401         }
9402     },
9403
9404
9405     /**
9406      * Loads data from a passed data block. A Reader which understands the format of the data
9407      * must have been configured in the constructor.
9408      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9409      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9410      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9411      */
9412     loadData : function(o, append){
9413         var r = this.reader.readRecords(o);
9414         this.loadRecords(r, {add: append}, true);
9415     },
9416
9417     /**
9418      * Gets the number of cached records.
9419      * <p>
9420      * <em>If using paging, this may not be the total size of the dataset. If the data object
9421      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9422      * the data set size</em>
9423      */
9424     getCount : function(){
9425         return this.data.length || 0;
9426     },
9427
9428     /**
9429      * Gets the total number of records in the dataset as returned by the server.
9430      * <p>
9431      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9432      * the dataset size</em>
9433      */
9434     getTotalCount : function(){
9435         return this.totalLength || 0;
9436     },
9437
9438     /**
9439      * Returns the sort state of the Store as an object with two properties:
9440      * <pre><code>
9441  field {String} The name of the field by which the Records are sorted
9442  direction {String} The sort order, "ASC" or "DESC"
9443      * </code></pre>
9444      */
9445     getSortState : function(){
9446         return this.sortInfo;
9447     },
9448
9449     // private
9450     applySort : function(){
9451         if(this.sortInfo && !this.remoteSort){
9452             var s = this.sortInfo, f = s.field;
9453             var st = this.fields.get(f).sortType;
9454             var fn = function(r1, r2){
9455                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9456                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9457             };
9458             this.data.sort(s.direction, fn);
9459             if(this.snapshot && this.snapshot != this.data){
9460                 this.snapshot.sort(s.direction, fn);
9461             }
9462         }
9463     },
9464
9465     /**
9466      * Sets the default sort column and order to be used by the next load operation.
9467      * @param {String} fieldName The name of the field to sort by.
9468      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9469      */
9470     setDefaultSort : function(field, dir){
9471         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9472     },
9473
9474     /**
9475      * Sort the Records.
9476      * If remote sorting is used, the sort is performed on the server, and the cache is
9477      * reloaded. If local sorting is used, the cache is sorted internally.
9478      * @param {String} fieldName The name of the field to sort by.
9479      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9480      */
9481     sort : function(fieldName, dir){
9482         var f = this.fields.get(fieldName);
9483         if(!dir){
9484             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9485             
9486             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9487                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9488             }else{
9489                 dir = f.sortDir;
9490             }
9491         }
9492         this.sortToggle[f.name] = dir;
9493         this.sortInfo = {field: f.name, direction: dir};
9494         if(!this.remoteSort){
9495             this.applySort();
9496             this.fireEvent("datachanged", this);
9497         }else{
9498             this.load(this.lastOptions);
9499         }
9500     },
9501
9502     /**
9503      * Calls the specified function for each of the Records in the cache.
9504      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9505      * Returning <em>false</em> aborts and exits the iteration.
9506      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9507      */
9508     each : function(fn, scope){
9509         this.data.each(fn, scope);
9510     },
9511
9512     /**
9513      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9514      * (e.g., during paging).
9515      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9516      */
9517     getModifiedRecords : function(){
9518         return this.modified;
9519     },
9520
9521     // private
9522     createFilterFn : function(property, value, anyMatch){
9523         if(!value.exec){ // not a regex
9524             value = String(value);
9525             if(value.length == 0){
9526                 return false;
9527             }
9528             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9529         }
9530         
9531         return function(r){
9532             return value.test(r.data[property]);
9533         };
9534     },
9535
9536     /**
9537      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9538      * @param {String} property A field on your records
9539      * @param {Number} start The record index to start at (defaults to 0)
9540      * @param {Number} end The last record index to include (defaults to length - 1)
9541      * @return {Number} The sum
9542      */
9543     sum : function(property, start, end){
9544         var rs = this.data.items, v = 0;
9545         start = start || 0;
9546         end = (end || end === 0) ? end : rs.length-1;
9547
9548         for(var i = start; i <= end; i++){
9549             v += (rs[i].data[property] || 0);
9550         }
9551         return v;
9552     },
9553
9554     /**
9555      * Filter the records by a specified property.
9556      * @param {String} field A field on your records
9557      * @param {String/RegExp} value Either a string that the field
9558      * should start with or a RegExp to test against the field
9559      * @param {Boolean} anyMatch True to match any part not just the beginning
9560      */
9561     filter : function(property, value, anyMatch){
9562         
9563         if(typeof(property) == 'string'){
9564             var fn = this.createFilterFn(property, value, anyMatch);
9565             return fn ? this.filterBy(fn) : this.clearFilter();
9566         }
9567         
9568         var fn = [];
9569         var afn = [];
9570         
9571         var _this = this;
9572         
9573         Roo.each(property, function(p){
9574             if(anyMatch == true){
9575                 afn.push(_this.createFilterFn(p, value, true));
9576             }
9577             
9578             fn.push(_this.createFilterFn(p, value, false));
9579         });
9580         
9581         if(!fn.length && !afn.length){
9582             return this.clearFilter();
9583         }
9584         
9585         this.snapshot = this.snapshot || this.data;
9586         
9587         var filterData = [];
9588         
9589         Roo.each(fn, function(f){
9590             filterData.push(_this.queryBy(f, _this));
9591         });
9592         
9593         Roo.each(afn, function(f){
9594             filterData.push(_this.queryBy(f, _this));
9595         });
9596         
9597         var data = this.snapshot || this.data;
9598         
9599         var r = new Roo.util.MixedCollection();
9600         r.getKey = data.getKey;
9601         
9602         var keys =[];
9603         
9604         Roo.each(filterData, function(d){
9605             var k = d.keys, it = d.items;
9606             for(var i = 0, len = it.length; i < len; i++){
9607                 if(keys.indexOf(k[i]) == -1){
9608                     r.add(k[i], it[i]);
9609                 }
9610             }
9611         });
9612         
9613         this.data = r;
9614         this.fireEvent("datachanged", this);
9615     },
9616
9617     /**
9618      * Filter by a function. The specified function will be called with each
9619      * record in this data source. If the function returns true the record is included,
9620      * otherwise it is filtered.
9621      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9622      * @param {Object} scope (optional) The scope of the function (defaults to this)
9623      */
9624     filterBy : function(fn, scope){
9625         this.snapshot = this.snapshot || this.data;
9626         this.data = this.queryBy(fn, scope||this);
9627         this.fireEvent("datachanged", this);
9628     },
9629
9630     /**
9631      * Query the records by a specified property.
9632      * @param {String} field A field on your records
9633      * @param {String/RegExp} value Either a string that the field
9634      * should start with or a RegExp to test against the field
9635      * @param {Boolean} anyMatch True to match any part not just the beginning
9636      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9637      */
9638     query : function(property, value, anyMatch){
9639         var fn = this.createFilterFn(property, value, anyMatch);
9640         return fn ? this.queryBy(fn) : this.data.clone();
9641     },
9642
9643     /**
9644      * Query by a function. The specified function will be called with each
9645      * record in this data source. If the function returns true the record is included
9646      * in the results.
9647      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9648      * @param {Object} scope (optional) The scope of the function (defaults to this)
9649       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9650      **/
9651     queryBy : function(fn, scope){
9652         var data = this.snapshot || this.data;
9653         return data.filterBy(fn, scope||this);
9654     },
9655
9656     /**
9657      * Collects unique values for a particular dataIndex from this store.
9658      * @param {String} dataIndex The property to collect
9659      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9660      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9661      * @return {Array} An array of the unique values
9662      **/
9663     collect : function(dataIndex, allowNull, bypassFilter){
9664         var d = (bypassFilter === true && this.snapshot) ?
9665                 this.snapshot.items : this.data.items;
9666         var v, sv, r = [], l = {};
9667         for(var i = 0, len = d.length; i < len; i++){
9668             v = d[i].data[dataIndex];
9669             sv = String(v);
9670             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9671                 l[sv] = true;
9672                 r[r.length] = v;
9673             }
9674         }
9675         return r;
9676     },
9677
9678     /**
9679      * Revert to a view of the Record cache with no filtering applied.
9680      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9681      */
9682     clearFilter : function(suppressEvent){
9683         if(this.snapshot && this.snapshot != this.data){
9684             this.data = this.snapshot;
9685             delete this.snapshot;
9686             if(suppressEvent !== true){
9687                 this.fireEvent("datachanged", this);
9688             }
9689         }
9690     },
9691
9692     // private
9693     afterEdit : function(record){
9694         if(this.modified.indexOf(record) == -1){
9695             this.modified.push(record);
9696         }
9697         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9698     },
9699     
9700     // private
9701     afterReject : function(record){
9702         this.modified.remove(record);
9703         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9704     },
9705
9706     // private
9707     afterCommit : function(record){
9708         this.modified.remove(record);
9709         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9710     },
9711
9712     /**
9713      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9714      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9715      */
9716     commitChanges : function(){
9717         var m = this.modified.slice(0);
9718         this.modified = [];
9719         for(var i = 0, len = m.length; i < len; i++){
9720             m[i].commit();
9721         }
9722     },
9723
9724     /**
9725      * Cancel outstanding changes on all changed records.
9726      */
9727     rejectChanges : function(){
9728         var m = this.modified.slice(0);
9729         this.modified = [];
9730         for(var i = 0, len = m.length; i < len; i++){
9731             m[i].reject();
9732         }
9733     },
9734
9735     onMetaChange : function(meta, rtype, o){
9736         this.recordType = rtype;
9737         this.fields = rtype.prototype.fields;
9738         delete this.snapshot;
9739         this.sortInfo = meta.sortInfo || this.sortInfo;
9740         this.modified = [];
9741         this.fireEvent('metachange', this, this.reader.meta);
9742     },
9743     
9744     moveIndex : function(data, type)
9745     {
9746         var index = this.indexOf(data);
9747         
9748         var newIndex = index + type;
9749         
9750         this.remove(data);
9751         
9752         this.insert(newIndex, data);
9753         
9754     }
9755 });/*
9756  * Based on:
9757  * Ext JS Library 1.1.1
9758  * Copyright(c) 2006-2007, Ext JS, LLC.
9759  *
9760  * Originally Released Under LGPL - original licence link has changed is not relivant.
9761  *
9762  * Fork - LGPL
9763  * <script type="text/javascript">
9764  */
9765
9766 /**
9767  * @class Roo.data.SimpleStore
9768  * @extends Roo.data.Store
9769  * Small helper class to make creating Stores from Array data easier.
9770  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9771  * @cfg {Array} fields An array of field definition objects, or field name strings.
9772  * @cfg {Array} data The multi-dimensional array of data
9773  * @constructor
9774  * @param {Object} config
9775  */
9776 Roo.data.SimpleStore = function(config){
9777     Roo.data.SimpleStore.superclass.constructor.call(this, {
9778         isLocal : true,
9779         reader: new Roo.data.ArrayReader({
9780                 id: config.id
9781             },
9782             Roo.data.Record.create(config.fields)
9783         ),
9784         proxy : new Roo.data.MemoryProxy(config.data)
9785     });
9786     this.load();
9787 };
9788 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9789  * Based on:
9790  * Ext JS Library 1.1.1
9791  * Copyright(c) 2006-2007, Ext JS, LLC.
9792  *
9793  * Originally Released Under LGPL - original licence link has changed is not relivant.
9794  *
9795  * Fork - LGPL
9796  * <script type="text/javascript">
9797  */
9798
9799 /**
9800 /**
9801  * @extends Roo.data.Store
9802  * @class Roo.data.JsonStore
9803  * Small helper class to make creating Stores for JSON data easier. <br/>
9804 <pre><code>
9805 var store = new Roo.data.JsonStore({
9806     url: 'get-images.php',
9807     root: 'images',
9808     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9809 });
9810 </code></pre>
9811  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9812  * JsonReader and HttpProxy (unless inline data is provided).</b>
9813  * @cfg {Array} fields An array of field definition objects, or field name strings.
9814  * @constructor
9815  * @param {Object} config
9816  */
9817 Roo.data.JsonStore = function(c){
9818     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9819         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9820         reader: new Roo.data.JsonReader(c, c.fields)
9821     }));
9822 };
9823 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9824  * Based on:
9825  * Ext JS Library 1.1.1
9826  * Copyright(c) 2006-2007, Ext JS, LLC.
9827  *
9828  * Originally Released Under LGPL - original licence link has changed is not relivant.
9829  *
9830  * Fork - LGPL
9831  * <script type="text/javascript">
9832  */
9833
9834  
9835 Roo.data.Field = function(config){
9836     if(typeof config == "string"){
9837         config = {name: config};
9838     }
9839     Roo.apply(this, config);
9840     
9841     if(!this.type){
9842         this.type = "auto";
9843     }
9844     
9845     var st = Roo.data.SortTypes;
9846     // named sortTypes are supported, here we look them up
9847     if(typeof this.sortType == "string"){
9848         this.sortType = st[this.sortType];
9849     }
9850     
9851     // set default sortType for strings and dates
9852     if(!this.sortType){
9853         switch(this.type){
9854             case "string":
9855                 this.sortType = st.asUCString;
9856                 break;
9857             case "date":
9858                 this.sortType = st.asDate;
9859                 break;
9860             default:
9861                 this.sortType = st.none;
9862         }
9863     }
9864
9865     // define once
9866     var stripRe = /[\$,%]/g;
9867
9868     // prebuilt conversion function for this field, instead of
9869     // switching every time we're reading a value
9870     if(!this.convert){
9871         var cv, dateFormat = this.dateFormat;
9872         switch(this.type){
9873             case "":
9874             case "auto":
9875             case undefined:
9876                 cv = function(v){ return v; };
9877                 break;
9878             case "string":
9879                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9880                 break;
9881             case "int":
9882                 cv = function(v){
9883                     return v !== undefined && v !== null && v !== '' ?
9884                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9885                     };
9886                 break;
9887             case "float":
9888                 cv = function(v){
9889                     return v !== undefined && v !== null && v !== '' ?
9890                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9891                     };
9892                 break;
9893             case "bool":
9894             case "boolean":
9895                 cv = function(v){ return v === true || v === "true" || v == 1; };
9896                 break;
9897             case "date":
9898                 cv = function(v){
9899                     if(!v){
9900                         return '';
9901                     }
9902                     if(v instanceof Date){
9903                         return v;
9904                     }
9905                     if(dateFormat){
9906                         if(dateFormat == "timestamp"){
9907                             return new Date(v*1000);
9908                         }
9909                         return Date.parseDate(v, dateFormat);
9910                     }
9911                     var parsed = Date.parse(v);
9912                     return parsed ? new Date(parsed) : null;
9913                 };
9914              break;
9915             
9916         }
9917         this.convert = cv;
9918     }
9919 };
9920
9921 Roo.data.Field.prototype = {
9922     dateFormat: null,
9923     defaultValue: "",
9924     mapping: null,
9925     sortType : null,
9926     sortDir : "ASC"
9927 };/*
9928  * Based on:
9929  * Ext JS Library 1.1.1
9930  * Copyright(c) 2006-2007, Ext JS, LLC.
9931  *
9932  * Originally Released Under LGPL - original licence link has changed is not relivant.
9933  *
9934  * Fork - LGPL
9935  * <script type="text/javascript">
9936  */
9937  
9938 // Base class for reading structured data from a data source.  This class is intended to be
9939 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9940
9941 /**
9942  * @class Roo.data.DataReader
9943  * Base class for reading structured data from a data source.  This class is intended to be
9944  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9945  */
9946
9947 Roo.data.DataReader = function(meta, recordType){
9948     
9949     this.meta = meta;
9950     
9951     this.recordType = recordType instanceof Array ? 
9952         Roo.data.Record.create(recordType) : recordType;
9953 };
9954
9955 Roo.data.DataReader.prototype = {
9956      /**
9957      * Create an empty record
9958      * @param {Object} data (optional) - overlay some values
9959      * @return {Roo.data.Record} record created.
9960      */
9961     newRow :  function(d) {
9962         var da =  {};
9963         this.recordType.prototype.fields.each(function(c) {
9964             switch( c.type) {
9965                 case 'int' : da[c.name] = 0; break;
9966                 case 'date' : da[c.name] = new Date(); break;
9967                 case 'float' : da[c.name] = 0.0; break;
9968                 case 'boolean' : da[c.name] = false; break;
9969                 default : da[c.name] = ""; break;
9970             }
9971             
9972         });
9973         return new this.recordType(Roo.apply(da, d));
9974     }
9975     
9976 };/*
9977  * Based on:
9978  * Ext JS Library 1.1.1
9979  * Copyright(c) 2006-2007, Ext JS, LLC.
9980  *
9981  * Originally Released Under LGPL - original licence link has changed is not relivant.
9982  *
9983  * Fork - LGPL
9984  * <script type="text/javascript">
9985  */
9986
9987 /**
9988  * @class Roo.data.DataProxy
9989  * @extends Roo.data.Observable
9990  * This class is an abstract base class for implementations which provide retrieval of
9991  * unformatted data objects.<br>
9992  * <p>
9993  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9994  * (of the appropriate type which knows how to parse the data object) to provide a block of
9995  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9996  * <p>
9997  * Custom implementations must implement the load method as described in
9998  * {@link Roo.data.HttpProxy#load}.
9999  */
10000 Roo.data.DataProxy = function(){
10001     this.addEvents({
10002         /**
10003          * @event beforeload
10004          * Fires before a network request is made to retrieve a data object.
10005          * @param {Object} This DataProxy object.
10006          * @param {Object} params The params parameter to the load function.
10007          */
10008         beforeload : true,
10009         /**
10010          * @event load
10011          * Fires before the load method's callback is called.
10012          * @param {Object} This DataProxy object.
10013          * @param {Object} o The data object.
10014          * @param {Object} arg The callback argument object passed to the load function.
10015          */
10016         load : true,
10017         /**
10018          * @event loadexception
10019          * Fires if an Exception occurs during data retrieval.
10020          * @param {Object} This DataProxy object.
10021          * @param {Object} o The data object.
10022          * @param {Object} arg The callback argument object passed to the load function.
10023          * @param {Object} e The Exception.
10024          */
10025         loadexception : true
10026     });
10027     Roo.data.DataProxy.superclass.constructor.call(this);
10028 };
10029
10030 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10031
10032     /**
10033      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10034      */
10035 /*
10036  * Based on:
10037  * Ext JS Library 1.1.1
10038  * Copyright(c) 2006-2007, Ext JS, LLC.
10039  *
10040  * Originally Released Under LGPL - original licence link has changed is not relivant.
10041  *
10042  * Fork - LGPL
10043  * <script type="text/javascript">
10044  */
10045 /**
10046  * @class Roo.data.MemoryProxy
10047  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10048  * to the Reader when its load method is called.
10049  * @constructor
10050  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10051  */
10052 Roo.data.MemoryProxy = function(data){
10053     if (data.data) {
10054         data = data.data;
10055     }
10056     Roo.data.MemoryProxy.superclass.constructor.call(this);
10057     this.data = data;
10058 };
10059
10060 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10061     /**
10062      * Load data from the requested source (in this case an in-memory
10063      * data object passed to the constructor), read the data object into
10064      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10065      * process that block using the passed callback.
10066      * @param {Object} params This parameter is not used by the MemoryProxy class.
10067      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10068      * object into a block of Roo.data.Records.
10069      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10070      * The function must be passed <ul>
10071      * <li>The Record block object</li>
10072      * <li>The "arg" argument from the load function</li>
10073      * <li>A boolean success indicator</li>
10074      * </ul>
10075      * @param {Object} scope The scope in which to call the callback
10076      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10077      */
10078     load : function(params, reader, callback, scope, arg){
10079         params = params || {};
10080         var result;
10081         try {
10082             result = reader.readRecords(this.data);
10083         }catch(e){
10084             this.fireEvent("loadexception", this, arg, null, e);
10085             callback.call(scope, null, arg, false);
10086             return;
10087         }
10088         callback.call(scope, result, arg, true);
10089     },
10090     
10091     // private
10092     update : function(params, records){
10093         
10094     }
10095 });/*
10096  * Based on:
10097  * Ext JS Library 1.1.1
10098  * Copyright(c) 2006-2007, Ext JS, LLC.
10099  *
10100  * Originally Released Under LGPL - original licence link has changed is not relivant.
10101  *
10102  * Fork - LGPL
10103  * <script type="text/javascript">
10104  */
10105 /**
10106  * @class Roo.data.HttpProxy
10107  * @extends Roo.data.DataProxy
10108  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10109  * configured to reference a certain URL.<br><br>
10110  * <p>
10111  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10112  * from which the running page was served.<br><br>
10113  * <p>
10114  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10115  * <p>
10116  * Be aware that to enable the browser to parse an XML document, the server must set
10117  * the Content-Type header in the HTTP response to "text/xml".
10118  * @constructor
10119  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10120  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10121  * will be used to make the request.
10122  */
10123 Roo.data.HttpProxy = function(conn){
10124     Roo.data.HttpProxy.superclass.constructor.call(this);
10125     // is conn a conn config or a real conn?
10126     this.conn = conn;
10127     this.useAjax = !conn || !conn.events;
10128   
10129 };
10130
10131 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10132     // thse are take from connection...
10133     
10134     /**
10135      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10136      */
10137     /**
10138      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10139      * extra parameters to each request made by this object. (defaults to undefined)
10140      */
10141     /**
10142      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10143      *  to each request made by this object. (defaults to undefined)
10144      */
10145     /**
10146      * @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)
10147      */
10148     /**
10149      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10150      */
10151      /**
10152      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10153      * @type Boolean
10154      */
10155   
10156
10157     /**
10158      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10159      * @type Boolean
10160      */
10161     /**
10162      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10163      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10164      * a finer-grained basis than the DataProxy events.
10165      */
10166     getConnection : function(){
10167         return this.useAjax ? Roo.Ajax : this.conn;
10168     },
10169
10170     /**
10171      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10172      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10173      * process that block using the passed callback.
10174      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10175      * for the request to the remote server.
10176      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10177      * object into a block of Roo.data.Records.
10178      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10179      * The function must be passed <ul>
10180      * <li>The Record block object</li>
10181      * <li>The "arg" argument from the load function</li>
10182      * <li>A boolean success indicator</li>
10183      * </ul>
10184      * @param {Object} scope The scope in which to call the callback
10185      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10186      */
10187     load : function(params, reader, callback, scope, arg){
10188         if(this.fireEvent("beforeload", this, params) !== false){
10189             var  o = {
10190                 params : params || {},
10191                 request: {
10192                     callback : callback,
10193                     scope : scope,
10194                     arg : arg
10195                 },
10196                 reader: reader,
10197                 callback : this.loadResponse,
10198                 scope: this
10199             };
10200             if(this.useAjax){
10201                 Roo.applyIf(o, this.conn);
10202                 if(this.activeRequest){
10203                     Roo.Ajax.abort(this.activeRequest);
10204                 }
10205                 this.activeRequest = Roo.Ajax.request(o);
10206             }else{
10207                 this.conn.request(o);
10208             }
10209         }else{
10210             callback.call(scope||this, null, arg, false);
10211         }
10212     },
10213
10214     // private
10215     loadResponse : function(o, success, response){
10216         delete this.activeRequest;
10217         if(!success){
10218             this.fireEvent("loadexception", this, o, response);
10219             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10220             return;
10221         }
10222         var result;
10223         try {
10224             result = o.reader.read(response);
10225         }catch(e){
10226             this.fireEvent("loadexception", this, o, response, e);
10227             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10228             return;
10229         }
10230         
10231         this.fireEvent("load", this, o, o.request.arg);
10232         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10233     },
10234
10235     // private
10236     update : function(dataSet){
10237
10238     },
10239
10240     // private
10241     updateResponse : function(dataSet){
10242
10243     }
10244 });/*
10245  * Based on:
10246  * Ext JS Library 1.1.1
10247  * Copyright(c) 2006-2007, Ext JS, LLC.
10248  *
10249  * Originally Released Under LGPL - original licence link has changed is not relivant.
10250  *
10251  * Fork - LGPL
10252  * <script type="text/javascript">
10253  */
10254
10255 /**
10256  * @class Roo.data.ScriptTagProxy
10257  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10258  * other than the originating domain of the running page.<br><br>
10259  * <p>
10260  * <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
10261  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10262  * <p>
10263  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10264  * source code that is used as the source inside a &lt;script> tag.<br><br>
10265  * <p>
10266  * In order for the browser to process the returned data, the server must wrap the data object
10267  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10268  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10269  * depending on whether the callback name was passed:
10270  * <p>
10271  * <pre><code>
10272 boolean scriptTag = false;
10273 String cb = request.getParameter("callback");
10274 if (cb != null) {
10275     scriptTag = true;
10276     response.setContentType("text/javascript");
10277 } else {
10278     response.setContentType("application/x-json");
10279 }
10280 Writer out = response.getWriter();
10281 if (scriptTag) {
10282     out.write(cb + "(");
10283 }
10284 out.print(dataBlock.toJsonString());
10285 if (scriptTag) {
10286     out.write(");");
10287 }
10288 </pre></code>
10289  *
10290  * @constructor
10291  * @param {Object} config A configuration object.
10292  */
10293 Roo.data.ScriptTagProxy = function(config){
10294     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10295     Roo.apply(this, config);
10296     this.head = document.getElementsByTagName("head")[0];
10297 };
10298
10299 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10300
10301 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10302     /**
10303      * @cfg {String} url The URL from which to request the data object.
10304      */
10305     /**
10306      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10307      */
10308     timeout : 30000,
10309     /**
10310      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10311      * the server the name of the callback function set up by the load call to process the returned data object.
10312      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10313      * javascript output which calls this named function passing the data object as its only parameter.
10314      */
10315     callbackParam : "callback",
10316     /**
10317      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10318      * name to the request.
10319      */
10320     nocache : true,
10321
10322     /**
10323      * Load data from the configured URL, read the data object into
10324      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10325      * process that block using the passed callback.
10326      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10327      * for the request to the remote server.
10328      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10329      * object into a block of Roo.data.Records.
10330      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10331      * The function must be passed <ul>
10332      * <li>The Record block object</li>
10333      * <li>The "arg" argument from the load function</li>
10334      * <li>A boolean success indicator</li>
10335      * </ul>
10336      * @param {Object} scope The scope in which to call the callback
10337      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10338      */
10339     load : function(params, reader, callback, scope, arg){
10340         if(this.fireEvent("beforeload", this, params) !== false){
10341
10342             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10343
10344             var url = this.url;
10345             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10346             if(this.nocache){
10347                 url += "&_dc=" + (new Date().getTime());
10348             }
10349             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10350             var trans = {
10351                 id : transId,
10352                 cb : "stcCallback"+transId,
10353                 scriptId : "stcScript"+transId,
10354                 params : params,
10355                 arg : arg,
10356                 url : url,
10357                 callback : callback,
10358                 scope : scope,
10359                 reader : reader
10360             };
10361             var conn = this;
10362
10363             window[trans.cb] = function(o){
10364                 conn.handleResponse(o, trans);
10365             };
10366
10367             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10368
10369             if(this.autoAbort !== false){
10370                 this.abort();
10371             }
10372
10373             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10374
10375             var script = document.createElement("script");
10376             script.setAttribute("src", url);
10377             script.setAttribute("type", "text/javascript");
10378             script.setAttribute("id", trans.scriptId);
10379             this.head.appendChild(script);
10380
10381             this.trans = trans;
10382         }else{
10383             callback.call(scope||this, null, arg, false);
10384         }
10385     },
10386
10387     // private
10388     isLoading : function(){
10389         return this.trans ? true : false;
10390     },
10391
10392     /**
10393      * Abort the current server request.
10394      */
10395     abort : function(){
10396         if(this.isLoading()){
10397             this.destroyTrans(this.trans);
10398         }
10399     },
10400
10401     // private
10402     destroyTrans : function(trans, isLoaded){
10403         this.head.removeChild(document.getElementById(trans.scriptId));
10404         clearTimeout(trans.timeoutId);
10405         if(isLoaded){
10406             window[trans.cb] = undefined;
10407             try{
10408                 delete window[trans.cb];
10409             }catch(e){}
10410         }else{
10411             // if hasn't been loaded, wait for load to remove it to prevent script error
10412             window[trans.cb] = function(){
10413                 window[trans.cb] = undefined;
10414                 try{
10415                     delete window[trans.cb];
10416                 }catch(e){}
10417             };
10418         }
10419     },
10420
10421     // private
10422     handleResponse : function(o, trans){
10423         this.trans = false;
10424         this.destroyTrans(trans, true);
10425         var result;
10426         try {
10427             result = trans.reader.readRecords(o);
10428         }catch(e){
10429             this.fireEvent("loadexception", this, o, trans.arg, e);
10430             trans.callback.call(trans.scope||window, null, trans.arg, false);
10431             return;
10432         }
10433         this.fireEvent("load", this, o, trans.arg);
10434         trans.callback.call(trans.scope||window, result, trans.arg, true);
10435     },
10436
10437     // private
10438     handleFailure : function(trans){
10439         this.trans = false;
10440         this.destroyTrans(trans, false);
10441         this.fireEvent("loadexception", this, null, trans.arg);
10442         trans.callback.call(trans.scope||window, null, trans.arg, false);
10443     }
10444 });/*
10445  * Based on:
10446  * Ext JS Library 1.1.1
10447  * Copyright(c) 2006-2007, Ext JS, LLC.
10448  *
10449  * Originally Released Under LGPL - original licence link has changed is not relivant.
10450  *
10451  * Fork - LGPL
10452  * <script type="text/javascript">
10453  */
10454
10455 /**
10456  * @class Roo.data.JsonReader
10457  * @extends Roo.data.DataReader
10458  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10459  * based on mappings in a provided Roo.data.Record constructor.
10460  * 
10461  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10462  * in the reply previously. 
10463  * 
10464  * <p>
10465  * Example code:
10466  * <pre><code>
10467 var RecordDef = Roo.data.Record.create([
10468     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10469     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10470 ]);
10471 var myReader = new Roo.data.JsonReader({
10472     totalProperty: "results",    // The property which contains the total dataset size (optional)
10473     root: "rows",                // The property which contains an Array of row objects
10474     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10475 }, RecordDef);
10476 </code></pre>
10477  * <p>
10478  * This would consume a JSON file like this:
10479  * <pre><code>
10480 { 'results': 2, 'rows': [
10481     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10482     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10483 }
10484 </code></pre>
10485  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10486  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10487  * paged from the remote server.
10488  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10489  * @cfg {String} root name of the property which contains the Array of row objects.
10490  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10491  * @constructor
10492  * Create a new JsonReader
10493  * @param {Object} meta Metadata configuration options
10494  * @param {Object} recordType Either an Array of field definition objects,
10495  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10496  */
10497 Roo.data.JsonReader = function(meta, recordType){
10498     
10499     meta = meta || {};
10500     // set some defaults:
10501     Roo.applyIf(meta, {
10502         totalProperty: 'total',
10503         successProperty : 'success',
10504         root : 'data',
10505         id : 'id'
10506     });
10507     
10508     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10509 };
10510 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10511     
10512     /**
10513      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10514      * Used by Store query builder to append _requestMeta to params.
10515      * 
10516      */
10517     metaFromRemote : false,
10518     /**
10519      * This method is only used by a DataProxy which has retrieved data from a remote server.
10520      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10521      * @return {Object} data A data block which is used by an Roo.data.Store object as
10522      * a cache of Roo.data.Records.
10523      */
10524     read : function(response){
10525         var json = response.responseText;
10526        
10527         var o = /* eval:var:o */ eval("("+json+")");
10528         if(!o) {
10529             throw {message: "JsonReader.read: Json object not found"};
10530         }
10531         
10532         if(o.metaData){
10533             
10534             delete this.ef;
10535             this.metaFromRemote = true;
10536             this.meta = o.metaData;
10537             this.recordType = Roo.data.Record.create(o.metaData.fields);
10538             this.onMetaChange(this.meta, this.recordType, o);
10539         }
10540         return this.readRecords(o);
10541     },
10542
10543     // private function a store will implement
10544     onMetaChange : function(meta, recordType, o){
10545
10546     },
10547
10548     /**
10549          * @ignore
10550          */
10551     simpleAccess: function(obj, subsc) {
10552         return obj[subsc];
10553     },
10554
10555         /**
10556          * @ignore
10557          */
10558     getJsonAccessor: function(){
10559         var re = /[\[\.]/;
10560         return function(expr) {
10561             try {
10562                 return(re.test(expr))
10563                     ? new Function("obj", "return obj." + expr)
10564                     : function(obj){
10565                         return obj[expr];
10566                     };
10567             } catch(e){}
10568             return Roo.emptyFn;
10569         };
10570     }(),
10571
10572     /**
10573      * Create a data block containing Roo.data.Records from an XML document.
10574      * @param {Object} o An object which contains an Array of row objects in the property specified
10575      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10576      * which contains the total size of the dataset.
10577      * @return {Object} data A data block which is used by an Roo.data.Store object as
10578      * a cache of Roo.data.Records.
10579      */
10580     readRecords : function(o){
10581         /**
10582          * After any data loads, the raw JSON data is available for further custom processing.
10583          * @type Object
10584          */
10585         this.o = o;
10586         var s = this.meta, Record = this.recordType,
10587             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10588
10589 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10590         if (!this.ef) {
10591             if(s.totalProperty) {
10592                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10593                 }
10594                 if(s.successProperty) {
10595                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10596                 }
10597                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10598                 if (s.id) {
10599                         var g = this.getJsonAccessor(s.id);
10600                         this.getId = function(rec) {
10601                                 var r = g(rec);  
10602                                 return (r === undefined || r === "") ? null : r;
10603                         };
10604                 } else {
10605                         this.getId = function(){return null;};
10606                 }
10607             this.ef = [];
10608             for(var jj = 0; jj < fl; jj++){
10609                 f = fi[jj];
10610                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10611                 this.ef[jj] = this.getJsonAccessor(map);
10612             }
10613         }
10614
10615         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10616         if(s.totalProperty){
10617             var vt = parseInt(this.getTotal(o), 10);
10618             if(!isNaN(vt)){
10619                 totalRecords = vt;
10620             }
10621         }
10622         if(s.successProperty){
10623             var vs = this.getSuccess(o);
10624             if(vs === false || vs === 'false'){
10625                 success = false;
10626             }
10627         }
10628         var records = [];
10629         for(var i = 0; i < c; i++){
10630                 var n = root[i];
10631             var values = {};
10632             var id = this.getId(n);
10633             for(var j = 0; j < fl; j++){
10634                 f = fi[j];
10635             var v = this.ef[j](n);
10636             if (!f.convert) {
10637                 Roo.log('missing convert for ' + f.name);
10638                 Roo.log(f);
10639                 continue;
10640             }
10641             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10642             }
10643             var record = new Record(values, id);
10644             record.json = n;
10645             records[i] = record;
10646         }
10647         return {
10648             raw : o,
10649             success : success,
10650             records : records,
10651             totalRecords : totalRecords
10652         };
10653     }
10654 });/*
10655  * Based on:
10656  * Ext JS Library 1.1.1
10657  * Copyright(c) 2006-2007, Ext JS, LLC.
10658  *
10659  * Originally Released Under LGPL - original licence link has changed is not relivant.
10660  *
10661  * Fork - LGPL
10662  * <script type="text/javascript">
10663  */
10664
10665 /**
10666  * @class Roo.data.ArrayReader
10667  * @extends Roo.data.DataReader
10668  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10669  * Each element of that Array represents a row of data fields. The
10670  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10671  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10672  * <p>
10673  * Example code:.
10674  * <pre><code>
10675 var RecordDef = Roo.data.Record.create([
10676     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10677     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10678 ]);
10679 var myReader = new Roo.data.ArrayReader({
10680     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10681 }, RecordDef);
10682 </code></pre>
10683  * <p>
10684  * This would consume an Array like this:
10685  * <pre><code>
10686 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10687   </code></pre>
10688  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10689  * @constructor
10690  * Create a new JsonReader
10691  * @param {Object} meta Metadata configuration options.
10692  * @param {Object} recordType Either an Array of field definition objects
10693  * as specified to {@link Roo.data.Record#create},
10694  * or an {@link Roo.data.Record} object
10695  * created using {@link Roo.data.Record#create}.
10696  */
10697 Roo.data.ArrayReader = function(meta, recordType){
10698     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10699 };
10700
10701 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10702     /**
10703      * Create a data block containing Roo.data.Records from an XML document.
10704      * @param {Object} o An Array of row objects which represents the dataset.
10705      * @return {Object} data A data block which is used by an Roo.data.Store object as
10706      * a cache of Roo.data.Records.
10707      */
10708     readRecords : function(o){
10709         var sid = this.meta ? this.meta.id : null;
10710         var recordType = this.recordType, fields = recordType.prototype.fields;
10711         var records = [];
10712         var root = o;
10713             for(var i = 0; i < root.length; i++){
10714                     var n = root[i];
10715                 var values = {};
10716                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10717                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10718                 var f = fields.items[j];
10719                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10720                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10721                 v = f.convert(v);
10722                 values[f.name] = v;
10723             }
10724                 var record = new recordType(values, id);
10725                 record.json = n;
10726                 records[records.length] = record;
10727             }
10728             return {
10729                 records : records,
10730                 totalRecords : records.length
10731             };
10732     }
10733 });/*
10734  * - LGPL
10735  * * 
10736  */
10737
10738 /**
10739  * @class Roo.bootstrap.ComboBox
10740  * @extends Roo.bootstrap.TriggerField
10741  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10742  * @cfg {Boolean} append (true|false) default false
10743  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10744  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10745  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10746  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10747  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10748  * @cfg {Boolean} anyMatch (true|false) any match when filter default false
10749  * @constructor
10750  * Create a new ComboBox.
10751  * @param {Object} config Configuration options
10752  */
10753 Roo.bootstrap.ComboBox = function(config){
10754     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10755     this.addEvents({
10756         /**
10757          * @event expand
10758          * Fires when the dropdown list is expanded
10759              * @param {Roo.bootstrap.ComboBox} combo This combo box
10760              */
10761         'expand' : true,
10762         /**
10763          * @event collapse
10764          * Fires when the dropdown list is collapsed
10765              * @param {Roo.bootstrap.ComboBox} combo This combo box
10766              */
10767         'collapse' : true,
10768         /**
10769          * @event beforeselect
10770          * Fires before a list item is selected. Return false to cancel the selection.
10771              * @param {Roo.bootstrap.ComboBox} combo This combo box
10772              * @param {Roo.data.Record} record The data record returned from the underlying store
10773              * @param {Number} index The index of the selected item in the dropdown list
10774              */
10775         'beforeselect' : true,
10776         /**
10777          * @event select
10778          * Fires when a list item is selected
10779              * @param {Roo.bootstrap.ComboBox} combo This combo box
10780              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10781              * @param {Number} index The index of the selected item in the dropdown list
10782              */
10783         'select' : true,
10784         /**
10785          * @event beforequery
10786          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10787          * The event object passed has these properties:
10788              * @param {Roo.bootstrap.ComboBox} combo This combo box
10789              * @param {String} query The query
10790              * @param {Boolean} forceAll true to force "all" query
10791              * @param {Boolean} cancel true to cancel the query
10792              * @param {Object} e The query event object
10793              */
10794         'beforequery': true,
10795          /**
10796          * @event add
10797          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10798              * @param {Roo.bootstrap.ComboBox} combo This combo box
10799              */
10800         'add' : true,
10801         /**
10802          * @event edit
10803          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10804              * @param {Roo.bootstrap.ComboBox} combo This combo box
10805              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10806              */
10807         'edit' : true,
10808         /**
10809          * @event remove
10810          * Fires when the remove value from the combobox array
10811              * @param {Roo.bootstrap.ComboBox} combo This combo box
10812              */
10813         'remove' : true
10814         
10815     });
10816     
10817     this.item = [];
10818     this.tickItems = [];
10819     
10820     this.selectedIndex = -1;
10821     if(this.mode == 'local'){
10822         if(config.queryDelay === undefined){
10823             this.queryDelay = 10;
10824         }
10825         if(config.minChars === undefined){
10826             this.minChars = 0;
10827         }
10828     }
10829 };
10830
10831 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10832      
10833     /**
10834      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10835      * rendering into an Roo.Editor, defaults to false)
10836      */
10837     /**
10838      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10839      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10840      */
10841     /**
10842      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10843      */
10844     /**
10845      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10846      * the dropdown list (defaults to undefined, with no header element)
10847      */
10848
10849      /**
10850      * @cfg {String/Roo.Template} tpl The template to use to render the output
10851      */
10852      
10853      /**
10854      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10855      */
10856     listWidth: undefined,
10857     /**
10858      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10859      * mode = 'remote' or 'text' if mode = 'local')
10860      */
10861     displayField: undefined,
10862     
10863     /**
10864      * @cfg {Array} filterField The filter field name to bind to this CombBox (defaults to undefined if
10865      */
10866     filterField: undefined,
10867     
10868     /**
10869      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10870      * mode = 'remote' or 'value' if mode = 'local'). 
10871      * Note: use of a valueField requires the user make a selection
10872      * in order for a value to be mapped.
10873      */
10874     valueField: undefined,
10875     
10876     
10877     /**
10878      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10879      * field's data value (defaults to the underlying DOM element's name)
10880      */
10881     hiddenName: undefined,
10882     /**
10883      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10884      */
10885     listClass: '',
10886     /**
10887      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10888      */
10889     selectedClass: 'active',
10890     
10891     /**
10892      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10893      */
10894     shadow:'sides',
10895     /**
10896      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10897      * anchor positions (defaults to 'tl-bl')
10898      */
10899     listAlign: 'tl-bl?',
10900     /**
10901      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10902      */
10903     maxHeight: 300,
10904     /**
10905      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10906      * query specified by the allQuery config option (defaults to 'query')
10907      */
10908     triggerAction: 'query',
10909     /**
10910      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10911      * (defaults to 4, does not apply if editable = false)
10912      */
10913     minChars : 4,
10914     /**
10915      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10916      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10917      */
10918     typeAhead: false,
10919     /**
10920      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10921      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10922      */
10923     queryDelay: 500,
10924     /**
10925      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10926      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10927      */
10928     pageSize: 0,
10929     /**
10930      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10931      * when editable = true (defaults to false)
10932      */
10933     selectOnFocus:false,
10934     /**
10935      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10936      */
10937     queryParam: 'query',
10938     /**
10939      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10940      * when mode = 'remote' (defaults to 'Loading...')
10941      */
10942     loadingText: 'Loading...',
10943     /**
10944      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10945      */
10946     resizable: false,
10947     /**
10948      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10949      */
10950     handleHeight : 8,
10951     /**
10952      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10953      * traditional select (defaults to true)
10954      */
10955     editable: true,
10956     /**
10957      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10958      */
10959     allQuery: '',
10960     /**
10961      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10962      */
10963     mode: 'remote',
10964     /**
10965      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10966      * listWidth has a higher value)
10967      */
10968     minListWidth : 70,
10969     /**
10970      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10971      * allow the user to set arbitrary text into the field (defaults to false)
10972      */
10973     forceSelection:false,
10974     /**
10975      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10976      * if typeAhead = true (defaults to 250)
10977      */
10978     typeAheadDelay : 250,
10979     /**
10980      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10981      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10982      */
10983     valueNotFoundText : undefined,
10984     /**
10985      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10986      */
10987     blockFocus : false,
10988     
10989     /**
10990      * @cfg {Boolean} disableClear Disable showing of clear button.
10991      */
10992     disableClear : false,
10993     /**
10994      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10995      */
10996     alwaysQuery : false,
10997     
10998     /**
10999      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11000      */
11001     multiple : false,
11002     
11003     /**
11004      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11005      */
11006     invalidClass : "has-warning",
11007     
11008     /**
11009      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11010      */
11011     validClass : "has-success",
11012     
11013     /**
11014      * @cfg {Boolean} filterSort (true|false) sort the filter result default false
11015      */
11016     filterSort : false,
11017     
11018     /**
11019      * @cfg {String} filterSortDir (ASC|DESC) dir of sort the filter result default ASC
11020      */
11021     filterSortDir : 'ASC',
11022     
11023     //private
11024     addicon : false,
11025     editicon: false,
11026     
11027     page: 0,
11028     hasQuery: false,
11029     append: false,
11030     loadNext: false,
11031     autoFocus : true,
11032     tickable : false,
11033     btnPosition : 'right',
11034     triggerList : true,
11035     showToggleBtn : true,
11036     anyMatch : false,
11037     // element that contains real text value.. (when hidden is used..)
11038     
11039     getAutoCreate : function()
11040     {
11041         var cfg = false;
11042         
11043         /*
11044          *  Normal ComboBox
11045          */
11046         if(!this.tickable){
11047             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11048             return cfg;
11049         }
11050         
11051         /*
11052          *  ComboBox with tickable selections
11053          */
11054              
11055         var align = this.labelAlign || this.parentLabelAlign();
11056         
11057         cfg = {
11058             cls : 'form-group roo-combobox-tickable' //input-group
11059         };
11060         
11061         var buttons = {
11062             tag : 'div',
11063             cls : 'tickable-buttons',
11064             cn : [
11065                 {
11066                     tag : 'button',
11067                     type : 'button',
11068                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11069                     html : 'Edit'
11070                 },
11071                 {
11072                     tag : 'button',
11073                     type : 'button',
11074                     name : 'ok',
11075                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11076                     html : 'Done'
11077                 },
11078                 {
11079                     tag : 'button',
11080                     type : 'button',
11081                     name : 'cancel',
11082                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11083                     html : 'Cancel'
11084                 }
11085             ]
11086         };
11087         
11088         if(this.editable){
11089             buttons.cn.unshift({
11090                 tag: 'input',
11091                 cls: 'select2-search-field-input'
11092             });
11093         }
11094         
11095         var _this = this;
11096         
11097         Roo.each(buttons.cn, function(c){
11098             if (_this.size) {
11099                 c.cls += ' btn-' + _this.size;
11100             }
11101
11102             if (_this.disabled) {
11103                 c.disabled = true;
11104             }
11105         });
11106         
11107         var box = {
11108             tag: 'div',
11109             cn: [
11110                 {
11111                     tag: 'input',
11112                     type : 'hidden',
11113                     cls: 'form-hidden-field'
11114                 },
11115                 {
11116                     tag: 'ul',
11117                     cls: 'select2-choices',
11118                     cn:[
11119                         {
11120                             tag: 'li',
11121                             cls: 'select2-search-field',
11122                             cn: [
11123
11124                                 buttons
11125                             ]
11126                         }
11127                     ]
11128                 }
11129             ]
11130         }
11131         
11132         var combobox = {
11133             cls: 'select2-container input-group select2-container-multi',
11134             cn: [
11135                 box
11136 //                {
11137 //                    tag: 'ul',
11138 //                    cls: 'typeahead typeahead-long dropdown-menu',
11139 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11140 //                }
11141             ]
11142         };
11143         
11144         if(this.hasFeedback && !this.allowBlank){
11145             
11146             var feedback = {
11147                 tag: 'span',
11148                 cls: 'glyphicon form-control-feedback'
11149             };
11150
11151             combobox.cn.push(feedback);
11152         }
11153         
11154         if (align ==='left' && this.fieldLabel.length) {
11155             
11156                 Roo.log("left and has label");
11157                 cfg.cn = [
11158                     
11159                     {
11160                         tag: 'label',
11161                         'for' :  id,
11162                         cls : 'control-label col-sm-' + this.labelWidth,
11163                         html : this.fieldLabel
11164                         
11165                     },
11166                     {
11167                         cls : "col-sm-" + (12 - this.labelWidth), 
11168                         cn: [
11169                             combobox
11170                         ]
11171                     }
11172                     
11173                 ];
11174         } else if ( this.fieldLabel.length) {
11175                 Roo.log(" label");
11176                  cfg.cn = [
11177                    
11178                     {
11179                         tag: 'label',
11180                         //cls : 'input-group-addon',
11181                         html : this.fieldLabel
11182                         
11183                     },
11184                     
11185                     combobox
11186                     
11187                 ];
11188
11189         } else {
11190             
11191                 Roo.log(" no label && no align");
11192                 cfg = combobox
11193                      
11194                 
11195         }
11196          
11197         var settings=this;
11198         ['xs','sm','md','lg'].map(function(size){
11199             if (settings[size]) {
11200                 cfg.cls += ' col-' + size + '-' + settings[size];
11201             }
11202         });
11203         
11204         return cfg;
11205         
11206     },
11207     
11208     // private
11209     initEvents: function()
11210     {
11211         
11212         if (!this.store) {
11213             throw "can not find store for combo";
11214         }
11215         this.store = Roo.factory(this.store, Roo.data);
11216         
11217         if(this.tickable){
11218             this.initTickableEvents();
11219             return;
11220         }
11221         
11222         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11223         
11224         if(this.hiddenName){
11225             
11226             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11227             
11228             this.hiddenField.dom.value =
11229                 this.hiddenValue !== undefined ? this.hiddenValue :
11230                 this.value !== undefined ? this.value : '';
11231
11232             // prevent input submission
11233             this.el.dom.removeAttribute('name');
11234             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11235              
11236              
11237         }
11238         //if(Roo.isGecko){
11239         //    this.el.dom.setAttribute('autocomplete', 'off');
11240         //}
11241         
11242         var cls = 'x-combo-list';
11243         
11244         //this.list = new Roo.Layer({
11245         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11246         //});
11247         
11248         var _this = this;
11249         
11250         (function(){
11251             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11252             _this.list.setWidth(lw);
11253         }).defer(100);
11254         
11255         this.list.on('mouseover', this.onViewOver, this);
11256         this.list.on('mousemove', this.onViewMove, this);
11257         
11258         this.list.on('scroll', this.onViewScroll, this);
11259         
11260         /*
11261         this.list.swallowEvent('mousewheel');
11262         this.assetHeight = 0;
11263
11264         if(this.title){
11265             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11266             this.assetHeight += this.header.getHeight();
11267         }
11268
11269         this.innerList = this.list.createChild({cls:cls+'-inner'});
11270         this.innerList.on('mouseover', this.onViewOver, this);
11271         this.innerList.on('mousemove', this.onViewMove, this);
11272         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11273         
11274         if(this.allowBlank && !this.pageSize && !this.disableClear){
11275             this.footer = this.list.createChild({cls:cls+'-ft'});
11276             this.pageTb = new Roo.Toolbar(this.footer);
11277            
11278         }
11279         if(this.pageSize){
11280             this.footer = this.list.createChild({cls:cls+'-ft'});
11281             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11282                     {pageSize: this.pageSize});
11283             
11284         }
11285         
11286         if (this.pageTb && this.allowBlank && !this.disableClear) {
11287             var _this = this;
11288             this.pageTb.add(new Roo.Toolbar.Fill(), {
11289                 cls: 'x-btn-icon x-btn-clear',
11290                 text: '&#160;',
11291                 handler: function()
11292                 {
11293                     _this.collapse();
11294                     _this.clearValue();
11295                     _this.onSelect(false, -1);
11296                 }
11297             });
11298         }
11299         if (this.footer) {
11300             this.assetHeight += this.footer.getHeight();
11301         }
11302         */
11303             
11304         if(!this.tpl){
11305             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11306         }
11307
11308         this.view = new Roo.View(this.list, this.tpl, {
11309             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11310         });
11311         //this.view.wrapEl.setDisplayed(false);
11312         this.view.on('click', this.onViewClick, this);
11313         
11314         
11315         
11316         this.store.on('beforeload', this.onBeforeLoad, this);
11317         this.store.on('load', this.onLoad, this);
11318         this.store.on('loadexception', this.onLoadException, this);
11319         /*
11320         if(this.resizable){
11321             this.resizer = new Roo.Resizable(this.list,  {
11322                pinned:true, handles:'se'
11323             });
11324             this.resizer.on('resize', function(r, w, h){
11325                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11326                 this.listWidth = w;
11327                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11328                 this.restrictHeight();
11329             }, this);
11330             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11331         }
11332         */
11333         if(!this.editable){
11334             this.editable = true;
11335             this.setEditable(false);
11336         }
11337         
11338         /*
11339         
11340         if (typeof(this.events.add.listeners) != 'undefined') {
11341             
11342             this.addicon = this.wrap.createChild(
11343                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11344        
11345             this.addicon.on('click', function(e) {
11346                 this.fireEvent('add', this);
11347             }, this);
11348         }
11349         if (typeof(this.events.edit.listeners) != 'undefined') {
11350             
11351             this.editicon = this.wrap.createChild(
11352                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11353             if (this.addicon) {
11354                 this.editicon.setStyle('margin-left', '40px');
11355             }
11356             this.editicon.on('click', function(e) {
11357                 
11358                 // we fire even  if inothing is selected..
11359                 this.fireEvent('edit', this, this.lastData );
11360                 
11361             }, this);
11362         }
11363         */
11364         
11365         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11366             "up" : function(e){
11367                 this.inKeyMode = true;
11368                 this.selectPrev();
11369             },
11370
11371             "down" : function(e){
11372                 if(!this.isExpanded()){
11373                     this.onTriggerClick();
11374                 }else{
11375                     this.inKeyMode = true;
11376                     this.selectNext();
11377                 }
11378             },
11379
11380             "enter" : function(e){
11381 //                this.onViewClick();
11382                 //return true;
11383                 this.collapse();
11384                 
11385                 if(this.fireEvent("specialkey", this, e)){
11386                     this.onViewClick(false);
11387                 }
11388                 
11389                 return true;
11390             },
11391
11392             "esc" : function(e){
11393                 this.collapse();
11394             },
11395
11396             "tab" : function(e){
11397                 this.collapse();
11398                 
11399                 if(this.fireEvent("specialkey", this, e)){
11400                     this.onViewClick(false);
11401                 }
11402                 
11403                 return true;
11404             },
11405
11406             scope : this,
11407
11408             doRelay : function(foo, bar, hname){
11409                 if(hname == 'down' || this.scope.isExpanded()){
11410                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11411                 }
11412                 return true;
11413             },
11414
11415             forceKeyDown: true
11416         });
11417         
11418         
11419         this.queryDelay = Math.max(this.queryDelay || 10,
11420                 this.mode == 'local' ? 10 : 250);
11421         
11422         
11423         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11424         
11425         if(this.typeAhead){
11426             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11427         }
11428         if(this.editable !== false){
11429             this.inputEl().on("keyup", this.onKeyUp, this);
11430         }
11431         if(this.forceSelection){
11432             this.inputEl().on('blur', this.doForce, this);
11433         }
11434         
11435         if(this.multiple){
11436             this.choices = this.el.select('ul.select2-choices', true).first();
11437             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11438         }
11439     },
11440     
11441     initTickableEvents: function()
11442     {   
11443         this.createList();
11444         
11445         if(this.hiddenName){
11446             
11447             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11448             
11449             this.hiddenField.dom.value =
11450                 this.hiddenValue !== undefined ? this.hiddenValue :
11451                 this.value !== undefined ? this.value : '';
11452
11453             // prevent input submission
11454             this.el.dom.removeAttribute('name');
11455             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11456              
11457              
11458         }
11459         
11460 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11461         
11462         this.choices = this.el.select('ul.select2-choices', true).first();
11463         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11464         if(this.triggerList){
11465             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11466         }
11467          
11468         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11469         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11470         
11471         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11472         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11473         
11474         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11475         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11476         
11477         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11478         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11479         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11480         
11481         this.okBtn.hide();
11482         this.cancelBtn.hide();
11483         
11484         var _this = this;
11485         
11486         (function(){
11487             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11488             _this.list.setWidth(lw);
11489         }).defer(100);
11490         
11491         this.list.on('mouseover', this.onViewOver, this);
11492         this.list.on('mousemove', this.onViewMove, this);
11493         
11494         this.list.on('scroll', this.onViewScroll, this);
11495         
11496         if(!this.tpl){
11497             this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
11498         }
11499
11500         this.view = new Roo.View(this.list, this.tpl, {
11501             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11502         });
11503         
11504         //this.view.wrapEl.setDisplayed(false);
11505         this.view.on('click', this.onViewClick, this);
11506         
11507         
11508         
11509         this.store.on('beforeload', this.onBeforeLoad, this);
11510         this.store.on('load', this.onLoad, this);
11511         this.store.on('loadexception', this.onLoadException, this);
11512         
11513         if(this.editable){
11514             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11515                 "up" : function(e){
11516                     this.inKeyMode = true;
11517                     this.selectPrev();
11518                 },
11519
11520                 "down" : function(e){
11521                     this.inKeyMode = true;
11522                     this.selectNext();
11523                 },
11524
11525                 "enter" : function(e){
11526                     if(this.fireEvent("specialkey", this, e)){
11527                         this.onViewClick(false);
11528                     }
11529                     
11530                     return true;
11531                 },
11532
11533                 "esc" : function(e){
11534                     this.onTickableFooterButtonClick(e, false, false);
11535                 },
11536
11537                 "tab" : function(e){
11538                     this.fireEvent("specialkey", this, e);
11539                     
11540                     this.onTickableFooterButtonClick(e, false, false);
11541                     
11542                     return true;
11543                 },
11544
11545                 scope : this,
11546
11547                 doRelay : function(e, fn, key){
11548                     if(this.scope.isExpanded()){
11549                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11550                     }
11551                     return true;
11552                 },
11553
11554                 forceKeyDown: true
11555             });
11556         }
11557         
11558         this.queryDelay = Math.max(this.queryDelay || 10,
11559                 this.mode == 'local' ? 10 : 250);
11560         
11561         
11562         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11563         
11564         if(this.typeAhead){
11565             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11566         }
11567         
11568         if(this.editable !== false){
11569             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11570         }
11571         
11572     },
11573
11574     onDestroy : function(){
11575         if(this.view){
11576             this.view.setStore(null);
11577             this.view.el.removeAllListeners();
11578             this.view.el.remove();
11579             this.view.purgeListeners();
11580         }
11581         if(this.list){
11582             this.list.dom.innerHTML  = '';
11583         }
11584         
11585         if(this.store){
11586             this.store.un('beforeload', this.onBeforeLoad, this);
11587             this.store.un('load', this.onLoad, this);
11588             this.store.un('loadexception', this.onLoadException, this);
11589         }
11590         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11591     },
11592
11593     // private
11594     fireKey : function(e){
11595         if(e.isNavKeyPress() && !this.list.isVisible()){
11596             this.fireEvent("specialkey", this, e);
11597         }
11598     },
11599
11600     // private
11601     onResize: function(w, h){
11602 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11603 //        
11604 //        if(typeof w != 'number'){
11605 //            // we do not handle it!?!?
11606 //            return;
11607 //        }
11608 //        var tw = this.trigger.getWidth();
11609 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11610 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11611 //        var x = w - tw;
11612 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11613 //            
11614 //        //this.trigger.setStyle('left', x+'px');
11615 //        
11616 //        if(this.list && this.listWidth === undefined){
11617 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11618 //            this.list.setWidth(lw);
11619 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11620 //        }
11621         
11622     
11623         
11624     },
11625
11626     /**
11627      * Allow or prevent the user from directly editing the field text.  If false is passed,
11628      * the user will only be able to select from the items defined in the dropdown list.  This method
11629      * is the runtime equivalent of setting the 'editable' config option at config time.
11630      * @param {Boolean} value True to allow the user to directly edit the field text
11631      */
11632     setEditable : function(value){
11633         if(value == this.editable){
11634             return;
11635         }
11636         this.editable = value;
11637         if(!value){
11638             this.inputEl().dom.setAttribute('readOnly', true);
11639             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11640             this.inputEl().addClass('x-combo-noedit');
11641         }else{
11642             this.inputEl().dom.setAttribute('readOnly', false);
11643             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11644             this.inputEl().removeClass('x-combo-noedit');
11645         }
11646     },
11647
11648     // private
11649     
11650     onBeforeLoad : function(combo,opts){
11651         if(!this.hasFocus){
11652             return;
11653         }
11654          if (!opts.add) {
11655             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11656          }
11657         this.restrictHeight();
11658         this.selectedIndex = -1;
11659     },
11660
11661     // private
11662     onLoad : function(){
11663         
11664         this.hasQuery = false;
11665         
11666         if(!this.hasFocus){
11667             return;
11668         }
11669         
11670         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11671             this.loading.hide();
11672         }
11673              
11674         if(this.store.getCount() > 0){
11675             this.expand();
11676             this.restrictHeight();
11677             if(this.lastQuery == this.allQuery){
11678                 if(this.editable && !this.tickable){
11679                     this.inputEl().dom.select();
11680                 }
11681                 
11682                 if(
11683                     !this.selectByValue(this.value, true) &&
11684                     this.autoFocus && 
11685                     (
11686                         !this.store.lastOptions ||
11687                         typeof(this.store.lastOptions.add) == 'undefined' || 
11688                         this.store.lastOptions.add != true
11689                     )
11690                 ){
11691                     this.select(0, true);
11692                 }
11693             }else{
11694                 if(this.autoFocus){
11695                     this.selectNext();
11696                 }
11697                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11698                     this.taTask.delay(this.typeAheadDelay);
11699                 }
11700             }
11701         }else{
11702             this.onEmptyResults();
11703         }
11704         
11705         //this.el.focus();
11706     },
11707     // private
11708     onLoadException : function()
11709     {
11710         this.hasQuery = false;
11711         
11712         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11713             this.loading.hide();
11714         }
11715         
11716         if(this.tickable && this.editable){
11717             return;
11718         }
11719         
11720         this.collapse();
11721         
11722         Roo.log(this.store.reader.jsonData);
11723         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11724             // fixme
11725             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11726         }
11727         
11728         
11729     },
11730     // private
11731     onTypeAhead : function(){
11732         if(this.store.getCount() > 0){
11733             var r = this.store.getAt(0);
11734             var newValue = r.data[this.displayField];
11735             var len = newValue.length;
11736             var selStart = this.getRawValue().length;
11737             
11738             if(selStart != len){
11739                 this.setRawValue(newValue);
11740                 this.selectText(selStart, newValue.length);
11741             }
11742         }
11743     },
11744
11745     // private
11746     onSelect : function(record, index){
11747         
11748         if(this.fireEvent('beforeselect', this, record, index) !== false){
11749         
11750             this.setFromData(index > -1 ? record.data : false);
11751             
11752             this.collapse();
11753             this.fireEvent('select', this, record, index);
11754         }
11755     },
11756
11757     /**
11758      * Returns the currently selected field value or empty string if no value is set.
11759      * @return {String} value The selected value
11760      */
11761     getValue : function(){
11762         
11763         if(this.multiple){
11764             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11765         }
11766         
11767         if(this.valueField){
11768             return typeof this.value != 'undefined' ? this.value : '';
11769         }else{
11770             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11771         }
11772     },
11773
11774     /**
11775      * Clears any text/value currently set in the field
11776      */
11777     clearValue : function(){
11778         if(this.hiddenField){
11779             this.hiddenField.dom.value = '';
11780         }
11781         this.value = '';
11782         this.setRawValue('');
11783         this.lastSelectionText = '';
11784         this.lastData = false;
11785         
11786     },
11787
11788     /**
11789      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11790      * will be displayed in the field.  If the value does not match the data value of an existing item,
11791      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11792      * Otherwise the field will be blank (although the value will still be set).
11793      * @param {String} value The value to match
11794      */
11795     setValue : function(v){
11796         if(this.multiple){
11797             this.syncValue();
11798             return;
11799         }
11800         
11801         var text = v;
11802         if(this.valueField){
11803             var r = this.findRecord(this.valueField, v);
11804             if(r){
11805                 text = r.data[this.displayField];
11806             }else if(this.valueNotFoundText !== undefined){
11807                 text = this.valueNotFoundText;
11808             }
11809         }
11810         this.lastSelectionText = text;
11811         if(this.hiddenField){
11812             this.hiddenField.dom.value = v;
11813         }
11814         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11815         this.value = v;
11816     },
11817     /**
11818      * @property {Object} the last set data for the element
11819      */
11820     
11821     lastData : false,
11822     /**
11823      * Sets the value of the field based on a object which is related to the record format for the store.
11824      * @param {Object} value the value to set as. or false on reset?
11825      */
11826     setFromData : function(o){
11827         
11828         if(this.multiple){
11829             this.addItem(o);
11830             return;
11831         }
11832             
11833         var dv = ''; // display value
11834         var vv = ''; // value value..
11835         this.lastData = o;
11836         if (this.displayField) {
11837             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11838         } else {
11839             // this is an error condition!!!
11840             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11841         }
11842         
11843         if(this.valueField){
11844             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11845         }
11846         
11847         if(this.hiddenField){
11848             this.hiddenField.dom.value = vv;
11849             
11850             this.lastSelectionText = dv;
11851             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11852             this.value = vv;
11853             return;
11854         }
11855         // no hidden field.. - we store the value in 'value', but still display
11856         // display field!!!!
11857         this.lastSelectionText = dv;
11858         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11859         this.value = vv;
11860         
11861         
11862     },
11863     // private
11864     reset : function(){
11865         // overridden so that last data is reset..
11866         
11867         if(this.multiple){
11868             this.clearItem();
11869             return;
11870         }
11871         
11872         this.setValue(this.originalValue);
11873         this.clearInvalid();
11874         this.lastData = false;
11875         if (this.view) {
11876             this.view.clearSelections();
11877         }
11878     },
11879     // private
11880     findRecord : function(prop, value){
11881         var record;
11882         if(this.store.getCount() > 0){
11883             this.store.each(function(r){
11884                 if(r.data[prop] == value){
11885                     record = r;
11886                     return false;
11887                 }
11888                 return true;
11889             });
11890         }
11891         return record;
11892     },
11893     
11894     getName: function()
11895     {
11896         // returns hidden if it's set..
11897         if (!this.rendered) {return ''};
11898         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11899         
11900     },
11901     // private
11902     onViewMove : function(e, t){
11903         this.inKeyMode = false;
11904     },
11905
11906     // private
11907     onViewOver : function(e, t){
11908         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11909             return;
11910         }
11911         var item = this.view.findItemFromChild(t);
11912         
11913         if(item){
11914             var index = this.view.indexOf(item);
11915             this.select(index, false);
11916         }
11917     },
11918
11919     // private
11920     onViewClick : function(view, doFocus, el, e)
11921     {
11922         var index = this.view.getSelectedIndexes()[0];
11923         
11924         var r = this.store.getAt(index);
11925         
11926         if(this.tickable){
11927             
11928             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
11929                 return;
11930             }
11931             
11932             var rm = false;
11933             var _this = this;
11934             
11935             Roo.each(this.tickItems, function(v,k){
11936                 
11937                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11938                     _this.tickItems.splice(k, 1);
11939                     
11940                     if(typeof(e) == 'undefined' && view == false){
11941                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
11942                     }
11943                     
11944                     rm = true;
11945                     return;
11946                 }
11947             });
11948             
11949             if(rm){
11950                 return;
11951             }
11952             
11953             this.tickItems.push(r.data);
11954             
11955             if(typeof(e) == 'undefined' && view == false){
11956                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
11957             }
11958                     
11959             return;
11960         }
11961         
11962         if(r){
11963             this.onSelect(r, index);
11964         }
11965         if(doFocus !== false && !this.blockFocus){
11966             this.inputEl().focus();
11967         }
11968     },
11969
11970     // private
11971     restrictHeight : function(){
11972         //this.innerList.dom.style.height = '';
11973         //var inner = this.innerList.dom;
11974         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11975         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11976         //this.list.beginUpdate();
11977         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11978         this.list.alignTo(this.inputEl(), this.listAlign);
11979         this.list.alignTo(this.inputEl(), this.listAlign);
11980         //this.list.endUpdate();
11981     },
11982
11983     // private
11984     onEmptyResults : function(){
11985         
11986         if(this.tickable && this.editable){
11987             this.restrictHeight();
11988             return;
11989         }
11990         
11991         this.collapse();
11992     },
11993
11994     /**
11995      * Returns true if the dropdown list is expanded, else false.
11996      */
11997     isExpanded : function(){
11998         return this.list.isVisible();
11999     },
12000
12001     /**
12002      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12003      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12004      * @param {String} value The data value of the item to select
12005      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12006      * selected item if it is not currently in view (defaults to true)
12007      * @return {Boolean} True if the value matched an item in the list, else false
12008      */
12009     selectByValue : function(v, scrollIntoView){
12010         if(v !== undefined && v !== null){
12011             var r = this.findRecord(this.valueField || this.displayField, v);
12012             if(r){
12013                 this.select(this.store.indexOf(r), scrollIntoView);
12014                 return true;
12015             }
12016         }
12017         return false;
12018     },
12019
12020     /**
12021      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12022      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12023      * @param {Number} index The zero-based index of the list item to select
12024      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12025      * selected item if it is not currently in view (defaults to true)
12026      */
12027     select : function(index, scrollIntoView){
12028         this.selectedIndex = index;
12029         this.view.select(index);
12030         if(scrollIntoView !== false){
12031             var el = this.view.getNode(index);
12032             /*
12033              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12034              */
12035             if(el){
12036                 this.list.scrollChildIntoView(el, false);
12037             }
12038         }
12039     },
12040
12041     // private
12042     selectNext : function(){
12043         var ct = this.store.getCount();
12044         if(ct > 0){
12045             if(this.selectedIndex == -1){
12046                 this.select(0);
12047             }else if(this.selectedIndex < ct-1){
12048                 this.select(this.selectedIndex+1);
12049             }
12050         }
12051     },
12052
12053     // private
12054     selectPrev : function(){
12055         var ct = this.store.getCount();
12056         if(ct > 0){
12057             if(this.selectedIndex == -1){
12058                 this.select(0);
12059             }else if(this.selectedIndex != 0){
12060                 this.select(this.selectedIndex-1);
12061             }
12062         }
12063     },
12064
12065     // private
12066     onKeyUp : function(e){
12067         if(this.editable !== false && !e.isSpecialKey()){
12068             this.lastKey = e.getKey();
12069             this.dqTask.delay(this.queryDelay);
12070         }
12071     },
12072
12073     // private
12074     validateBlur : function(){
12075         return !this.list || !this.list.isVisible();   
12076     },
12077
12078     // private
12079     initQuery : function(){
12080         
12081         var v = this.getRawValue();
12082         
12083         if(this.tickable && this.editable){
12084             v = this.tickableInputEl().getValue();
12085         }
12086         
12087         this.doQuery(v);
12088     },
12089
12090     // private
12091     doForce : function(){
12092         if(this.inputEl().dom.value.length > 0){
12093             this.inputEl().dom.value =
12094                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12095              
12096         }
12097     },
12098
12099     /**
12100      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12101      * query allowing the query action to be canceled if needed.
12102      * @param {String} query The SQL query to execute
12103      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12104      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12105      * saved in the current store (defaults to false)
12106      */
12107     doQuery : function(q, forceAll){
12108         
12109         if(q === undefined || q === null){
12110             q = '';
12111         }
12112         var qe = {
12113             query: q,
12114             forceAll: forceAll,
12115             combo: this,
12116             cancel:false
12117         };
12118         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12119             return false;
12120         }
12121         q = qe.query;
12122         
12123         forceAll = qe.forceAll;
12124         if(forceAll === true || (q.length >= this.minChars)){
12125             
12126             this.hasQuery = true;
12127             
12128             if(this.lastQuery != q || this.alwaysQuery){
12129                 this.lastQuery = q;
12130                 if(this.mode == 'local'){
12131                     this.selectedIndex = -1;
12132                     if(forceAll){
12133                         this.store.clearFilter();
12134                     }else{
12135                         this.store.filter(this.filterField || this.displayField, q, this.anyMatch);
12136                         
12137                         if(this.filterSort){
12138                             this.store.data.sort(this.displayField, this.filterSortDir);
12139                         }
12140                         
12141                     }
12142                     this.onLoad();
12143                 }else{
12144                     
12145                     this.store.baseParams[this.queryParam] = q;
12146                     
12147                     var options = {params : this.getParams(q)};
12148                     
12149                     if(this.loadNext){
12150                         options.add = true;
12151                         options.params.start = this.page * this.pageSize;
12152                     }
12153                     
12154                     this.store.load(options);
12155                     
12156                     /*
12157                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12158                      *  we should expand the list on onLoad
12159                      *  so command out it
12160                      */
12161 //                    this.expand();
12162                 }
12163             }else{
12164                 this.selectedIndex = -1;
12165                 this.onLoad();   
12166             }
12167         }
12168         
12169         this.loadNext = false;
12170     },
12171
12172     // private
12173     getParams : function(q){
12174         var p = {};
12175         //p[this.queryParam] = q;
12176         
12177         if(this.pageSize){
12178             p.start = 0;
12179             p.limit = this.pageSize;
12180         }
12181         return p;
12182     },
12183
12184     /**
12185      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12186      */
12187     collapse : function(){
12188         if(!this.isExpanded()){
12189             return;
12190         }
12191         
12192         this.list.hide();
12193         
12194         if(this.tickable){
12195             this.hasFocus = false;
12196             this.okBtn.hide();
12197             this.cancelBtn.hide();
12198             this.trigger.show();
12199             
12200             if(this.editable){
12201                 this.tickableInputEl().dom.value = '';
12202                 this.tickableInputEl().blur();
12203             }
12204             
12205         }
12206         
12207         Roo.get(document).un('mousedown', this.collapseIf, this);
12208         Roo.get(document).un('mousewheel', this.collapseIf, this);
12209         if (!this.editable) {
12210             Roo.get(document).un('keydown', this.listKeyPress, this);
12211         }
12212         this.fireEvent('collapse', this);
12213     },
12214
12215     // private
12216     collapseIf : function(e){
12217         var in_combo  = e.within(this.el);
12218         var in_list =  e.within(this.list);
12219         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12220         
12221         if (in_combo || in_list || is_list) {
12222             //e.stopPropagation();
12223             return;
12224         }
12225         
12226         if(this.tickable){
12227             this.onTickableFooterButtonClick(e, false, false);
12228         }
12229
12230         this.collapse();
12231         
12232     },
12233
12234     /**
12235      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12236      */
12237     expand : function(){
12238        
12239         if(this.isExpanded() || !this.hasFocus){
12240             return;
12241         }
12242         
12243         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12244         this.list.setWidth(lw);
12245         
12246         
12247          Roo.log('expand');
12248         
12249         this.list.show();
12250         
12251         this.restrictHeight();
12252         
12253         if(this.tickable){
12254             
12255             this.tickItems = Roo.apply([], this.item);
12256             
12257             this.okBtn.show();
12258             this.cancelBtn.show();
12259             this.trigger.hide();
12260             
12261             if(this.editable){
12262                 this.tickableInputEl().focus();
12263             }
12264             
12265         }
12266         
12267         Roo.get(document).on('mousedown', this.collapseIf, this);
12268         Roo.get(document).on('mousewheel', this.collapseIf, this);
12269         if (!this.editable) {
12270             Roo.get(document).on('keydown', this.listKeyPress, this);
12271         }
12272         
12273         this.fireEvent('expand', this);
12274     },
12275
12276     // private
12277     // Implements the default empty TriggerField.onTriggerClick function
12278     onTriggerClick : function(e)
12279     {
12280         Roo.log('trigger click');
12281         
12282         if(this.disabled || !this.triggerList){
12283             return;
12284         }
12285         
12286         this.page = 0;
12287         this.loadNext = false;
12288         
12289         if(this.isExpanded()){
12290             this.collapse();
12291             if (!this.blockFocus) {
12292                 this.inputEl().focus();
12293             }
12294             
12295         }else {
12296             this.hasFocus = true;
12297             if(this.triggerAction == 'all') {
12298                 this.doQuery(this.allQuery, true);
12299             } else {
12300                 this.doQuery(this.getRawValue());
12301             }
12302             if (!this.blockFocus) {
12303                 this.inputEl().focus();
12304             }
12305         }
12306     },
12307     
12308     onTickableTriggerClick : function(e)
12309     {
12310         if(this.disabled){
12311             return;
12312         }
12313         
12314         this.page = 0;
12315         this.loadNext = false;
12316         this.hasFocus = true;
12317         
12318         if(this.triggerAction == 'all') {
12319             this.doQuery(this.allQuery, true);
12320         } else {
12321             this.doQuery(this.getRawValue());
12322         }
12323     },
12324     
12325     onSearchFieldClick : function(e)
12326     {
12327         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12328             this.onTickableFooterButtonClick(e, false, false);
12329             return;
12330         }
12331         
12332         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12333             return;
12334         }
12335         
12336         this.page = 0;
12337         this.loadNext = false;
12338         this.hasFocus = true;
12339         
12340         if(this.triggerAction == 'all') {
12341             this.doQuery(this.allQuery, true);
12342         } else {
12343             this.doQuery(this.getRawValue());
12344         }
12345     },
12346     
12347     listKeyPress : function(e)
12348     {
12349         //Roo.log('listkeypress');
12350         // scroll to first matching element based on key pres..
12351         if (e.isSpecialKey()) {
12352             return false;
12353         }
12354         var k = String.fromCharCode(e.getKey()).toUpperCase();
12355         //Roo.log(k);
12356         var match  = false;
12357         var csel = this.view.getSelectedNodes();
12358         var cselitem = false;
12359         if (csel.length) {
12360             var ix = this.view.indexOf(csel[0]);
12361             cselitem  = this.store.getAt(ix);
12362             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12363                 cselitem = false;
12364             }
12365             
12366         }
12367         
12368         this.store.each(function(v) { 
12369             if (cselitem) {
12370                 // start at existing selection.
12371                 if (cselitem.id == v.id) {
12372                     cselitem = false;
12373                 }
12374                 return true;
12375             }
12376                 
12377             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12378                 match = this.store.indexOf(v);
12379                 return false;
12380             }
12381             return true;
12382         }, this);
12383         
12384         if (match === false) {
12385             return true; // no more action?
12386         }
12387         // scroll to?
12388         this.view.select(match);
12389         var sn = Roo.get(this.view.getSelectedNodes()[0])
12390         sn.scrollIntoView(sn.dom.parentNode, false);
12391     },
12392     
12393     onViewScroll : function(e, t){
12394         
12395         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){
12396             return;
12397         }
12398         
12399         this.hasQuery = true;
12400         
12401         this.loading = this.list.select('.loading', true).first();
12402         
12403         if(this.loading === null){
12404             this.list.createChild({
12405                 tag: 'div',
12406                 cls: 'loading select2-more-results select2-active',
12407                 html: 'Loading more results...'
12408             })
12409             
12410             this.loading = this.list.select('.loading', true).first();
12411             
12412             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12413             
12414             this.loading.hide();
12415         }
12416         
12417         this.loading.show();
12418         
12419         var _combo = this;
12420         
12421         this.page++;
12422         this.loadNext = true;
12423         
12424         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12425         
12426         return;
12427     },
12428     
12429     addItem : function(o)
12430     {   
12431         var dv = ''; // display value
12432         
12433         if (this.displayField) {
12434             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12435         } else {
12436             // this is an error condition!!!
12437             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12438         }
12439         
12440         if(!dv.length){
12441             return;
12442         }
12443         
12444         var choice = this.choices.createChild({
12445             tag: 'li',
12446             cls: 'select2-search-choice',
12447             cn: [
12448                 {
12449                     tag: 'div',
12450                     html: dv
12451                 },
12452                 {
12453                     tag: 'a',
12454                     href: '#',
12455                     cls: 'select2-search-choice-close',
12456                     tabindex: '-1'
12457                 }
12458             ]
12459             
12460         }, this.searchField);
12461         
12462         var close = choice.select('a.select2-search-choice-close', true).first()
12463         
12464         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12465         
12466         this.item.push(o);
12467         
12468         this.lastData = o;
12469         
12470         this.syncValue();
12471         
12472         this.inputEl().dom.value = '';
12473         
12474         this.validate();
12475     },
12476     
12477     onRemoveItem : function(e, _self, o)
12478     {
12479         e.preventDefault();
12480         
12481         this.lastItem = Roo.apply([], this.item);
12482         
12483         var index = this.item.indexOf(o.data) * 1;
12484         
12485         if( index < 0){
12486             Roo.log('not this item?!');
12487             return;
12488         }
12489         
12490         this.item.splice(index, 1);
12491         o.item.remove();
12492         
12493         this.syncValue();
12494         
12495         this.fireEvent('remove', this, e);
12496         
12497         this.validate();
12498         
12499     },
12500     
12501     syncValue : function()
12502     {
12503         if(!this.item.length){
12504             this.clearValue();
12505             return;
12506         }
12507             
12508         var value = [];
12509         var _this = this;
12510         Roo.each(this.item, function(i){
12511             if(_this.valueField){
12512                 value.push(i[_this.valueField]);
12513                 return;
12514             }
12515
12516             value.push(i);
12517         });
12518
12519         this.value = value.join(',');
12520
12521         if(this.hiddenField){
12522             this.hiddenField.dom.value = this.value;
12523         }
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         if (this.carousel) {
14662             cfg.cls += ' carousel slide';
14663             
14664             cfg.cn = [{
14665                cls : 'carousel-inner'
14666             }];
14667         
14668             if(this.bullets > 0){
14669                 
14670                 var bullets = {
14671                     cls : 'carousel-bullets',
14672                     cn : []
14673                 };
14674                 
14675                 for (var i = 0; i < this.bullets; i++){
14676                     bullets.cn.push({
14677                         cls : 'bullet bullet-' + i
14678                     });
14679                 }
14680                 
14681                 bullets.cn.push({
14682                     cls : 'clear'
14683                 });
14684                 
14685                 cfg.cn[0].cn = bullets;
14686             }
14687         }
14688         
14689         return cfg;
14690     },
14691     
14692     initEvents:  function()
14693     {
14694         Roo.log('-------- init events on tab group ---------');
14695         
14696         var _this = this;
14697         
14698         if(this.bullets > 0){
14699             
14700             for (var i = 0; i < this.bullets; i++){
14701                 var bullet = this.el.select('.bullet-' + i, true).first();
14702                 
14703                 if(!bullet){
14704                     continue;
14705                 }
14706                 
14707                 bullet.on('click', (function(e, el, o, ii, t){
14708                     
14709                     e.preventDefault();
14710                     
14711                     _this.showPanel(ii);
14712                     
14713                 }).createDelegate(this, [i, bullet], true));
14714                 
14715             }
14716         }
14717         
14718         if(this.autoslide){
14719             this.slideFn = window.setInterval(function() {
14720                 _this.showPanelNext();
14721             }, this.timer);
14722         }
14723     },
14724     
14725     getChildContainer : function()
14726     {
14727         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14728     },
14729     
14730     /**
14731     * register a Navigation item
14732     * @param {Roo.bootstrap.NavItem} the navitem to add
14733     */
14734     register : function(item)
14735     {
14736         this.tabs.push( item);
14737         item.navId = this.navId; // not really needed..
14738     
14739     },
14740     
14741     getActivePanel : function()
14742     {
14743         var r = false;
14744         Roo.each(this.tabs, function(t) {
14745             if (t.active) {
14746                 r = t;
14747                 return false;
14748             }
14749             return null;
14750         });
14751         return r;
14752         
14753     },
14754     getPanelByName : function(n)
14755     {
14756         var r = false;
14757         Roo.each(this.tabs, function(t) {
14758             if (t.tabId == n) {
14759                 r = t;
14760                 return false;
14761             }
14762             return null;
14763         });
14764         return r;
14765     },
14766     indexOfPanel : function(p)
14767     {
14768         var r = false;
14769         Roo.each(this.tabs, function(t,i) {
14770             if (t.tabId == p.tabId) {
14771                 r = i;
14772                 return false;
14773             }
14774             return null;
14775         });
14776         return r;
14777     },
14778     /**
14779      * show a specific panel
14780      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14781      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14782      */
14783     showPanel : function (pan)
14784     {
14785         if(this.transition){
14786             Roo.log("waiting for the transitionend");
14787             return;
14788         }
14789         
14790         if (typeof(pan) == 'number') {
14791             pan = this.tabs[pan];
14792         }
14793         if (typeof(pan) == 'string') {
14794             pan = this.getPanelByName(pan);
14795         }
14796         if (pan.tabId == this.getActivePanel().tabId) {
14797             return true;
14798         }
14799         var cur = this.getActivePanel();
14800         
14801         if (false === cur.fireEvent('beforedeactivate')) {
14802             return false;
14803         }
14804         
14805         if(this.bullets > 0){
14806             this.setActiveBullet(this.indexOfPanel(pan));
14807         }
14808         
14809         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
14810             
14811             this.transition = true;
14812             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14813             var lr = dir == 'next' ? 'left' : 'right';
14814             pan.el.addClass(dir); // or prev
14815             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14816             cur.el.addClass(lr); // or right
14817             pan.el.addClass(lr);
14818             
14819             var _this = this;
14820             cur.el.on('transitionend', function() {
14821                 Roo.log("trans end?");
14822                 
14823                 pan.el.removeClass([lr,dir]);
14824                 pan.setActive(true);
14825                 
14826                 cur.el.removeClass([lr]);
14827                 cur.setActive(false);
14828                 
14829                 _this.transition = false;
14830                 
14831             }, this, { single:  true } );
14832             
14833             return true;
14834         }
14835         
14836         cur.setActive(false);
14837         pan.setActive(true);
14838         
14839         return true;
14840         
14841     },
14842     showPanelNext : function()
14843     {
14844         var i = this.indexOfPanel(this.getActivePanel());
14845         
14846         if (i >= this.tabs.length - 1 && !this.autoslide) {
14847             return;
14848         }
14849         
14850         if (i >= this.tabs.length - 1 && this.autoslide) {
14851             i = -1;
14852         }
14853         
14854         this.showPanel(this.tabs[i+1]);
14855     },
14856     
14857     showPanelPrev : function()
14858     {
14859         var i = this.indexOfPanel(this.getActivePanel());
14860         
14861         if (i  < 1 && !this.autoslide) {
14862             return;
14863         }
14864         
14865         if (i < 1 && this.autoslide) {
14866             i = this.tabs.length;
14867         }
14868         
14869         this.showPanel(this.tabs[i-1]);
14870     },
14871     
14872     setActiveBullet : function(i)
14873     {
14874         Roo.each(this.el.select('.bullet', true).elements, function(el){
14875             el.removeClass('selected');
14876         });
14877
14878         var bullet = this.el.select('.bullet-' + i, true).first();
14879         
14880         if(!bullet){
14881             return;
14882         }
14883         
14884         bullet.addClass('selected');
14885     }
14886     
14887     
14888   
14889 });
14890
14891  
14892
14893  
14894  
14895 Roo.apply(Roo.bootstrap.TabGroup, {
14896     
14897     groups: {},
14898      /**
14899     * register a Navigation Group
14900     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14901     */
14902     register : function(navgrp)
14903     {
14904         this.groups[navgrp.navId] = navgrp;
14905         
14906     },
14907     /**
14908     * fetch a Navigation Group based on the navigation ID
14909     * if one does not exist , it will get created.
14910     * @param {string} the navgroup to add
14911     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14912     */
14913     get: function(navId) {
14914         if (typeof(this.groups[navId]) == 'undefined') {
14915             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14916         }
14917         return this.groups[navId] ;
14918     }
14919     
14920     
14921     
14922 });
14923
14924  /*
14925  * - LGPL
14926  *
14927  * TabPanel
14928  * 
14929  */
14930
14931 /**
14932  * @class Roo.bootstrap.TabPanel
14933  * @extends Roo.bootstrap.Component
14934  * Bootstrap TabPanel class
14935  * @cfg {Boolean} active panel active
14936  * @cfg {String} html panel content
14937  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14938  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14939  * 
14940  * 
14941  * @constructor
14942  * Create a new TabPanel
14943  * @param {Object} config The config object
14944  */
14945
14946 Roo.bootstrap.TabPanel = function(config){
14947     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14948     this.addEvents({
14949         /**
14950              * @event changed
14951              * Fires when the active status changes
14952              * @param {Roo.bootstrap.TabPanel} this
14953              * @param {Boolean} state the new state
14954             
14955          */
14956         'changed': true,
14957         /**
14958              * @event beforedeactivate
14959              * Fires before a tab is de-activated - can be used to do validation on a form.
14960              * @param {Roo.bootstrap.TabPanel} this
14961              * @return {Boolean} false if there is an error
14962             
14963          */
14964         'beforedeactivate': true
14965      });
14966     
14967     this.tabId = this.tabId || Roo.id();
14968   
14969 };
14970
14971 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14972     
14973     active: false,
14974     html: false,
14975     tabId: false,
14976     navId : false,
14977     
14978     getAutoCreate : function(){
14979         var cfg = {
14980             tag: 'div',
14981             // item is needed for carousel - not sure if it has any effect otherwise
14982             cls: 'tab-pane item',
14983             html: this.html || ''
14984         };
14985         
14986         if(this.active){
14987             cfg.cls += ' active';
14988         }
14989         
14990         if(this.tabId){
14991             cfg.tabId = this.tabId;
14992         }
14993         
14994         
14995         return cfg;
14996     },
14997     
14998     initEvents:  function()
14999     {
15000         Roo.log('-------- init events on tab panel ---------');
15001         
15002         var p = this.parent();
15003         this.navId = this.navId || p.navId;
15004         
15005         if (typeof(this.navId) != 'undefined') {
15006             // not really needed.. but just in case.. parent should be a NavGroup.
15007             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15008             Roo.log(['register', tg, this]);
15009             tg.register(this);
15010             
15011             var i = tg.tabs.length - 1;
15012             
15013             if(this.active && tg.bullets > 0 && i < tg.bullets){
15014                 tg.setActiveBullet(i);
15015             }
15016         }
15017         
15018     },
15019     
15020     
15021     onRender : function(ct, position)
15022     {
15023        // Roo.log("Call onRender: " + this.xtype);
15024         
15025         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
15026         
15027         
15028         
15029         
15030         
15031     },
15032     
15033     setActive: function(state)
15034     {
15035         Roo.log("panel - set active " + this.tabId + "=" + state);
15036         
15037         this.active = state;
15038         if (!state) {
15039             this.el.removeClass('active');
15040             
15041         } else  if (!this.el.hasClass('active')) {
15042             this.el.addClass('active');
15043         }
15044         
15045         this.fireEvent('changed', this, state);
15046     }
15047     
15048     
15049 });
15050  
15051
15052  
15053
15054  /*
15055  * - LGPL
15056  *
15057  * DateField
15058  * 
15059  */
15060
15061 /**
15062  * @class Roo.bootstrap.DateField
15063  * @extends Roo.bootstrap.Input
15064  * Bootstrap DateField class
15065  * @cfg {Number} weekStart default 0
15066  * @cfg {String} viewMode default empty, (months|years)
15067  * @cfg {String} minViewMode default empty, (months|years)
15068  * @cfg {Number} startDate default -Infinity
15069  * @cfg {Number} endDate default Infinity
15070  * @cfg {Boolean} todayHighlight default false
15071  * @cfg {Boolean} todayBtn default false
15072  * @cfg {Boolean} calendarWeeks default false
15073  * @cfg {Object} daysOfWeekDisabled default empty
15074  * @cfg {Boolean} singleMode default false (true | false)
15075  * 
15076  * @cfg {Boolean} keyboardNavigation default true
15077  * @cfg {String} language default en
15078  * 
15079  * @constructor
15080  * Create a new DateField
15081  * @param {Object} config The config object
15082  */
15083
15084 Roo.bootstrap.DateField = function(config){
15085     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
15086      this.addEvents({
15087             /**
15088              * @event show
15089              * Fires when this field show.
15090              * @param {Roo.bootstrap.DateField} this
15091              * @param {Mixed} date The date value
15092              */
15093             show : true,
15094             /**
15095              * @event show
15096              * Fires when this field hide.
15097              * @param {Roo.bootstrap.DateField} this
15098              * @param {Mixed} date The date value
15099              */
15100             hide : true,
15101             /**
15102              * @event select
15103              * Fires when select a date.
15104              * @param {Roo.bootstrap.DateField} this
15105              * @param {Mixed} date The date value
15106              */
15107             select : true
15108         });
15109 };
15110
15111 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
15112     
15113     /**
15114      * @cfg {String} format
15115      * The default date format string which can be overriden for localization support.  The format must be
15116      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
15117      */
15118     format : "m/d/y",
15119     /**
15120      * @cfg {String} altFormats
15121      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
15122      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
15123      */
15124     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
15125     
15126     weekStart : 0,
15127     
15128     viewMode : '',
15129     
15130     minViewMode : '',
15131     
15132     todayHighlight : false,
15133     
15134     todayBtn: false,
15135     
15136     language: 'en',
15137     
15138     keyboardNavigation: true,
15139     
15140     calendarWeeks: false,
15141     
15142     startDate: -Infinity,
15143     
15144     endDate: Infinity,
15145     
15146     daysOfWeekDisabled: [],
15147     
15148     _events: [],
15149     
15150     singleMode : false,
15151     
15152     UTCDate: function()
15153     {
15154         return new Date(Date.UTC.apply(Date, arguments));
15155     },
15156     
15157     UTCToday: function()
15158     {
15159         var today = new Date();
15160         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
15161     },
15162     
15163     getDate: function() {
15164             var d = this.getUTCDate();
15165             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
15166     },
15167     
15168     getUTCDate: function() {
15169             return this.date;
15170     },
15171     
15172     setDate: function(d) {
15173             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
15174     },
15175     
15176     setUTCDate: function(d) {
15177             this.date = d;
15178             this.setValue(this.formatDate(this.date));
15179     },
15180         
15181     onRender: function(ct, position)
15182     {
15183         
15184         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
15185         
15186         this.language = this.language || 'en';
15187         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
15188         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
15189         
15190         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
15191         this.format = this.format || 'm/d/y';
15192         this.isInline = false;
15193         this.isInput = true;
15194         this.component = this.el.select('.add-on', true).first() || false;
15195         this.component = (this.component && this.component.length === 0) ? false : this.component;
15196         this.hasInput = this.component && this.inputEL().length;
15197         
15198         if (typeof(this.minViewMode === 'string')) {
15199             switch (this.minViewMode) {
15200                 case 'months':
15201                     this.minViewMode = 1;
15202                     break;
15203                 case 'years':
15204                     this.minViewMode = 2;
15205                     break;
15206                 default:
15207                     this.minViewMode = 0;
15208                     break;
15209             }
15210         }
15211         
15212         if (typeof(this.viewMode === 'string')) {
15213             switch (this.viewMode) {
15214                 case 'months':
15215                     this.viewMode = 1;
15216                     break;
15217                 case 'years':
15218                     this.viewMode = 2;
15219                     break;
15220                 default:
15221                     this.viewMode = 0;
15222                     break;
15223             }
15224         }
15225                 
15226         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
15227         
15228 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
15229         
15230         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15231         
15232         this.picker().on('mousedown', this.onMousedown, this);
15233         this.picker().on('click', this.onClick, this);
15234         
15235         this.picker().addClass('datepicker-dropdown');
15236         
15237         this.startViewMode = this.viewMode;
15238         
15239         if(this.singleMode){
15240             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
15241                 v.setVisibilityMode(Roo.Element.DISPLAY)
15242                 v.hide();
15243             });
15244             
15245             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
15246                 v.setStyle('width', '189px');
15247             });
15248         }
15249         
15250         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
15251             if(!this.calendarWeeks){
15252                 v.remove();
15253                 return;
15254             }
15255             
15256             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15257             v.attr('colspan', function(i, val){
15258                 return parseInt(val) + 1;
15259             });
15260         })
15261                         
15262         
15263         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
15264         
15265         this.setStartDate(this.startDate);
15266         this.setEndDate(this.endDate);
15267         
15268         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
15269         
15270         this.fillDow();
15271         this.fillMonths();
15272         this.update();
15273         this.showMode();
15274         
15275         if(this.isInline) {
15276             this.show();
15277         }
15278     },
15279     
15280     picker : function()
15281     {
15282         return this.pickerEl;
15283 //        return this.el.select('.datepicker', true).first();
15284     },
15285     
15286     fillDow: function()
15287     {
15288         var dowCnt = this.weekStart;
15289         
15290         var dow = {
15291             tag: 'tr',
15292             cn: [
15293                 
15294             ]
15295         };
15296         
15297         if(this.calendarWeeks){
15298             dow.cn.push({
15299                 tag: 'th',
15300                 cls: 'cw',
15301                 html: '&nbsp;'
15302             })
15303         }
15304         
15305         while (dowCnt < this.weekStart + 7) {
15306             dow.cn.push({
15307                 tag: 'th',
15308                 cls: 'dow',
15309                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
15310             });
15311         }
15312         
15313         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
15314     },
15315     
15316     fillMonths: function()
15317     {    
15318         var i = 0;
15319         var months = this.picker().select('>.datepicker-months td', true).first();
15320         
15321         months.dom.innerHTML = '';
15322         
15323         while (i < 12) {
15324             var month = {
15325                 tag: 'span',
15326                 cls: 'month',
15327                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
15328             }
15329             
15330             months.createChild(month);
15331         }
15332         
15333     },
15334     
15335     update: function()
15336     {
15337         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;
15338         
15339         if (this.date < this.startDate) {
15340             this.viewDate = new Date(this.startDate);
15341         } else if (this.date > this.endDate) {
15342             this.viewDate = new Date(this.endDate);
15343         } else {
15344             this.viewDate = new Date(this.date);
15345         }
15346         
15347         this.fill();
15348     },
15349     
15350     fill: function() 
15351     {
15352         var d = new Date(this.viewDate),
15353                 year = d.getUTCFullYear(),
15354                 month = d.getUTCMonth(),
15355                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
15356                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
15357                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
15358                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
15359                 currentDate = this.date && this.date.valueOf(),
15360                 today = this.UTCToday();
15361         
15362         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
15363         
15364 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15365         
15366 //        this.picker.select('>tfoot th.today').
15367 //                                              .text(dates[this.language].today)
15368 //                                              .toggle(this.todayBtn !== false);
15369     
15370         this.updateNavArrows();
15371         this.fillMonths();
15372                                                 
15373         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
15374         
15375         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
15376          
15377         prevMonth.setUTCDate(day);
15378         
15379         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
15380         
15381         var nextMonth = new Date(prevMonth);
15382         
15383         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
15384         
15385         nextMonth = nextMonth.valueOf();
15386         
15387         var fillMonths = false;
15388         
15389         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
15390         
15391         while(prevMonth.valueOf() < nextMonth) {
15392             var clsName = '';
15393             
15394             if (prevMonth.getUTCDay() === this.weekStart) {
15395                 if(fillMonths){
15396                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
15397                 }
15398                     
15399                 fillMonths = {
15400                     tag: 'tr',
15401                     cn: []
15402                 };
15403                 
15404                 if(this.calendarWeeks){
15405                     // ISO 8601: First week contains first thursday.
15406                     // ISO also states week starts on Monday, but we can be more abstract here.
15407                     var
15408                     // Start of current week: based on weekstart/current date
15409                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
15410                     // Thursday of this week
15411                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
15412                     // First Thursday of year, year from thursday
15413                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
15414                     // Calendar week: ms between thursdays, div ms per day, div 7 days
15415                     calWeek =  (th - yth) / 864e5 / 7 + 1;
15416                     
15417                     fillMonths.cn.push({
15418                         tag: 'td',
15419                         cls: 'cw',
15420                         html: calWeek
15421                     });
15422                 }
15423             }
15424             
15425             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
15426                 clsName += ' old';
15427             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
15428                 clsName += ' new';
15429             }
15430             if (this.todayHighlight &&
15431                 prevMonth.getUTCFullYear() == today.getFullYear() &&
15432                 prevMonth.getUTCMonth() == today.getMonth() &&
15433                 prevMonth.getUTCDate() == today.getDate()) {
15434                 clsName += ' today';
15435             }
15436             
15437             if (currentDate && prevMonth.valueOf() === currentDate) {
15438                 clsName += ' active';
15439             }
15440             
15441             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
15442                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
15443                     clsName += ' disabled';
15444             }
15445             
15446             fillMonths.cn.push({
15447                 tag: 'td',
15448                 cls: 'day ' + clsName,
15449                 html: prevMonth.getDate()
15450             })
15451             
15452             prevMonth.setDate(prevMonth.getDate()+1);
15453         }
15454           
15455         var currentYear = this.date && this.date.getUTCFullYear();
15456         var currentMonth = this.date && this.date.getUTCMonth();
15457         
15458         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
15459         
15460         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
15461             v.removeClass('active');
15462             
15463             if(currentYear === year && k === currentMonth){
15464                 v.addClass('active');
15465             }
15466             
15467             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
15468                 v.addClass('disabled');
15469             }
15470             
15471         });
15472         
15473         
15474         year = parseInt(year/10, 10) * 10;
15475         
15476         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
15477         
15478         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
15479         
15480         year -= 1;
15481         for (var i = -1; i < 11; i++) {
15482             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
15483                 tag: 'span',
15484                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
15485                 html: year
15486             })
15487             
15488             year += 1;
15489         }
15490     },
15491     
15492     showMode: function(dir) 
15493     {
15494         if (dir) {
15495             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15496         }
15497         
15498         Roo.each(this.picker().select('>div',true).elements, function(v){
15499             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15500             v.hide();
15501         });
15502         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15503     },
15504     
15505     place: function()
15506     {
15507         if(this.isInline) return;
15508         
15509         this.picker().removeClass(['bottom', 'top']);
15510         
15511         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15512             /*
15513              * place to the top of element!
15514              *
15515              */
15516             
15517             this.picker().addClass('top');
15518             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15519             
15520             return;
15521         }
15522         
15523         this.picker().addClass('bottom');
15524         
15525         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15526     },
15527     
15528     parseDate : function(value)
15529     {
15530         if(!value || value instanceof Date){
15531             return value;
15532         }
15533         var v = Date.parseDate(value, this.format);
15534         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15535             v = Date.parseDate(value, 'Y-m-d');
15536         }
15537         if(!v && this.altFormats){
15538             if(!this.altFormatsArray){
15539                 this.altFormatsArray = this.altFormats.split("|");
15540             }
15541             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15542                 v = Date.parseDate(value, this.altFormatsArray[i]);
15543             }
15544         }
15545         return v;
15546     },
15547     
15548     formatDate : function(date, fmt)
15549     {   
15550         return (!date || !(date instanceof Date)) ?
15551         date : date.dateFormat(fmt || this.format);
15552     },
15553     
15554     onFocus : function()
15555     {
15556         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15557         this.show();
15558     },
15559     
15560     onBlur : function()
15561     {
15562         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15563         
15564         var d = this.inputEl().getValue();
15565         
15566         this.setValue(d);
15567                 
15568         this.hide();
15569     },
15570     
15571     show : function()
15572     {
15573         this.picker().show();
15574         this.update();
15575         this.place();
15576         
15577         this.fireEvent('show', this, this.date);
15578     },
15579     
15580     hide : function()
15581     {
15582         if(this.isInline) return;
15583         this.picker().hide();
15584         this.viewMode = this.startViewMode;
15585         this.showMode();
15586         
15587         this.fireEvent('hide', this, this.date);
15588         
15589     },
15590     
15591     onMousedown: function(e)
15592     {
15593         e.stopPropagation();
15594         e.preventDefault();
15595     },
15596     
15597     keyup: function(e)
15598     {
15599         Roo.bootstrap.DateField.superclass.keyup.call(this);
15600         this.update();
15601     },
15602
15603     setValue: function(v)
15604     {
15605         
15606         // v can be a string or a date..
15607         
15608         
15609         var d = new Date(this.parseDate(v) ).clearTime();
15610         
15611         if(isNaN(d.getTime())){
15612             this.date = this.viewDate = '';
15613             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15614             return;
15615         }
15616         
15617         v = this.formatDate(d);
15618         
15619         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15620         
15621         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15622      
15623         this.update();
15624
15625         this.fireEvent('select', this, this.date);
15626         
15627     },
15628     
15629     getValue: function()
15630     {
15631         return this.formatDate(this.date);
15632     },
15633     
15634     fireKey: function(e)
15635     {
15636         if (!this.picker().isVisible()){
15637             if (e.keyCode == 27) // allow escape to hide and re-show picker
15638                 this.show();
15639             return;
15640         }
15641         
15642         var dateChanged = false,
15643         dir, day, month,
15644         newDate, newViewDate;
15645         
15646         switch(e.keyCode){
15647             case 27: // escape
15648                 this.hide();
15649                 e.preventDefault();
15650                 break;
15651             case 37: // left
15652             case 39: // right
15653                 if (!this.keyboardNavigation) break;
15654                 dir = e.keyCode == 37 ? -1 : 1;
15655                 
15656                 if (e.ctrlKey){
15657                     newDate = this.moveYear(this.date, dir);
15658                     newViewDate = this.moveYear(this.viewDate, dir);
15659                 } else if (e.shiftKey){
15660                     newDate = this.moveMonth(this.date, dir);
15661                     newViewDate = this.moveMonth(this.viewDate, dir);
15662                 } else {
15663                     newDate = new Date(this.date);
15664                     newDate.setUTCDate(this.date.getUTCDate() + dir);
15665                     newViewDate = new Date(this.viewDate);
15666                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15667                 }
15668                 if (this.dateWithinRange(newDate)){
15669                     this.date = newDate;
15670                     this.viewDate = newViewDate;
15671                     this.setValue(this.formatDate(this.date));
15672 //                    this.update();
15673                     e.preventDefault();
15674                     dateChanged = true;
15675                 }
15676                 break;
15677             case 38: // up
15678             case 40: // down
15679                 if (!this.keyboardNavigation) break;
15680                 dir = e.keyCode == 38 ? -1 : 1;
15681                 if (e.ctrlKey){
15682                     newDate = this.moveYear(this.date, dir);
15683                     newViewDate = this.moveYear(this.viewDate, dir);
15684                 } else if (e.shiftKey){
15685                     newDate = this.moveMonth(this.date, dir);
15686                     newViewDate = this.moveMonth(this.viewDate, dir);
15687                 } else {
15688                     newDate = new Date(this.date);
15689                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15690                     newViewDate = new Date(this.viewDate);
15691                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15692                 }
15693                 if (this.dateWithinRange(newDate)){
15694                     this.date = newDate;
15695                     this.viewDate = newViewDate;
15696                     this.setValue(this.formatDate(this.date));
15697 //                    this.update();
15698                     e.preventDefault();
15699                     dateChanged = true;
15700                 }
15701                 break;
15702             case 13: // enter
15703                 this.setValue(this.formatDate(this.date));
15704                 this.hide();
15705                 e.preventDefault();
15706                 break;
15707             case 9: // tab
15708                 this.setValue(this.formatDate(this.date));
15709                 this.hide();
15710                 break;
15711             case 16: // shift
15712             case 17: // ctrl
15713             case 18: // alt
15714                 break;
15715             default :
15716                 this.hide();
15717                 
15718         }
15719     },
15720     
15721     
15722     onClick: function(e) 
15723     {
15724         e.stopPropagation();
15725         e.preventDefault();
15726         
15727         var target = e.getTarget();
15728         
15729         if(target.nodeName.toLowerCase() === 'i'){
15730             target = Roo.get(target).dom.parentNode;
15731         }
15732         
15733         var nodeName = target.nodeName;
15734         var className = target.className;
15735         var html = target.innerHTML;
15736         //Roo.log(nodeName);
15737         
15738         switch(nodeName.toLowerCase()) {
15739             case 'th':
15740                 switch(className) {
15741                     case 'switch':
15742                         this.showMode(1);
15743                         break;
15744                     case 'prev':
15745                     case 'next':
15746                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15747                         switch(this.viewMode){
15748                                 case 0:
15749                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15750                                         break;
15751                                 case 1:
15752                                 case 2:
15753                                         this.viewDate = this.moveYear(this.viewDate, dir);
15754                                         break;
15755                         }
15756                         this.fill();
15757                         break;
15758                     case 'today':
15759                         var date = new Date();
15760                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15761 //                        this.fill()
15762                         this.setValue(this.formatDate(this.date));
15763                         
15764                         this.hide();
15765                         break;
15766                 }
15767                 break;
15768             case 'span':
15769                 if (className.indexOf('disabled') < 0) {
15770                     this.viewDate.setUTCDate(1);
15771                     if (className.indexOf('month') > -1) {
15772                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15773                     } else {
15774                         var year = parseInt(html, 10) || 0;
15775                         this.viewDate.setUTCFullYear(year);
15776                         
15777                     }
15778                     
15779                     if(this.singleMode){
15780                         this.setValue(this.formatDate(this.viewDate));
15781                         this.hide();
15782                         return;
15783                     }
15784                     
15785                     this.showMode(-1);
15786                     this.fill();
15787                 }
15788                 break;
15789                 
15790             case 'td':
15791                 //Roo.log(className);
15792                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15793                     var day = parseInt(html, 10) || 1;
15794                     var year = this.viewDate.getUTCFullYear(),
15795                         month = this.viewDate.getUTCMonth();
15796
15797                     if (className.indexOf('old') > -1) {
15798                         if(month === 0 ){
15799                             month = 11;
15800                             year -= 1;
15801                         }else{
15802                             month -= 1;
15803                         }
15804                     } else if (className.indexOf('new') > -1) {
15805                         if (month == 11) {
15806                             month = 0;
15807                             year += 1;
15808                         } else {
15809                             month += 1;
15810                         }
15811                     }
15812                     //Roo.log([year,month,day]);
15813                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15814                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15815 //                    this.fill();
15816                     //Roo.log(this.formatDate(this.date));
15817                     this.setValue(this.formatDate(this.date));
15818                     this.hide();
15819                 }
15820                 break;
15821         }
15822     },
15823     
15824     setStartDate: function(startDate)
15825     {
15826         this.startDate = startDate || -Infinity;
15827         if (this.startDate !== -Infinity) {
15828             this.startDate = this.parseDate(this.startDate);
15829         }
15830         this.update();
15831         this.updateNavArrows();
15832     },
15833
15834     setEndDate: function(endDate)
15835     {
15836         this.endDate = endDate || Infinity;
15837         if (this.endDate !== Infinity) {
15838             this.endDate = this.parseDate(this.endDate);
15839         }
15840         this.update();
15841         this.updateNavArrows();
15842     },
15843     
15844     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15845     {
15846         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15847         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15848             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15849         }
15850         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15851             return parseInt(d, 10);
15852         });
15853         this.update();
15854         this.updateNavArrows();
15855     },
15856     
15857     updateNavArrows: function() 
15858     {
15859         if(this.singleMode){
15860             return;
15861         }
15862         
15863         var d = new Date(this.viewDate),
15864         year = d.getUTCFullYear(),
15865         month = d.getUTCMonth();
15866         
15867         Roo.each(this.picker().select('.prev', true).elements, function(v){
15868             v.show();
15869             switch (this.viewMode) {
15870                 case 0:
15871
15872                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15873                         v.hide();
15874                     }
15875                     break;
15876                 case 1:
15877                 case 2:
15878                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15879                         v.hide();
15880                     }
15881                     break;
15882             }
15883         });
15884         
15885         Roo.each(this.picker().select('.next', true).elements, function(v){
15886             v.show();
15887             switch (this.viewMode) {
15888                 case 0:
15889
15890                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15891                         v.hide();
15892                     }
15893                     break;
15894                 case 1:
15895                 case 2:
15896                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15897                         v.hide();
15898                     }
15899                     break;
15900             }
15901         })
15902     },
15903     
15904     moveMonth: function(date, dir)
15905     {
15906         if (!dir) return date;
15907         var new_date = new Date(date.valueOf()),
15908         day = new_date.getUTCDate(),
15909         month = new_date.getUTCMonth(),
15910         mag = Math.abs(dir),
15911         new_month, test;
15912         dir = dir > 0 ? 1 : -1;
15913         if (mag == 1){
15914             test = dir == -1
15915             // If going back one month, make sure month is not current month
15916             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15917             ? function(){
15918                 return new_date.getUTCMonth() == month;
15919             }
15920             // If going forward one month, make sure month is as expected
15921             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15922             : function(){
15923                 return new_date.getUTCMonth() != new_month;
15924             };
15925             new_month = month + dir;
15926             new_date.setUTCMonth(new_month);
15927             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15928             if (new_month < 0 || new_month > 11)
15929                 new_month = (new_month + 12) % 12;
15930         } else {
15931             // For magnitudes >1, move one month at a time...
15932             for (var i=0; i<mag; i++)
15933                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15934                 new_date = this.moveMonth(new_date, dir);
15935             // ...then reset the day, keeping it in the new month
15936             new_month = new_date.getUTCMonth();
15937             new_date.setUTCDate(day);
15938             test = function(){
15939                 return new_month != new_date.getUTCMonth();
15940             };
15941         }
15942         // Common date-resetting loop -- if date is beyond end of month, make it
15943         // end of month
15944         while (test()){
15945             new_date.setUTCDate(--day);
15946             new_date.setUTCMonth(new_month);
15947         }
15948         return new_date;
15949     },
15950
15951     moveYear: function(date, dir)
15952     {
15953         return this.moveMonth(date, dir*12);
15954     },
15955
15956     dateWithinRange: function(date)
15957     {
15958         return date >= this.startDate && date <= this.endDate;
15959     },
15960
15961     
15962     remove: function() 
15963     {
15964         this.picker().remove();
15965     }
15966    
15967 });
15968
15969 Roo.apply(Roo.bootstrap.DateField,  {
15970     
15971     head : {
15972         tag: 'thead',
15973         cn: [
15974         {
15975             tag: 'tr',
15976             cn: [
15977             {
15978                 tag: 'th',
15979                 cls: 'prev',
15980                 html: '<i class="fa fa-arrow-left"/>'
15981             },
15982             {
15983                 tag: 'th',
15984                 cls: 'switch',
15985                 colspan: '5'
15986             },
15987             {
15988                 tag: 'th',
15989                 cls: 'next',
15990                 html: '<i class="fa fa-arrow-right"/>'
15991             }
15992
15993             ]
15994         }
15995         ]
15996     },
15997     
15998     content : {
15999         tag: 'tbody',
16000         cn: [
16001         {
16002             tag: 'tr',
16003             cn: [
16004             {
16005                 tag: 'td',
16006                 colspan: '7'
16007             }
16008             ]
16009         }
16010         ]
16011     },
16012     
16013     footer : {
16014         tag: 'tfoot',
16015         cn: [
16016         {
16017             tag: 'tr',
16018             cn: [
16019             {
16020                 tag: 'th',
16021                 colspan: '7',
16022                 cls: 'today'
16023             }
16024                     
16025             ]
16026         }
16027         ]
16028     },
16029     
16030     dates:{
16031         en: {
16032             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
16033             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
16034             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
16035             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16036             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
16037             today: "Today"
16038         }
16039     },
16040     
16041     modes: [
16042     {
16043         clsName: 'days',
16044         navFnc: 'Month',
16045         navStep: 1
16046     },
16047     {
16048         clsName: 'months',
16049         navFnc: 'FullYear',
16050         navStep: 1
16051     },
16052     {
16053         clsName: 'years',
16054         navFnc: 'FullYear',
16055         navStep: 10
16056     }]
16057 });
16058
16059 Roo.apply(Roo.bootstrap.DateField,  {
16060   
16061     template : {
16062         tag: 'div',
16063         cls: 'datepicker dropdown-menu roo-dynamic',
16064         cn: [
16065         {
16066             tag: 'div',
16067             cls: 'datepicker-days',
16068             cn: [
16069             {
16070                 tag: 'table',
16071                 cls: 'table-condensed',
16072                 cn:[
16073                 Roo.bootstrap.DateField.head,
16074                 {
16075                     tag: 'tbody'
16076                 },
16077                 Roo.bootstrap.DateField.footer
16078                 ]
16079             }
16080             ]
16081         },
16082         {
16083             tag: 'div',
16084             cls: 'datepicker-months',
16085             cn: [
16086             {
16087                 tag: 'table',
16088                 cls: 'table-condensed',
16089                 cn:[
16090                 Roo.bootstrap.DateField.head,
16091                 Roo.bootstrap.DateField.content,
16092                 Roo.bootstrap.DateField.footer
16093                 ]
16094             }
16095             ]
16096         },
16097         {
16098             tag: 'div',
16099             cls: 'datepicker-years',
16100             cn: [
16101             {
16102                 tag: 'table',
16103                 cls: 'table-condensed',
16104                 cn:[
16105                 Roo.bootstrap.DateField.head,
16106                 Roo.bootstrap.DateField.content,
16107                 Roo.bootstrap.DateField.footer
16108                 ]
16109             }
16110             ]
16111         }
16112         ]
16113     }
16114 });
16115
16116  
16117
16118  /*
16119  * - LGPL
16120  *
16121  * TimeField
16122  * 
16123  */
16124
16125 /**
16126  * @class Roo.bootstrap.TimeField
16127  * @extends Roo.bootstrap.Input
16128  * Bootstrap DateField class
16129  * 
16130  * 
16131  * @constructor
16132  * Create a new TimeField
16133  * @param {Object} config The config object
16134  */
16135
16136 Roo.bootstrap.TimeField = function(config){
16137     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
16138     this.addEvents({
16139             /**
16140              * @event show
16141              * Fires when this field show.
16142              * @param {Roo.bootstrap.DateField} thisthis
16143              * @param {Mixed} date The date value
16144              */
16145             show : true,
16146             /**
16147              * @event show
16148              * Fires when this field hide.
16149              * @param {Roo.bootstrap.DateField} this
16150              * @param {Mixed} date The date value
16151              */
16152             hide : true,
16153             /**
16154              * @event select
16155              * Fires when select a date.
16156              * @param {Roo.bootstrap.DateField} this
16157              * @param {Mixed} date The date value
16158              */
16159             select : true
16160         });
16161 };
16162
16163 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
16164     
16165     /**
16166      * @cfg {String} format
16167      * The default time format string which can be overriden for localization support.  The format must be
16168      * valid according to {@link Date#parseDate} (defaults to 'H:i').
16169      */
16170     format : "H:i",
16171        
16172     onRender: function(ct, position)
16173     {
16174         
16175         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
16176                 
16177         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
16178         
16179         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16180         
16181         this.pop = this.picker().select('>.datepicker-time',true).first();
16182         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16183         
16184         this.picker().on('mousedown', this.onMousedown, this);
16185         this.picker().on('click', this.onClick, this);
16186         
16187         this.picker().addClass('datepicker-dropdown');
16188     
16189         this.fillTime();
16190         this.update();
16191             
16192         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
16193         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
16194         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
16195         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
16196         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
16197         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
16198
16199     },
16200     
16201     fireKey: function(e){
16202         if (!this.picker().isVisible()){
16203             if (e.keyCode == 27) { // allow escape to hide and re-show picker
16204                 this.show();
16205             }
16206             return;
16207         }
16208
16209         e.preventDefault();
16210         
16211         switch(e.keyCode){
16212             case 27: // escape
16213                 this.hide();
16214                 break;
16215             case 37: // left
16216             case 39: // right
16217                 this.onTogglePeriod();
16218                 break;
16219             case 38: // up
16220                 this.onIncrementMinutes();
16221                 break;
16222             case 40: // down
16223                 this.onDecrementMinutes();
16224                 break;
16225             case 13: // enter
16226             case 9: // tab
16227                 this.setTime();
16228                 break;
16229         }
16230     },
16231     
16232     onClick: function(e) {
16233         e.stopPropagation();
16234         e.preventDefault();
16235     },
16236     
16237     picker : function()
16238     {
16239         return this.el.select('.datepicker', true).first();
16240     },
16241     
16242     fillTime: function()
16243     {    
16244         var time = this.pop.select('tbody', true).first();
16245         
16246         time.dom.innerHTML = '';
16247         
16248         time.createChild({
16249             tag: 'tr',
16250             cn: [
16251                 {
16252                     tag: 'td',
16253                     cn: [
16254                         {
16255                             tag: 'a',
16256                             href: '#',
16257                             cls: 'btn',
16258                             cn: [
16259                                 {
16260                                     tag: 'span',
16261                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
16262                                 }
16263                             ]
16264                         } 
16265                     ]
16266                 },
16267                 {
16268                     tag: 'td',
16269                     cls: 'separator'
16270                 },
16271                 {
16272                     tag: 'td',
16273                     cn: [
16274                         {
16275                             tag: 'a',
16276                             href: '#',
16277                             cls: 'btn',
16278                             cn: [
16279                                 {
16280                                     tag: 'span',
16281                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
16282                                 }
16283                             ]
16284                         }
16285                     ]
16286                 },
16287                 {
16288                     tag: 'td',
16289                     cls: 'separator'
16290                 }
16291             ]
16292         });
16293         
16294         time.createChild({
16295             tag: 'tr',
16296             cn: [
16297                 {
16298                     tag: 'td',
16299                     cn: [
16300                         {
16301                             tag: 'span',
16302                             cls: 'timepicker-hour',
16303                             html: '00'
16304                         }  
16305                     ]
16306                 },
16307                 {
16308                     tag: 'td',
16309                     cls: 'separator',
16310                     html: ':'
16311                 },
16312                 {
16313                     tag: 'td',
16314                     cn: [
16315                         {
16316                             tag: 'span',
16317                             cls: 'timepicker-minute',
16318                             html: '00'
16319                         }  
16320                     ]
16321                 },
16322                 {
16323                     tag: 'td',
16324                     cls: 'separator'
16325                 },
16326                 {
16327                     tag: 'td',
16328                     cn: [
16329                         {
16330                             tag: 'button',
16331                             type: 'button',
16332                             cls: 'btn btn-primary period',
16333                             html: 'AM'
16334                             
16335                         }
16336                     ]
16337                 }
16338             ]
16339         });
16340         
16341         time.createChild({
16342             tag: 'tr',
16343             cn: [
16344                 {
16345                     tag: 'td',
16346                     cn: [
16347                         {
16348                             tag: 'a',
16349                             href: '#',
16350                             cls: 'btn',
16351                             cn: [
16352                                 {
16353                                     tag: 'span',
16354                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
16355                                 }
16356                             ]
16357                         }
16358                     ]
16359                 },
16360                 {
16361                     tag: 'td',
16362                     cls: 'separator'
16363                 },
16364                 {
16365                     tag: 'td',
16366                     cn: [
16367                         {
16368                             tag: 'a',
16369                             href: '#',
16370                             cls: 'btn',
16371                             cn: [
16372                                 {
16373                                     tag: 'span',
16374                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
16375                                 }
16376                             ]
16377                         }
16378                     ]
16379                 },
16380                 {
16381                     tag: 'td',
16382                     cls: 'separator'
16383                 }
16384             ]
16385         });
16386         
16387     },
16388     
16389     update: function()
16390     {
16391         
16392         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
16393         
16394         this.fill();
16395     },
16396     
16397     fill: function() 
16398     {
16399         var hours = this.time.getHours();
16400         var minutes = this.time.getMinutes();
16401         var period = 'AM';
16402         
16403         if(hours > 11){
16404             period = 'PM';
16405         }
16406         
16407         if(hours == 0){
16408             hours = 12;
16409         }
16410         
16411         
16412         if(hours > 12){
16413             hours = hours - 12;
16414         }
16415         
16416         if(hours < 10){
16417             hours = '0' + hours;
16418         }
16419         
16420         if(minutes < 10){
16421             minutes = '0' + minutes;
16422         }
16423         
16424         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
16425         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
16426         this.pop.select('button', true).first().dom.innerHTML = period;
16427         
16428     },
16429     
16430     place: function()
16431     {   
16432         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
16433         
16434         var cls = ['bottom'];
16435         
16436         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
16437             cls.pop();
16438             cls.push('top');
16439         }
16440         
16441         cls.push('right');
16442         
16443         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
16444             cls.pop();
16445             cls.push('left');
16446         }
16447         
16448         this.picker().addClass(cls.join('-'));
16449         
16450         var _this = this;
16451         
16452         Roo.each(cls, function(c){
16453             if(c == 'bottom'){
16454                 _this.picker().setTop(_this.inputEl().getHeight());
16455                 return;
16456             }
16457             if(c == 'top'){
16458                 _this.picker().setTop(0 - _this.picker().getHeight());
16459                 return;
16460             }
16461             
16462             if(c == 'left'){
16463                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
16464                 return;
16465             }
16466             if(c == 'right'){
16467                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
16468                 return;
16469             }
16470         });
16471         
16472     },
16473   
16474     onFocus : function()
16475     {
16476         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
16477         this.show();
16478     },
16479     
16480     onBlur : function()
16481     {
16482         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
16483         this.hide();
16484     },
16485     
16486     show : function()
16487     {
16488         this.picker().show();
16489         this.pop.show();
16490         this.update();
16491         this.place();
16492         
16493         this.fireEvent('show', this, this.date);
16494     },
16495     
16496     hide : function()
16497     {
16498         this.picker().hide();
16499         this.pop.hide();
16500         
16501         this.fireEvent('hide', this, this.date);
16502     },
16503     
16504     setTime : function()
16505     {
16506         this.hide();
16507         this.setValue(this.time.format(this.format));
16508         
16509         this.fireEvent('select', this, this.date);
16510         
16511         
16512     },
16513     
16514     onMousedown: function(e){
16515         e.stopPropagation();
16516         e.preventDefault();
16517     },
16518     
16519     onIncrementHours: function()
16520     {
16521         Roo.log('onIncrementHours');
16522         this.time = this.time.add(Date.HOUR, 1);
16523         this.update();
16524         
16525     },
16526     
16527     onDecrementHours: function()
16528     {
16529         Roo.log('onDecrementHours');
16530         this.time = this.time.add(Date.HOUR, -1);
16531         this.update();
16532     },
16533     
16534     onIncrementMinutes: function()
16535     {
16536         Roo.log('onIncrementMinutes');
16537         this.time = this.time.add(Date.MINUTE, 1);
16538         this.update();
16539     },
16540     
16541     onDecrementMinutes: function()
16542     {
16543         Roo.log('onDecrementMinutes');
16544         this.time = this.time.add(Date.MINUTE, -1);
16545         this.update();
16546     },
16547     
16548     onTogglePeriod: function()
16549     {
16550         Roo.log('onTogglePeriod');
16551         this.time = this.time.add(Date.HOUR, 12);
16552         this.update();
16553     }
16554     
16555    
16556 });
16557
16558 Roo.apply(Roo.bootstrap.TimeField,  {
16559     
16560     content : {
16561         tag: 'tbody',
16562         cn: [
16563             {
16564                 tag: 'tr',
16565                 cn: [
16566                 {
16567                     tag: 'td',
16568                     colspan: '7'
16569                 }
16570                 ]
16571             }
16572         ]
16573     },
16574     
16575     footer : {
16576         tag: 'tfoot',
16577         cn: [
16578             {
16579                 tag: 'tr',
16580                 cn: [
16581                 {
16582                     tag: 'th',
16583                     colspan: '7',
16584                     cls: '',
16585                     cn: [
16586                         {
16587                             tag: 'button',
16588                             cls: 'btn btn-info ok',
16589                             html: 'OK'
16590                         }
16591                     ]
16592                 }
16593
16594                 ]
16595             }
16596         ]
16597     }
16598 });
16599
16600 Roo.apply(Roo.bootstrap.TimeField,  {
16601   
16602     template : {
16603         tag: 'div',
16604         cls: 'datepicker dropdown-menu',
16605         cn: [
16606             {
16607                 tag: 'div',
16608                 cls: 'datepicker-time',
16609                 cn: [
16610                 {
16611                     tag: 'table',
16612                     cls: 'table-condensed',
16613                     cn:[
16614                     Roo.bootstrap.TimeField.content,
16615                     Roo.bootstrap.TimeField.footer
16616                     ]
16617                 }
16618                 ]
16619             }
16620         ]
16621     }
16622 });
16623
16624  
16625
16626  /*
16627  * - LGPL
16628  *
16629  * MonthField
16630  * 
16631  */
16632
16633 /**
16634  * @class Roo.bootstrap.MonthField
16635  * @extends Roo.bootstrap.Input
16636  * Bootstrap MonthField class
16637  * 
16638  * @cfg {String} language default en
16639  * 
16640  * @constructor
16641  * Create a new MonthField
16642  * @param {Object} config The config object
16643  */
16644
16645 Roo.bootstrap.MonthField = function(config){
16646     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
16647     
16648     this.addEvents({
16649         /**
16650          * @event show
16651          * Fires when this field show.
16652          * @param {Roo.bootstrap.MonthField} this
16653          * @param {Mixed} date The date value
16654          */
16655         show : true,
16656         /**
16657          * @event show
16658          * Fires when this field hide.
16659          * @param {Roo.bootstrap.MonthField} this
16660          * @param {Mixed} date The date value
16661          */
16662         hide : true,
16663         /**
16664          * @event select
16665          * Fires when select a date.
16666          * @param {Roo.bootstrap.MonthField} this
16667          * @param {String} oldvalue The old value
16668          * @param {String} newvalue The new value
16669          */
16670         select : true
16671     });
16672 };
16673
16674 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
16675     
16676     onRender: function(ct, position)
16677     {
16678         
16679         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
16680         
16681         this.language = this.language || 'en';
16682         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
16683         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
16684         
16685         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
16686         this.isInline = false;
16687         this.isInput = true;
16688         this.component = this.el.select('.add-on', true).first() || false;
16689         this.component = (this.component && this.component.length === 0) ? false : this.component;
16690         this.hasInput = this.component && this.inputEL().length;
16691         
16692         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
16693         
16694         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16695         
16696         this.picker().on('mousedown', this.onMousedown, this);
16697         this.picker().on('click', this.onClick, this);
16698         
16699         this.picker().addClass('datepicker-dropdown');
16700         
16701         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16702             v.setStyle('width', '189px');
16703         });
16704         
16705         this.fillMonths();
16706         
16707         this.update();
16708         
16709         if(this.isInline) {
16710             this.show();
16711         }
16712         
16713     },
16714     
16715     setValue: function(v, suppressEvent)
16716     {   
16717         var o = this.getValue();
16718         
16719         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
16720         
16721         this.update();
16722
16723         if(suppressEvent !== true){
16724             this.fireEvent('select', this, o, v);
16725         }
16726         
16727     },
16728     
16729     getValue: function()
16730     {
16731         return this.value;
16732     },
16733     
16734     onClick: function(e) 
16735     {
16736         e.stopPropagation();
16737         e.preventDefault();
16738         
16739         var target = e.getTarget();
16740         
16741         if(target.nodeName.toLowerCase() === 'i'){
16742             target = Roo.get(target).dom.parentNode;
16743         }
16744         
16745         var nodeName = target.nodeName;
16746         var className = target.className;
16747         var html = target.innerHTML;
16748         
16749         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
16750             return;
16751         }
16752         
16753         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
16754         
16755         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16756         
16757         this.hide();
16758                         
16759     },
16760     
16761     picker : function()
16762     {
16763         return this.pickerEl;
16764     },
16765     
16766     fillMonths: function()
16767     {    
16768         var i = 0;
16769         var months = this.picker().select('>.datepicker-months td', true).first();
16770         
16771         months.dom.innerHTML = '';
16772         
16773         while (i < 12) {
16774             var month = {
16775                 tag: 'span',
16776                 cls: 'month',
16777                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
16778             }
16779             
16780             months.createChild(month);
16781         }
16782         
16783     },
16784     
16785     update: function()
16786     {
16787         var _this = this;
16788         
16789         if(typeof(this.vIndex) == 'undefined' && this.value.length){
16790             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
16791         }
16792         
16793         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
16794             e.removeClass('active');
16795             
16796             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
16797                 e.addClass('active');
16798             }
16799         })
16800     },
16801     
16802     place: function()
16803     {
16804         if(this.isInline) return;
16805         
16806         this.picker().removeClass(['bottom', 'top']);
16807         
16808         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16809             /*
16810              * place to the top of element!
16811              *
16812              */
16813             
16814             this.picker().addClass('top');
16815             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16816             
16817             return;
16818         }
16819         
16820         this.picker().addClass('bottom');
16821         
16822         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16823     },
16824     
16825     onFocus : function()
16826     {
16827         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
16828         this.show();
16829     },
16830     
16831     onBlur : function()
16832     {
16833         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
16834         
16835         var d = this.inputEl().getValue();
16836         
16837         this.setValue(d);
16838                 
16839         this.hide();
16840     },
16841     
16842     show : function()
16843     {
16844         this.picker().show();
16845         this.picker().select('>.datepicker-months', true).first().show();
16846         this.update();
16847         this.place();
16848         
16849         this.fireEvent('show', this, this.date);
16850     },
16851     
16852     hide : function()
16853     {
16854         if(this.isInline) return;
16855         this.picker().hide();
16856         this.fireEvent('hide', this, this.date);
16857         
16858     },
16859     
16860     onMousedown: function(e)
16861     {
16862         e.stopPropagation();
16863         e.preventDefault();
16864     },
16865     
16866     keyup: function(e)
16867     {
16868         Roo.bootstrap.MonthField.superclass.keyup.call(this);
16869         this.update();
16870     },
16871
16872     fireKey: function(e)
16873     {
16874         if (!this.picker().isVisible()){
16875             if (e.keyCode == 27) // allow escape to hide and re-show picker
16876                 this.show();
16877             return;
16878         }
16879         
16880         var dir;
16881         
16882         switch(e.keyCode){
16883             case 27: // escape
16884                 this.hide();
16885                 e.preventDefault();
16886                 break;
16887             case 37: // left
16888             case 39: // right
16889                 dir = e.keyCode == 37 ? -1 : 1;
16890                 
16891                 this.vIndex = this.vIndex + dir;
16892                 
16893                 if(this.vIndex < 0){
16894                     this.vIndex = 0;
16895                 }
16896                 
16897                 if(this.vIndex > 11){
16898                     this.vIndex = 11;
16899                 }
16900                 
16901                 if(isNaN(this.vIndex)){
16902                     this.vIndex = 0;
16903                 }
16904                 
16905                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16906                 
16907                 break;
16908             case 38: // up
16909             case 40: // down
16910                 
16911                 dir = e.keyCode == 38 ? -1 : 1;
16912                 
16913                 this.vIndex = this.vIndex + dir * 4;
16914                 
16915                 if(this.vIndex < 0){
16916                     this.vIndex = 0;
16917                 }
16918                 
16919                 if(this.vIndex > 11){
16920                     this.vIndex = 11;
16921                 }
16922                 
16923                 if(isNaN(this.vIndex)){
16924                     this.vIndex = 0;
16925                 }
16926                 
16927                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16928                 break;
16929                 
16930             case 13: // enter
16931                 
16932                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16933                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16934                 }
16935                 
16936                 this.hide();
16937                 e.preventDefault();
16938                 break;
16939             case 9: // tab
16940                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16941                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16942                 }
16943                 this.hide();
16944                 break;
16945             case 16: // shift
16946             case 17: // ctrl
16947             case 18: // alt
16948                 break;
16949             default :
16950                 this.hide();
16951                 
16952         }
16953     },
16954     
16955     remove: function() 
16956     {
16957         this.picker().remove();
16958     }
16959    
16960 });
16961
16962 Roo.apply(Roo.bootstrap.MonthField,  {
16963     
16964     content : {
16965         tag: 'tbody',
16966         cn: [
16967         {
16968             tag: 'tr',
16969             cn: [
16970             {
16971                 tag: 'td',
16972                 colspan: '7'
16973             }
16974             ]
16975         }
16976         ]
16977     },
16978     
16979     dates:{
16980         en: {
16981             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16982             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
16983         }
16984     }
16985 });
16986
16987 Roo.apply(Roo.bootstrap.MonthField,  {
16988   
16989     template : {
16990         tag: 'div',
16991         cls: 'datepicker dropdown-menu roo-dynamic',
16992         cn: [
16993             {
16994                 tag: 'div',
16995                 cls: 'datepicker-months',
16996                 cn: [
16997                 {
16998                     tag: 'table',
16999                     cls: 'table-condensed',
17000                     cn:[
17001                         Roo.bootstrap.DateField.content
17002                     ]
17003                 }
17004                 ]
17005             }
17006         ]
17007     }
17008 });
17009
17010  
17011
17012  
17013  /*
17014  * - LGPL
17015  *
17016  * CheckBox
17017  * 
17018  */
17019
17020 /**
17021  * @class Roo.bootstrap.CheckBox
17022  * @extends Roo.bootstrap.Input
17023  * Bootstrap CheckBox class
17024  * 
17025  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17026  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17027  * @cfg {String} boxLabel The text that appears beside the checkbox
17028  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
17029  * @cfg {Boolean} checked initnal the element
17030  * @cfg {Boolean} inline inline the element (default false)
17031  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
17032  * 
17033  * @constructor
17034  * Create a new CheckBox
17035  * @param {Object} config The config object
17036  */
17037
17038 Roo.bootstrap.CheckBox = function(config){
17039     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
17040    
17041     this.addEvents({
17042         /**
17043         * @event check
17044         * Fires when the element is checked or unchecked.
17045         * @param {Roo.bootstrap.CheckBox} this This input
17046         * @param {Boolean} checked The new checked value
17047         */
17048        check : true
17049     });
17050     
17051 };
17052
17053 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
17054   
17055     inputType: 'checkbox',
17056     inputValue: 1,
17057     valueOff: 0,
17058     boxLabel: false,
17059     checked: false,
17060     weight : false,
17061     inline: false,
17062     
17063     getAutoCreate : function()
17064     {
17065         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17066         
17067         var id = Roo.id();
17068         
17069         var cfg = {};
17070         
17071         cfg.cls = 'form-group ' + this.inputType; //input-group
17072         
17073         if(this.inline){
17074             cfg.cls += ' ' + this.inputType + '-inline';
17075         }
17076         
17077         var input =  {
17078             tag: 'input',
17079             id : id,
17080             type : this.inputType,
17081             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
17082             cls : 'roo-' + this.inputType, //'form-box',
17083             placeholder : this.placeholder || ''
17084             
17085         };
17086         
17087         if (this.weight) { // Validity check?
17088             cfg.cls += " " + this.inputType + "-" + this.weight;
17089         }
17090         
17091         if (this.disabled) {
17092             input.disabled=true;
17093         }
17094         
17095         if(this.checked){
17096             input.checked = this.checked;
17097         }
17098         
17099         if (this.name) {
17100             input.name = this.name;
17101         }
17102         
17103         if (this.size) {
17104             input.cls += ' input-' + this.size;
17105         }
17106         
17107         var settings=this;
17108         
17109         ['xs','sm','md','lg'].map(function(size){
17110             if (settings[size]) {
17111                 cfg.cls += ' col-' + size + '-' + settings[size];
17112             }
17113         });
17114         
17115         var inputblock = input;
17116          
17117         if (this.before || this.after) {
17118             
17119             inputblock = {
17120                 cls : 'input-group',
17121                 cn :  [] 
17122             };
17123             
17124             if (this.before) {
17125                 inputblock.cn.push({
17126                     tag :'span',
17127                     cls : 'input-group-addon',
17128                     html : this.before
17129                 });
17130             }
17131             
17132             inputblock.cn.push(input);
17133             
17134             if (this.after) {
17135                 inputblock.cn.push({
17136                     tag :'span',
17137                     cls : 'input-group-addon',
17138                     html : this.after
17139                 });
17140             }
17141             
17142         }
17143         
17144         if (align ==='left' && this.fieldLabel.length) {
17145                 Roo.log("left and has label");
17146                 cfg.cn = [
17147                     
17148                     {
17149                         tag: 'label',
17150                         'for' :  id,
17151                         cls : 'control-label col-md-' + this.labelWidth,
17152                         html : this.fieldLabel
17153                         
17154                     },
17155                     {
17156                         cls : "col-md-" + (12 - this.labelWidth), 
17157                         cn: [
17158                             inputblock
17159                         ]
17160                     }
17161                     
17162                 ];
17163         } else if ( this.fieldLabel.length) {
17164                 Roo.log(" label");
17165                 cfg.cn = [
17166                    
17167                     {
17168                         tag: this.boxLabel ? 'span' : 'label',
17169                         'for': id,
17170                         cls: 'control-label box-input-label',
17171                         //cls : 'input-group-addon',
17172                         html : this.fieldLabel
17173                         
17174                     },
17175                     
17176                     inputblock
17177                     
17178                 ];
17179
17180         } else {
17181             
17182                 Roo.log(" no label && no align");
17183                 cfg.cn = [  inputblock ] ;
17184                 
17185                 
17186         }
17187         if(this.boxLabel){
17188              var boxLabelCfg = {
17189                 tag: 'label',
17190                 //'for': id, // box label is handled by onclick - so no for...
17191                 cls: 'box-label',
17192                 html: this.boxLabel
17193             }
17194             
17195             if(this.tooltip){
17196                 boxLabelCfg.tooltip = this.tooltip;
17197             }
17198              
17199             cfg.cn.push(boxLabelCfg);
17200         }
17201         
17202         
17203        
17204         return cfg;
17205         
17206     },
17207     
17208     /**
17209      * return the real input element.
17210      */
17211     inputEl: function ()
17212     {
17213         return this.el.select('input.roo-' + this.inputType,true).first();
17214     },
17215     
17216     labelEl: function()
17217     {
17218         return this.el.select('label.control-label',true).first();
17219     },
17220     /* depricated... */
17221     
17222     label: function()
17223     {
17224         return this.labelEl();
17225     },
17226     
17227     initEvents : function()
17228     {
17229 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17230         
17231         this.inputEl().on('click', this.onClick,  this);
17232         
17233         if (this.boxLabel) { 
17234             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
17235         }
17236         
17237         this.startValue = this.getValue();
17238         
17239         if(this.groupId){
17240             Roo.bootstrap.CheckBox.register(this);
17241         }
17242     },
17243     
17244     onClick : function()
17245     {   
17246         this.setChecked(!this.checked);
17247     },
17248     
17249     setChecked : function(state,suppressEvent)
17250     {
17251         this.startValue = this.getValue();
17252         
17253         if(this.inputType == 'radio'){
17254             
17255             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17256                 e.dom.checked = false;
17257             });
17258             
17259             this.inputEl().dom.checked = true;
17260             
17261             this.inputEl().dom.value = this.inputValue;
17262             
17263             if(suppressEvent !== true){
17264                 this.fireEvent('check', this, true);
17265             }
17266             
17267             this.validate();
17268             
17269             return;
17270         }
17271         
17272         this.checked = state;
17273         
17274         this.inputEl().dom.checked = state;
17275         
17276         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17277         
17278         if(suppressEvent !== true){
17279             this.fireEvent('check', this, state);
17280         }
17281         
17282         this.validate();
17283     },
17284     
17285     getValue : function()
17286     {
17287         if(this.inputType == 'radio'){
17288             return this.getGroupValue();
17289         }
17290         
17291         return this.inputEl().getValue();
17292         
17293     },
17294     
17295     getGroupValue : function()
17296     {
17297         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
17298             return '';
17299         }
17300         
17301         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
17302     },
17303     
17304     setValue : function(v,suppressEvent)
17305     {
17306         if(this.inputType == 'radio'){
17307             this.setGroupValue(v, suppressEvent);
17308             return;
17309         }
17310         
17311         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
17312         
17313         this.validate();
17314     },
17315     
17316     setGroupValue : function(v, suppressEvent)
17317     {
17318         this.startValue = this.getValue();
17319         
17320         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17321             e.dom.checked = false;
17322             
17323             if(e.dom.value == v){
17324                 e.dom.checked = true;
17325             }
17326         });
17327         
17328         if(suppressEvent !== true){
17329             this.fireEvent('check', this, true);
17330         }
17331
17332         this.validate();
17333         
17334         return;
17335     },
17336     
17337     validate : function()
17338     {
17339         if(
17340                 this.disabled || 
17341                 (this.inputType == 'radio' && this.validateRadio()) ||
17342                 (this.inputType == 'checkbox' && this.validateCheckbox())
17343         ){
17344             this.markValid();
17345             return true;
17346         }
17347         
17348         this.markInvalid();
17349         return false;
17350     },
17351     
17352     validateRadio : function()
17353     {
17354         var valid = false;
17355         
17356         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17357             if(!e.dom.checked){
17358                 return;
17359             }
17360             
17361             valid = true;
17362             
17363             return false;
17364         });
17365         
17366         return valid;
17367     },
17368     
17369     validateCheckbox : function()
17370     {
17371         if(!this.groupId){
17372             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
17373         }
17374         
17375         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17376         
17377         if(!group){
17378             return false;
17379         }
17380         
17381         var r = false;
17382         
17383         for(var i in group){
17384             if(r){
17385                 break;
17386             }
17387             
17388             r = (group[i].getValue() == group[i].inputValue) ? true : false;
17389         }
17390         
17391         return r;
17392     },
17393     
17394     /**
17395      * Mark this field as valid
17396      */
17397     markValid : function()
17398     {
17399         if(this.allowBlank){
17400             return;
17401         }
17402         
17403         var _this = this;
17404         
17405         this.fireEvent('valid', this);
17406         
17407         if(this.inputType == 'radio'){
17408             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17409                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17410                 e.findParent('.form-group', false, true).addClass(_this.validClass);
17411             });
17412             
17413             return;
17414         }
17415         
17416         if(!this.groupId){
17417             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17418             this.el.findParent('.form-group', false, true).addClass(this.validClass);
17419             return;
17420         }
17421         
17422         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17423             
17424         if(!group){
17425             return;
17426         }
17427         
17428         for(var i in group){
17429             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17430             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
17431         }
17432     },
17433     
17434      /**
17435      * Mark this field as invalid
17436      * @param {String} msg The validation message
17437      */
17438     markInvalid : function(msg)
17439     {
17440         if(this.allowBlank){
17441             return;
17442         }
17443         
17444         var _this = this;
17445         
17446         this.fireEvent('invalid', this, msg);
17447         
17448         if(this.inputType == 'radio'){
17449             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17450                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17451                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
17452             });
17453             
17454             return;
17455         }
17456         
17457         if(!this.groupId){
17458             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17459             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
17460             return;
17461         }
17462         
17463         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17464             
17465         if(!group){
17466             return;
17467         }
17468         
17469         for(var i in group){
17470             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17471             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
17472         }
17473         
17474     }
17475     
17476 });
17477
17478 Roo.apply(Roo.bootstrap.CheckBox, {
17479     
17480     groups: {},
17481     
17482      /**
17483     * register a CheckBox Group
17484     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
17485     */
17486     register : function(checkbox)
17487     {
17488         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
17489             this.groups[checkbox.groupId] = {};
17490         }
17491         
17492         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
17493             return;
17494         }
17495         
17496         this.groups[checkbox.groupId][checkbox.name] = checkbox;
17497         
17498     },
17499     /**
17500     * fetch a CheckBox Group based on the group ID
17501     * @param {string} the group ID
17502     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
17503     */
17504     get: function(groupId) {
17505         if (typeof(this.groups[groupId]) == 'undefined') {
17506             return false;
17507         }
17508         
17509         return this.groups[groupId] ;
17510     }
17511     
17512     
17513 });
17514 /*
17515  * - LGPL
17516  *
17517  * Radio
17518  *
17519  *
17520  * not inline
17521  *<div class="radio">
17522   <label>
17523     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
17524     Option one is this and that&mdash;be sure to include why it's great
17525   </label>
17526 </div>
17527  *
17528  *
17529  *inline
17530  *<span>
17531  *<label class="radio-inline">fieldLabel</label>
17532  *<label class="radio-inline">
17533   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
17534 </label>
17535 <span>
17536  * 
17537  * 
17538  */
17539
17540 /**
17541  * @class Roo.bootstrap.Radio
17542  * @extends Roo.bootstrap.CheckBox
17543  * Bootstrap Radio class
17544
17545  * @constructor
17546  * Create a new Radio
17547  * @param {Object} config The config object
17548  */
17549
17550 Roo.bootstrap.Radio = function(config){
17551     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
17552    
17553 };
17554
17555 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
17556     
17557     inputType: 'radio',
17558     inputValue: '',
17559     valueOff: '',
17560     
17561     getAutoCreate : function()
17562     {
17563         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17564         align = align || 'left'; // default...
17565         
17566         
17567         
17568         var id = Roo.id();
17569         
17570         var cfg = {
17571                 tag : this.inline ? 'span' : 'div',
17572                 cls : '',
17573                 cn : []
17574         };
17575         
17576         var inline = this.inline ? ' radio-inline' : '';
17577         
17578         var lbl = {
17579                 tag: 'label' ,
17580                 // does not need for, as we wrap the input with it..
17581                 'for' : id,
17582                 cls : 'control-label box-label' + inline,
17583                 cn : []
17584         };
17585         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
17586         
17587         var fieldLabel = {
17588             tag: 'label' ,
17589             //cls : 'control-label' + inline,
17590             html : this.fieldLabel,
17591             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
17592         };
17593         
17594  
17595         
17596         
17597         var input =  {
17598             tag: 'input',
17599             id : id,
17600             type : this.inputType,
17601             //value : (!this.checked) ? this.valueOff : this.inputValue,
17602             value : this.inputValue,
17603             cls : 'roo-radio',
17604             placeholder : this.placeholder || '' // ?? needed????
17605             
17606         };
17607         if (this.weight) { // Validity check?
17608             input.cls += " radio-" + this.weight;
17609         }
17610         if (this.disabled) {
17611             input.disabled=true;
17612         }
17613         
17614         if(this.checked){
17615             input.checked = this.checked;
17616         }
17617         
17618         if (this.name) {
17619             input.name = this.name;
17620         }
17621         
17622         if (this.size) {
17623             input.cls += ' input-' + this.size;
17624         }
17625         
17626         //?? can span's inline have a width??
17627         
17628         var settings=this;
17629         ['xs','sm','md','lg'].map(function(size){
17630             if (settings[size]) {
17631                 cfg.cls += ' col-' + size + '-' + settings[size];
17632             }
17633         });
17634         
17635         var inputblock = input;
17636         
17637         if (this.before || this.after) {
17638             
17639             inputblock = {
17640                 cls : 'input-group',
17641                 tag : 'span',
17642                 cn :  [] 
17643             };
17644             if (this.before) {
17645                 inputblock.cn.push({
17646                     tag :'span',
17647                     cls : 'input-group-addon',
17648                     html : this.before
17649                 });
17650             }
17651             inputblock.cn.push(input);
17652             if (this.after) {
17653                 inputblock.cn.push({
17654                     tag :'span',
17655                     cls : 'input-group-addon',
17656                     html : this.after
17657                 });
17658             }
17659             
17660         };
17661         
17662         
17663         if (this.fieldLabel && this.fieldLabel.length) {
17664             cfg.cn.push(fieldLabel);
17665         }
17666        
17667         // normal bootstrap puts the input inside the label.
17668         // however with our styled version - it has to go after the input.
17669        
17670         //lbl.cn.push(inputblock);
17671         
17672         var lblwrap =  {
17673             tag: 'span',
17674             cls: 'radio' + inline,
17675             cn: [
17676                 inputblock,
17677                 lbl
17678             ]
17679         };
17680         
17681         cfg.cn.push( lblwrap);
17682         
17683         if(this.boxLabel){
17684             lbl.cn.push({
17685                 tag: 'span',
17686                 html: this.boxLabel
17687             })
17688         }
17689          
17690         
17691         return cfg;
17692         
17693     },
17694     
17695     initEvents : function()
17696     {
17697 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17698         
17699         this.inputEl().on('click', this.onClick,  this);
17700         if (this.boxLabel) {
17701             Roo.log('find label')
17702             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
17703         }
17704         
17705     },
17706     
17707     inputEl: function ()
17708     {
17709         return this.el.select('input.roo-radio',true).first();
17710     },
17711     onClick : function()
17712     {   
17713         Roo.log("click");
17714         this.setChecked(true);
17715     },
17716     
17717     setChecked : function(state,suppressEvent)
17718     {
17719         if(state){
17720             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17721                 v.dom.checked = false;
17722             });
17723         }
17724         Roo.log(this.inputEl().dom);
17725         this.checked = state;
17726         this.inputEl().dom.checked = state;
17727         
17728         if(suppressEvent !== true){
17729             this.fireEvent('check', this, state);
17730         }
17731         
17732         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17733         
17734     },
17735     
17736     getGroupValue : function()
17737     {
17738         var value = '';
17739         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17740             if(v.dom.checked == true){
17741                 value = v.dom.value;
17742             }
17743         });
17744         
17745         return value;
17746     },
17747     
17748     /**
17749      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
17750      * @return {Mixed} value The field value
17751      */
17752     getValue : function(){
17753         return this.getGroupValue();
17754     }
17755     
17756 });
17757
17758  
17759 //<script type="text/javascript">
17760
17761 /*
17762  * Based  Ext JS Library 1.1.1
17763  * Copyright(c) 2006-2007, Ext JS, LLC.
17764  * LGPL
17765  *
17766  */
17767  
17768 /**
17769  * @class Roo.HtmlEditorCore
17770  * @extends Roo.Component
17771  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
17772  *
17773  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
17774  */
17775
17776 Roo.HtmlEditorCore = function(config){
17777     
17778     
17779     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
17780     
17781     
17782     this.addEvents({
17783         /**
17784          * @event initialize
17785          * Fires when the editor is fully initialized (including the iframe)
17786          * @param {Roo.HtmlEditorCore} this
17787          */
17788         initialize: true,
17789         /**
17790          * @event activate
17791          * Fires when the editor is first receives the focus. Any insertion must wait
17792          * until after this event.
17793          * @param {Roo.HtmlEditorCore} this
17794          */
17795         activate: true,
17796          /**
17797          * @event beforesync
17798          * Fires before the textarea is updated with content from the editor iframe. Return false
17799          * to cancel the sync.
17800          * @param {Roo.HtmlEditorCore} this
17801          * @param {String} html
17802          */
17803         beforesync: true,
17804          /**
17805          * @event beforepush
17806          * Fires before the iframe editor is updated with content from the textarea. Return false
17807          * to cancel the push.
17808          * @param {Roo.HtmlEditorCore} this
17809          * @param {String} html
17810          */
17811         beforepush: true,
17812          /**
17813          * @event sync
17814          * Fires when the textarea is updated with content from the editor iframe.
17815          * @param {Roo.HtmlEditorCore} this
17816          * @param {String} html
17817          */
17818         sync: true,
17819          /**
17820          * @event push
17821          * Fires when the iframe editor is updated with content from the textarea.
17822          * @param {Roo.HtmlEditorCore} this
17823          * @param {String} html
17824          */
17825         push: true,
17826         
17827         /**
17828          * @event editorevent
17829          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17830          * @param {Roo.HtmlEditorCore} this
17831          */
17832         editorevent: true
17833         
17834     });
17835     
17836     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
17837     
17838     // defaults : white / black...
17839     this.applyBlacklists();
17840     
17841     
17842     
17843 };
17844
17845
17846 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
17847
17848
17849      /**
17850      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
17851      */
17852     
17853     owner : false,
17854     
17855      /**
17856      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17857      *                        Roo.resizable.
17858      */
17859     resizable : false,
17860      /**
17861      * @cfg {Number} height (in pixels)
17862      */   
17863     height: 300,
17864    /**
17865      * @cfg {Number} width (in pixels)
17866      */   
17867     width: 500,
17868     
17869     /**
17870      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17871      * 
17872      */
17873     stylesheets: false,
17874     
17875     // id of frame..
17876     frameId: false,
17877     
17878     // private properties
17879     validationEvent : false,
17880     deferHeight: true,
17881     initialized : false,
17882     activated : false,
17883     sourceEditMode : false,
17884     onFocus : Roo.emptyFn,
17885     iframePad:3,
17886     hideMode:'offsets',
17887     
17888     clearUp: true,
17889     
17890     // blacklist + whitelisted elements..
17891     black: false,
17892     white: false,
17893      
17894     
17895
17896     /**
17897      * Protected method that will not generally be called directly. It
17898      * is called when the editor initializes the iframe with HTML contents. Override this method if you
17899      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
17900      */
17901     getDocMarkup : function(){
17902         // body styles..
17903         var st = '';
17904         
17905         // inherit styels from page...?? 
17906         if (this.stylesheets === false) {
17907             
17908             Roo.get(document.head).select('style').each(function(node) {
17909                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17910             });
17911             
17912             Roo.get(document.head).select('link').each(function(node) { 
17913                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17914             });
17915             
17916         } else if (!this.stylesheets.length) {
17917                 // simple..
17918                 st = '<style type="text/css">' +
17919                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17920                    '</style>';
17921         } else { 
17922             
17923         }
17924         
17925         st +=  '<style type="text/css">' +
17926             'IMG { cursor: pointer } ' +
17927         '</style>';
17928
17929         
17930         return '<html><head>' + st  +
17931             //<style type="text/css">' +
17932             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17933             //'</style>' +
17934             ' </head><body class="roo-htmleditor-body"></body></html>';
17935     },
17936
17937     // private
17938     onRender : function(ct, position)
17939     {
17940         var _t = this;
17941         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
17942         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
17943         
17944         
17945         this.el.dom.style.border = '0 none';
17946         this.el.dom.setAttribute('tabIndex', -1);
17947         this.el.addClass('x-hidden hide');
17948         
17949         
17950         
17951         if(Roo.isIE){ // fix IE 1px bogus margin
17952             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
17953         }
17954        
17955         
17956         this.frameId = Roo.id();
17957         
17958          
17959         
17960         var iframe = this.owner.wrap.createChild({
17961             tag: 'iframe',
17962             cls: 'form-control', // bootstrap..
17963             id: this.frameId,
17964             name: this.frameId,
17965             frameBorder : 'no',
17966             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
17967         }, this.el
17968         );
17969         
17970         
17971         this.iframe = iframe.dom;
17972
17973          this.assignDocWin();
17974         
17975         this.doc.designMode = 'on';
17976        
17977         this.doc.open();
17978         this.doc.write(this.getDocMarkup());
17979         this.doc.close();
17980
17981         
17982         var task = { // must defer to wait for browser to be ready
17983             run : function(){
17984                 //console.log("run task?" + this.doc.readyState);
17985                 this.assignDocWin();
17986                 if(this.doc.body || this.doc.readyState == 'complete'){
17987                     try {
17988                         this.doc.designMode="on";
17989                     } catch (e) {
17990                         return;
17991                     }
17992                     Roo.TaskMgr.stop(task);
17993                     this.initEditor.defer(10, this);
17994                 }
17995             },
17996             interval : 10,
17997             duration: 10000,
17998             scope: this
17999         };
18000         Roo.TaskMgr.start(task);
18001
18002     },
18003
18004     // private
18005     onResize : function(w, h)
18006     {
18007          Roo.log('resize: ' +w + ',' + h );
18008         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18009         if(!this.iframe){
18010             return;
18011         }
18012         if(typeof w == 'number'){
18013             
18014             this.iframe.style.width = w + 'px';
18015         }
18016         if(typeof h == 'number'){
18017             
18018             this.iframe.style.height = h + 'px';
18019             if(this.doc){
18020                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18021             }
18022         }
18023         
18024     },
18025
18026     /**
18027      * Toggles the editor between standard and source edit mode.
18028      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18029      */
18030     toggleSourceEdit : function(sourceEditMode){
18031         
18032         this.sourceEditMode = sourceEditMode === true;
18033         
18034         if(this.sourceEditMode){
18035  
18036             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
18037             
18038         }else{
18039             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
18040             //this.iframe.className = '';
18041             this.deferFocus();
18042         }
18043         //this.setSize(this.owner.wrap.getSize());
18044         //this.fireEvent('editmodechange', this, this.sourceEditMode);
18045     },
18046
18047     
18048   
18049
18050     /**
18051      * Protected method that will not generally be called directly. If you need/want
18052      * custom HTML cleanup, this is the method you should override.
18053      * @param {String} html The HTML to be cleaned
18054      * return {String} The cleaned HTML
18055      */
18056     cleanHtml : function(html){
18057         html = String(html);
18058         if(html.length > 5){
18059             if(Roo.isSafari){ // strip safari nonsense
18060                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
18061             }
18062         }
18063         if(html == '&nbsp;'){
18064             html = '';
18065         }
18066         return html;
18067     },
18068
18069     /**
18070      * HTML Editor -> Textarea
18071      * Protected method that will not generally be called directly. Syncs the contents
18072      * of the editor iframe with the textarea.
18073      */
18074     syncValue : function(){
18075         if(this.initialized){
18076             var bd = (this.doc.body || this.doc.documentElement);
18077             //this.cleanUpPaste(); -- this is done else where and causes havoc..
18078             var html = bd.innerHTML;
18079             if(Roo.isSafari){
18080                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
18081                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
18082                 if(m && m[1]){
18083                     html = '<div style="'+m[0]+'">' + html + '</div>';
18084                 }
18085             }
18086             html = this.cleanHtml(html);
18087             // fix up the special chars.. normaly like back quotes in word...
18088             // however we do not want to do this with chinese..
18089             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
18090                 var cc = b.charCodeAt();
18091                 if (
18092                     (cc >= 0x4E00 && cc < 0xA000 ) ||
18093                     (cc >= 0x3400 && cc < 0x4E00 ) ||
18094                     (cc >= 0xf900 && cc < 0xfb00 )
18095                 ) {
18096                         return b;
18097                 }
18098                 return "&#"+cc+";" 
18099             });
18100             if(this.owner.fireEvent('beforesync', this, html) !== false){
18101                 this.el.dom.value = html;
18102                 this.owner.fireEvent('sync', this, html);
18103             }
18104         }
18105     },
18106
18107     /**
18108      * Protected method that will not generally be called directly. Pushes the value of the textarea
18109      * into the iframe editor.
18110      */
18111     pushValue : function(){
18112         if(this.initialized){
18113             var v = this.el.dom.value.trim();
18114             
18115 //            if(v.length < 1){
18116 //                v = '&#160;';
18117 //            }
18118             
18119             if(this.owner.fireEvent('beforepush', this, v) !== false){
18120                 var d = (this.doc.body || this.doc.documentElement);
18121                 d.innerHTML = v;
18122                 this.cleanUpPaste();
18123                 this.el.dom.value = d.innerHTML;
18124                 this.owner.fireEvent('push', this, v);
18125             }
18126         }
18127     },
18128
18129     // private
18130     deferFocus : function(){
18131         this.focus.defer(10, this);
18132     },
18133
18134     // doc'ed in Field
18135     focus : function(){
18136         if(this.win && !this.sourceEditMode){
18137             this.win.focus();
18138         }else{
18139             this.el.focus();
18140         }
18141     },
18142     
18143     assignDocWin: function()
18144     {
18145         var iframe = this.iframe;
18146         
18147          if(Roo.isIE){
18148             this.doc = iframe.contentWindow.document;
18149             this.win = iframe.contentWindow;
18150         } else {
18151 //            if (!Roo.get(this.frameId)) {
18152 //                return;
18153 //            }
18154 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18155 //            this.win = Roo.get(this.frameId).dom.contentWindow;
18156             
18157             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
18158                 return;
18159             }
18160             
18161             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18162             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
18163         }
18164     },
18165     
18166     // private
18167     initEditor : function(){
18168         //console.log("INIT EDITOR");
18169         this.assignDocWin();
18170         
18171         
18172         
18173         this.doc.designMode="on";
18174         this.doc.open();
18175         this.doc.write(this.getDocMarkup());
18176         this.doc.close();
18177         
18178         var dbody = (this.doc.body || this.doc.documentElement);
18179         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
18180         // this copies styles from the containing element into thsi one..
18181         // not sure why we need all of this..
18182         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
18183         
18184         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
18185         //ss['background-attachment'] = 'fixed'; // w3c
18186         dbody.bgProperties = 'fixed'; // ie
18187         //Roo.DomHelper.applyStyles(dbody, ss);
18188         Roo.EventManager.on(this.doc, {
18189             //'mousedown': this.onEditorEvent,
18190             'mouseup': this.onEditorEvent,
18191             'dblclick': this.onEditorEvent,
18192             'click': this.onEditorEvent,
18193             'keyup': this.onEditorEvent,
18194             buffer:100,
18195             scope: this
18196         });
18197         if(Roo.isGecko){
18198             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
18199         }
18200         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
18201             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
18202         }
18203         this.initialized = true;
18204
18205         this.owner.fireEvent('initialize', this);
18206         this.pushValue();
18207     },
18208
18209     // private
18210     onDestroy : function(){
18211         
18212         
18213         
18214         if(this.rendered){
18215             
18216             //for (var i =0; i < this.toolbars.length;i++) {
18217             //    // fixme - ask toolbars for heights?
18218             //    this.toolbars[i].onDestroy();
18219            // }
18220             
18221             //this.wrap.dom.innerHTML = '';
18222             //this.wrap.remove();
18223         }
18224     },
18225
18226     // private
18227     onFirstFocus : function(){
18228         
18229         this.assignDocWin();
18230         
18231         
18232         this.activated = true;
18233          
18234     
18235         if(Roo.isGecko){ // prevent silly gecko errors
18236             this.win.focus();
18237             var s = this.win.getSelection();
18238             if(!s.focusNode || s.focusNode.nodeType != 3){
18239                 var r = s.getRangeAt(0);
18240                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
18241                 r.collapse(true);
18242                 this.deferFocus();
18243             }
18244             try{
18245                 this.execCmd('useCSS', true);
18246                 this.execCmd('styleWithCSS', false);
18247             }catch(e){}
18248         }
18249         this.owner.fireEvent('activate', this);
18250     },
18251
18252     // private
18253     adjustFont: function(btn){
18254         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
18255         //if(Roo.isSafari){ // safari
18256         //    adjust *= 2;
18257        // }
18258         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
18259         if(Roo.isSafari){ // safari
18260             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
18261             v =  (v < 10) ? 10 : v;
18262             v =  (v > 48) ? 48 : v;
18263             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
18264             
18265         }
18266         
18267         
18268         v = Math.max(1, v+adjust);
18269         
18270         this.execCmd('FontSize', v  );
18271     },
18272
18273     onEditorEvent : function(e){
18274         this.owner.fireEvent('editorevent', this, e);
18275       //  this.updateToolbar();
18276         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
18277     },
18278
18279     insertTag : function(tg)
18280     {
18281         // could be a bit smarter... -> wrap the current selected tRoo..
18282         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
18283             
18284             range = this.createRange(this.getSelection());
18285             var wrappingNode = this.doc.createElement(tg.toLowerCase());
18286             wrappingNode.appendChild(range.extractContents());
18287             range.insertNode(wrappingNode);
18288
18289             return;
18290             
18291             
18292             
18293         }
18294         this.execCmd("formatblock",   tg);
18295         
18296     },
18297     
18298     insertText : function(txt)
18299     {
18300         
18301         
18302         var range = this.createRange();
18303         range.deleteContents();
18304                //alert(Sender.getAttribute('label'));
18305                
18306         range.insertNode(this.doc.createTextNode(txt));
18307     } ,
18308     
18309      
18310
18311     /**
18312      * Executes a Midas editor command on the editor document and performs necessary focus and
18313      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
18314      * @param {String} cmd The Midas command
18315      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18316      */
18317     relayCmd : function(cmd, value){
18318         this.win.focus();
18319         this.execCmd(cmd, value);
18320         this.owner.fireEvent('editorevent', this);
18321         //this.updateToolbar();
18322         this.owner.deferFocus();
18323     },
18324
18325     /**
18326      * Executes a Midas editor command directly on the editor document.
18327      * For visual commands, you should use {@link #relayCmd} instead.
18328      * <b>This should only be called after the editor is initialized.</b>
18329      * @param {String} cmd The Midas command
18330      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18331      */
18332     execCmd : function(cmd, value){
18333         this.doc.execCommand(cmd, false, value === undefined ? null : value);
18334         this.syncValue();
18335     },
18336  
18337  
18338    
18339     /**
18340      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
18341      * to insert tRoo.
18342      * @param {String} text | dom node.. 
18343      */
18344     insertAtCursor : function(text)
18345     {
18346         
18347         
18348         
18349         if(!this.activated){
18350             return;
18351         }
18352         /*
18353         if(Roo.isIE){
18354             this.win.focus();
18355             var r = this.doc.selection.createRange();
18356             if(r){
18357                 r.collapse(true);
18358                 r.pasteHTML(text);
18359                 this.syncValue();
18360                 this.deferFocus();
18361             
18362             }
18363             return;
18364         }
18365         */
18366         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
18367             this.win.focus();
18368             
18369             
18370             // from jquery ui (MIT licenced)
18371             var range, node;
18372             var win = this.win;
18373             
18374             if (win.getSelection && win.getSelection().getRangeAt) {
18375                 range = win.getSelection().getRangeAt(0);
18376                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
18377                 range.insertNode(node);
18378             } else if (win.document.selection && win.document.selection.createRange) {
18379                 // no firefox support
18380                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18381                 win.document.selection.createRange().pasteHTML(txt);
18382             } else {
18383                 // no firefox support
18384                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18385                 this.execCmd('InsertHTML', txt);
18386             } 
18387             
18388             this.syncValue();
18389             
18390             this.deferFocus();
18391         }
18392     },
18393  // private
18394     mozKeyPress : function(e){
18395         if(e.ctrlKey){
18396             var c = e.getCharCode(), cmd;
18397           
18398             if(c > 0){
18399                 c = String.fromCharCode(c).toLowerCase();
18400                 switch(c){
18401                     case 'b':
18402                         cmd = 'bold';
18403                         break;
18404                     case 'i':
18405                         cmd = 'italic';
18406                         break;
18407                     
18408                     case 'u':
18409                         cmd = 'underline';
18410                         break;
18411                     
18412                     case 'v':
18413                         this.cleanUpPaste.defer(100, this);
18414                         return;
18415                         
18416                 }
18417                 if(cmd){
18418                     this.win.focus();
18419                     this.execCmd(cmd);
18420                     this.deferFocus();
18421                     e.preventDefault();
18422                 }
18423                 
18424             }
18425         }
18426     },
18427
18428     // private
18429     fixKeys : function(){ // load time branching for fastest keydown performance
18430         if(Roo.isIE){
18431             return function(e){
18432                 var k = e.getKey(), r;
18433                 if(k == e.TAB){
18434                     e.stopEvent();
18435                     r = this.doc.selection.createRange();
18436                     if(r){
18437                         r.collapse(true);
18438                         r.pasteHTML('&#160;&#160;&#160;&#160;');
18439                         this.deferFocus();
18440                     }
18441                     return;
18442                 }
18443                 
18444                 if(k == e.ENTER){
18445                     r = this.doc.selection.createRange();
18446                     if(r){
18447                         var target = r.parentElement();
18448                         if(!target || target.tagName.toLowerCase() != 'li'){
18449                             e.stopEvent();
18450                             r.pasteHTML('<br />');
18451                             r.collapse(false);
18452                             r.select();
18453                         }
18454                     }
18455                 }
18456                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18457                     this.cleanUpPaste.defer(100, this);
18458                     return;
18459                 }
18460                 
18461                 
18462             };
18463         }else if(Roo.isOpera){
18464             return function(e){
18465                 var k = e.getKey();
18466                 if(k == e.TAB){
18467                     e.stopEvent();
18468                     this.win.focus();
18469                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
18470                     this.deferFocus();
18471                 }
18472                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18473                     this.cleanUpPaste.defer(100, this);
18474                     return;
18475                 }
18476                 
18477             };
18478         }else if(Roo.isSafari){
18479             return function(e){
18480                 var k = e.getKey();
18481                 
18482                 if(k == e.TAB){
18483                     e.stopEvent();
18484                     this.execCmd('InsertText','\t');
18485                     this.deferFocus();
18486                     return;
18487                 }
18488                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18489                     this.cleanUpPaste.defer(100, this);
18490                     return;
18491                 }
18492                 
18493              };
18494         }
18495     }(),
18496     
18497     getAllAncestors: function()
18498     {
18499         var p = this.getSelectedNode();
18500         var a = [];
18501         if (!p) {
18502             a.push(p); // push blank onto stack..
18503             p = this.getParentElement();
18504         }
18505         
18506         
18507         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
18508             a.push(p);
18509             p = p.parentNode;
18510         }
18511         a.push(this.doc.body);
18512         return a;
18513     },
18514     lastSel : false,
18515     lastSelNode : false,
18516     
18517     
18518     getSelection : function() 
18519     {
18520         this.assignDocWin();
18521         return Roo.isIE ? this.doc.selection : this.win.getSelection();
18522     },
18523     
18524     getSelectedNode: function() 
18525     {
18526         // this may only work on Gecko!!!
18527         
18528         // should we cache this!!!!
18529         
18530         
18531         
18532          
18533         var range = this.createRange(this.getSelection()).cloneRange();
18534         
18535         if (Roo.isIE) {
18536             var parent = range.parentElement();
18537             while (true) {
18538                 var testRange = range.duplicate();
18539                 testRange.moveToElementText(parent);
18540                 if (testRange.inRange(range)) {
18541                     break;
18542                 }
18543                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
18544                     break;
18545                 }
18546                 parent = parent.parentElement;
18547             }
18548             return parent;
18549         }
18550         
18551         // is ancestor a text element.
18552         var ac =  range.commonAncestorContainer;
18553         if (ac.nodeType == 3) {
18554             ac = ac.parentNode;
18555         }
18556         
18557         var ar = ac.childNodes;
18558          
18559         var nodes = [];
18560         var other_nodes = [];
18561         var has_other_nodes = false;
18562         for (var i=0;i<ar.length;i++) {
18563             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
18564                 continue;
18565             }
18566             // fullly contained node.
18567             
18568             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
18569                 nodes.push(ar[i]);
18570                 continue;
18571             }
18572             
18573             // probably selected..
18574             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
18575                 other_nodes.push(ar[i]);
18576                 continue;
18577             }
18578             // outer..
18579             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
18580                 continue;
18581             }
18582             
18583             
18584             has_other_nodes = true;
18585         }
18586         if (!nodes.length && other_nodes.length) {
18587             nodes= other_nodes;
18588         }
18589         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
18590             return false;
18591         }
18592         
18593         return nodes[0];
18594     },
18595     createRange: function(sel)
18596     {
18597         // this has strange effects when using with 
18598         // top toolbar - not sure if it's a great idea.
18599         //this.editor.contentWindow.focus();
18600         if (typeof sel != "undefined") {
18601             try {
18602                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
18603             } catch(e) {
18604                 return this.doc.createRange();
18605             }
18606         } else {
18607             return this.doc.createRange();
18608         }
18609     },
18610     getParentElement: function()
18611     {
18612         
18613         this.assignDocWin();
18614         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
18615         
18616         var range = this.createRange(sel);
18617          
18618         try {
18619             var p = range.commonAncestorContainer;
18620             while (p.nodeType == 3) { // text node
18621                 p = p.parentNode;
18622             }
18623             return p;
18624         } catch (e) {
18625             return null;
18626         }
18627     
18628     },
18629     /***
18630      *
18631      * Range intersection.. the hard stuff...
18632      *  '-1' = before
18633      *  '0' = hits..
18634      *  '1' = after.
18635      *         [ -- selected range --- ]
18636      *   [fail]                        [fail]
18637      *
18638      *    basically..
18639      *      if end is before start or  hits it. fail.
18640      *      if start is after end or hits it fail.
18641      *
18642      *   if either hits (but other is outside. - then it's not 
18643      *   
18644      *    
18645      **/
18646     
18647     
18648     // @see http://www.thismuchiknow.co.uk/?p=64.
18649     rangeIntersectsNode : function(range, node)
18650     {
18651         var nodeRange = node.ownerDocument.createRange();
18652         try {
18653             nodeRange.selectNode(node);
18654         } catch (e) {
18655             nodeRange.selectNodeContents(node);
18656         }
18657     
18658         var rangeStartRange = range.cloneRange();
18659         rangeStartRange.collapse(true);
18660     
18661         var rangeEndRange = range.cloneRange();
18662         rangeEndRange.collapse(false);
18663     
18664         var nodeStartRange = nodeRange.cloneRange();
18665         nodeStartRange.collapse(true);
18666     
18667         var nodeEndRange = nodeRange.cloneRange();
18668         nodeEndRange.collapse(false);
18669     
18670         return rangeStartRange.compareBoundaryPoints(
18671                  Range.START_TO_START, nodeEndRange) == -1 &&
18672                rangeEndRange.compareBoundaryPoints(
18673                  Range.START_TO_START, nodeStartRange) == 1;
18674         
18675          
18676     },
18677     rangeCompareNode : function(range, node)
18678     {
18679         var nodeRange = node.ownerDocument.createRange();
18680         try {
18681             nodeRange.selectNode(node);
18682         } catch (e) {
18683             nodeRange.selectNodeContents(node);
18684         }
18685         
18686         
18687         range.collapse(true);
18688     
18689         nodeRange.collapse(true);
18690      
18691         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
18692         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
18693          
18694         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
18695         
18696         var nodeIsBefore   =  ss == 1;
18697         var nodeIsAfter    = ee == -1;
18698         
18699         if (nodeIsBefore && nodeIsAfter)
18700             return 0; // outer
18701         if (!nodeIsBefore && nodeIsAfter)
18702             return 1; //right trailed.
18703         
18704         if (nodeIsBefore && !nodeIsAfter)
18705             return 2;  // left trailed.
18706         // fully contined.
18707         return 3;
18708     },
18709
18710     // private? - in a new class?
18711     cleanUpPaste :  function()
18712     {
18713         // cleans up the whole document..
18714         Roo.log('cleanuppaste');
18715         
18716         this.cleanUpChildren(this.doc.body);
18717         var clean = this.cleanWordChars(this.doc.body.innerHTML);
18718         if (clean != this.doc.body.innerHTML) {
18719             this.doc.body.innerHTML = clean;
18720         }
18721         
18722     },
18723     
18724     cleanWordChars : function(input) {// change the chars to hex code
18725         var he = Roo.HtmlEditorCore;
18726         
18727         var output = input;
18728         Roo.each(he.swapCodes, function(sw) { 
18729             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
18730             
18731             output = output.replace(swapper, sw[1]);
18732         });
18733         
18734         return output;
18735     },
18736     
18737     
18738     cleanUpChildren : function (n)
18739     {
18740         if (!n.childNodes.length) {
18741             return;
18742         }
18743         for (var i = n.childNodes.length-1; i > -1 ; i--) {
18744            this.cleanUpChild(n.childNodes[i]);
18745         }
18746     },
18747     
18748     
18749         
18750     
18751     cleanUpChild : function (node)
18752     {
18753         var ed = this;
18754         //console.log(node);
18755         if (node.nodeName == "#text") {
18756             // clean up silly Windows -- stuff?
18757             return; 
18758         }
18759         if (node.nodeName == "#comment") {
18760             node.parentNode.removeChild(node);
18761             // clean up silly Windows -- stuff?
18762             return; 
18763         }
18764         var lcname = node.tagName.toLowerCase();
18765         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
18766         // whitelist of tags..
18767         
18768         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
18769             // remove node.
18770             node.parentNode.removeChild(node);
18771             return;
18772             
18773         }
18774         
18775         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
18776         
18777         // remove <a name=....> as rendering on yahoo mailer is borked with this.
18778         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
18779         
18780         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
18781         //    remove_keep_children = true;
18782         //}
18783         
18784         if (remove_keep_children) {
18785             this.cleanUpChildren(node);
18786             // inserts everything just before this node...
18787             while (node.childNodes.length) {
18788                 var cn = node.childNodes[0];
18789                 node.removeChild(cn);
18790                 node.parentNode.insertBefore(cn, node);
18791             }
18792             node.parentNode.removeChild(node);
18793             return;
18794         }
18795         
18796         if (!node.attributes || !node.attributes.length) {
18797             this.cleanUpChildren(node);
18798             return;
18799         }
18800         
18801         function cleanAttr(n,v)
18802         {
18803             
18804             if (v.match(/^\./) || v.match(/^\//)) {
18805                 return;
18806             }
18807             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
18808                 return;
18809             }
18810             if (v.match(/^#/)) {
18811                 return;
18812             }
18813 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
18814             node.removeAttribute(n);
18815             
18816         }
18817         
18818         var cwhite = this.cwhite;
18819         var cblack = this.cblack;
18820             
18821         function cleanStyle(n,v)
18822         {
18823             if (v.match(/expression/)) { //XSS?? should we even bother..
18824                 node.removeAttribute(n);
18825                 return;
18826             }
18827             
18828             var parts = v.split(/;/);
18829             var clean = [];
18830             
18831             Roo.each(parts, function(p) {
18832                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
18833                 if (!p.length) {
18834                     return true;
18835                 }
18836                 var l = p.split(':').shift().replace(/\s+/g,'');
18837                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
18838                 
18839                 if ( cwhite.length && cblack.indexOf(l) > -1) {
18840 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18841                     //node.removeAttribute(n);
18842                     return true;
18843                 }
18844                 //Roo.log()
18845                 // only allow 'c whitelisted system attributes'
18846                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
18847 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18848                     //node.removeAttribute(n);
18849                     return true;
18850                 }
18851                 
18852                 
18853                  
18854                 
18855                 clean.push(p);
18856                 return true;
18857             });
18858             if (clean.length) { 
18859                 node.setAttribute(n, clean.join(';'));
18860             } else {
18861                 node.removeAttribute(n);
18862             }
18863             
18864         }
18865         
18866         
18867         for (var i = node.attributes.length-1; i > -1 ; i--) {
18868             var a = node.attributes[i];
18869             //console.log(a);
18870             
18871             if (a.name.toLowerCase().substr(0,2)=='on')  {
18872                 node.removeAttribute(a.name);
18873                 continue;
18874             }
18875             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
18876                 node.removeAttribute(a.name);
18877                 continue;
18878             }
18879             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
18880                 cleanAttr(a.name,a.value); // fixme..
18881                 continue;
18882             }
18883             if (a.name == 'style') {
18884                 cleanStyle(a.name,a.value);
18885                 continue;
18886             }
18887             /// clean up MS crap..
18888             // tecnically this should be a list of valid class'es..
18889             
18890             
18891             if (a.name == 'class') {
18892                 if (a.value.match(/^Mso/)) {
18893                     node.className = '';
18894                 }
18895                 
18896                 if (a.value.match(/body/)) {
18897                     node.className = '';
18898                 }
18899                 continue;
18900             }
18901             
18902             // style cleanup!?
18903             // class cleanup?
18904             
18905         }
18906         
18907         
18908         this.cleanUpChildren(node);
18909         
18910         
18911     },
18912     /**
18913      * Clean up MS wordisms...
18914      */
18915     cleanWord : function(node)
18916     {
18917         var _t = this;
18918         var cleanWordChildren = function()
18919         {
18920             if (!node.childNodes.length) {
18921                 return;
18922             }
18923             for (var i = node.childNodes.length-1; i > -1 ; i--) {
18924                _t.cleanWord(node.childNodes[i]);
18925             }
18926         }
18927         
18928         
18929         if (!node) {
18930             this.cleanWord(this.doc.body);
18931             return;
18932         }
18933         if (node.nodeName == "#text") {
18934             // clean up silly Windows -- stuff?
18935             return; 
18936         }
18937         if (node.nodeName == "#comment") {
18938             node.parentNode.removeChild(node);
18939             // clean up silly Windows -- stuff?
18940             return; 
18941         }
18942         
18943         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
18944             node.parentNode.removeChild(node);
18945             return;
18946         }
18947         
18948         // remove - but keep children..
18949         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
18950             while (node.childNodes.length) {
18951                 var cn = node.childNodes[0];
18952                 node.removeChild(cn);
18953                 node.parentNode.insertBefore(cn, node);
18954             }
18955             node.parentNode.removeChild(node);
18956             cleanWordChildren();
18957             return;
18958         }
18959         // clean styles
18960         if (node.className.length) {
18961             
18962             var cn = node.className.split(/\W+/);
18963             var cna = [];
18964             Roo.each(cn, function(cls) {
18965                 if (cls.match(/Mso[a-zA-Z]+/)) {
18966                     return;
18967                 }
18968                 cna.push(cls);
18969             });
18970             node.className = cna.length ? cna.join(' ') : '';
18971             if (!cna.length) {
18972                 node.removeAttribute("class");
18973             }
18974         }
18975         
18976         if (node.hasAttribute("lang")) {
18977             node.removeAttribute("lang");
18978         }
18979         
18980         if (node.hasAttribute("style")) {
18981             
18982             var styles = node.getAttribute("style").split(";");
18983             var nstyle = [];
18984             Roo.each(styles, function(s) {
18985                 if (!s.match(/:/)) {
18986                     return;
18987                 }
18988                 var kv = s.split(":");
18989                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
18990                     return;
18991                 }
18992                 // what ever is left... we allow.
18993                 nstyle.push(s);
18994             });
18995             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
18996             if (!nstyle.length) {
18997                 node.removeAttribute('style');
18998             }
18999         }
19000         
19001         cleanWordChildren();
19002         
19003         
19004     },
19005     domToHTML : function(currentElement, depth, nopadtext) {
19006         
19007         depth = depth || 0;
19008         nopadtext = nopadtext || false;
19009     
19010         if (!currentElement) {
19011             return this.domToHTML(this.doc.body);
19012         }
19013         
19014         //Roo.log(currentElement);
19015         var j;
19016         var allText = false;
19017         var nodeName = currentElement.nodeName;
19018         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
19019         
19020         if  (nodeName == '#text') {
19021             
19022             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
19023         }
19024         
19025         
19026         var ret = '';
19027         if (nodeName != 'BODY') {
19028              
19029             var i = 0;
19030             // Prints the node tagName, such as <A>, <IMG>, etc
19031             if (tagName) {
19032                 var attr = [];
19033                 for(i = 0; i < currentElement.attributes.length;i++) {
19034                     // quoting?
19035                     var aname = currentElement.attributes.item(i).name;
19036                     if (!currentElement.attributes.item(i).value.length) {
19037                         continue;
19038                     }
19039                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
19040                 }
19041                 
19042                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
19043             } 
19044             else {
19045                 
19046                 // eack
19047             }
19048         } else {
19049             tagName = false;
19050         }
19051         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
19052             return ret;
19053         }
19054         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
19055             nopadtext = true;
19056         }
19057         
19058         
19059         // Traverse the tree
19060         i = 0;
19061         var currentElementChild = currentElement.childNodes.item(i);
19062         var allText = true;
19063         var innerHTML  = '';
19064         lastnode = '';
19065         while (currentElementChild) {
19066             // Formatting code (indent the tree so it looks nice on the screen)
19067             var nopad = nopadtext;
19068             if (lastnode == 'SPAN') {
19069                 nopad  = true;
19070             }
19071             // text
19072             if  (currentElementChild.nodeName == '#text') {
19073                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
19074                 toadd = nopadtext ? toadd : toadd.trim();
19075                 if (!nopad && toadd.length > 80) {
19076                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
19077                 }
19078                 innerHTML  += toadd;
19079                 
19080                 i++;
19081                 currentElementChild = currentElement.childNodes.item(i);
19082                 lastNode = '';
19083                 continue;
19084             }
19085             allText = false;
19086             
19087             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
19088                 
19089             // Recursively traverse the tree structure of the child node
19090             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
19091             lastnode = currentElementChild.nodeName;
19092             i++;
19093             currentElementChild=currentElement.childNodes.item(i);
19094         }
19095         
19096         ret += innerHTML;
19097         
19098         if (!allText) {
19099                 // The remaining code is mostly for formatting the tree
19100             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
19101         }
19102         
19103         
19104         if (tagName) {
19105             ret+= "</"+tagName+">";
19106         }
19107         return ret;
19108         
19109     },
19110         
19111     applyBlacklists : function()
19112     {
19113         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
19114         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
19115         
19116         this.white = [];
19117         this.black = [];
19118         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
19119             if (b.indexOf(tag) > -1) {
19120                 return;
19121             }
19122             this.white.push(tag);
19123             
19124         }, this);
19125         
19126         Roo.each(w, function(tag) {
19127             if (b.indexOf(tag) > -1) {
19128                 return;
19129             }
19130             if (this.white.indexOf(tag) > -1) {
19131                 return;
19132             }
19133             this.white.push(tag);
19134             
19135         }, this);
19136         
19137         
19138         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
19139             if (w.indexOf(tag) > -1) {
19140                 return;
19141             }
19142             this.black.push(tag);
19143             
19144         }, this);
19145         
19146         Roo.each(b, function(tag) {
19147             if (w.indexOf(tag) > -1) {
19148                 return;
19149             }
19150             if (this.black.indexOf(tag) > -1) {
19151                 return;
19152             }
19153             this.black.push(tag);
19154             
19155         }, this);
19156         
19157         
19158         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
19159         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
19160         
19161         this.cwhite = [];
19162         this.cblack = [];
19163         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
19164             if (b.indexOf(tag) > -1) {
19165                 return;
19166             }
19167             this.cwhite.push(tag);
19168             
19169         }, this);
19170         
19171         Roo.each(w, function(tag) {
19172             if (b.indexOf(tag) > -1) {
19173                 return;
19174             }
19175             if (this.cwhite.indexOf(tag) > -1) {
19176                 return;
19177             }
19178             this.cwhite.push(tag);
19179             
19180         }, this);
19181         
19182         
19183         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
19184             if (w.indexOf(tag) > -1) {
19185                 return;
19186             }
19187             this.cblack.push(tag);
19188             
19189         }, this);
19190         
19191         Roo.each(b, function(tag) {
19192             if (w.indexOf(tag) > -1) {
19193                 return;
19194             }
19195             if (this.cblack.indexOf(tag) > -1) {
19196                 return;
19197             }
19198             this.cblack.push(tag);
19199             
19200         }, this);
19201     },
19202     
19203     setStylesheets : function(stylesheets)
19204     {
19205         if(typeof(stylesheets) == 'string'){
19206             Roo.get(this.iframe.contentDocument.head).createChild({
19207                 tag : 'link',
19208                 rel : 'stylesheet',
19209                 type : 'text/css',
19210                 href : stylesheets
19211             });
19212             
19213             return;
19214         }
19215         var _this = this;
19216      
19217         Roo.each(stylesheets, function(s) {
19218             if(!s.length){
19219                 return;
19220             }
19221             
19222             Roo.get(_this.iframe.contentDocument.head).createChild({
19223                 tag : 'link',
19224                 rel : 'stylesheet',
19225                 type : 'text/css',
19226                 href : s
19227             });
19228         });
19229
19230         
19231     },
19232     
19233     removeStylesheets : function()
19234     {
19235         var _this = this;
19236         
19237         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
19238             s.remove();
19239         });
19240     }
19241     
19242     // hide stuff that is not compatible
19243     /**
19244      * @event blur
19245      * @hide
19246      */
19247     /**
19248      * @event change
19249      * @hide
19250      */
19251     /**
19252      * @event focus
19253      * @hide
19254      */
19255     /**
19256      * @event specialkey
19257      * @hide
19258      */
19259     /**
19260      * @cfg {String} fieldClass @hide
19261      */
19262     /**
19263      * @cfg {String} focusClass @hide
19264      */
19265     /**
19266      * @cfg {String} autoCreate @hide
19267      */
19268     /**
19269      * @cfg {String} inputType @hide
19270      */
19271     /**
19272      * @cfg {String} invalidClass @hide
19273      */
19274     /**
19275      * @cfg {String} invalidText @hide
19276      */
19277     /**
19278      * @cfg {String} msgFx @hide
19279      */
19280     /**
19281      * @cfg {String} validateOnBlur @hide
19282      */
19283 });
19284
19285 Roo.HtmlEditorCore.white = [
19286         'area', 'br', 'img', 'input', 'hr', 'wbr',
19287         
19288        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
19289        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
19290        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
19291        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
19292        'table',   'ul',         'xmp', 
19293        
19294        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
19295       'thead',   'tr', 
19296      
19297       'dir', 'menu', 'ol', 'ul', 'dl',
19298        
19299       'embed',  'object'
19300 ];
19301
19302
19303 Roo.HtmlEditorCore.black = [
19304     //    'embed',  'object', // enable - backend responsiblity to clean thiese
19305         'applet', // 
19306         'base',   'basefont', 'bgsound', 'blink',  'body', 
19307         'frame',  'frameset', 'head',    'html',   'ilayer', 
19308         'iframe', 'layer',  'link',     'meta',    'object',   
19309         'script', 'style' ,'title',  'xml' // clean later..
19310 ];
19311 Roo.HtmlEditorCore.clean = [
19312     'script', 'style', 'title', 'xml'
19313 ];
19314 Roo.HtmlEditorCore.remove = [
19315     'font'
19316 ];
19317 // attributes..
19318
19319 Roo.HtmlEditorCore.ablack = [
19320     'on'
19321 ];
19322     
19323 Roo.HtmlEditorCore.aclean = [ 
19324     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
19325 ];
19326
19327 // protocols..
19328 Roo.HtmlEditorCore.pwhite= [
19329         'http',  'https',  'mailto'
19330 ];
19331
19332 // white listed style attributes.
19333 Roo.HtmlEditorCore.cwhite= [
19334       //  'text-align', /// default is to allow most things..
19335       
19336          
19337 //        'font-size'//??
19338 ];
19339
19340 // black listed style attributes.
19341 Roo.HtmlEditorCore.cblack= [
19342       //  'font-size' -- this can be set by the project 
19343 ];
19344
19345
19346 Roo.HtmlEditorCore.swapCodes   =[ 
19347     [    8211, "--" ], 
19348     [    8212, "--" ], 
19349     [    8216,  "'" ],  
19350     [    8217, "'" ],  
19351     [    8220, '"' ],  
19352     [    8221, '"' ],  
19353     [    8226, "*" ],  
19354     [    8230, "..." ]
19355 ]; 
19356
19357     /*
19358  * - LGPL
19359  *
19360  * HtmlEditor
19361  * 
19362  */
19363
19364 /**
19365  * @class Roo.bootstrap.HtmlEditor
19366  * @extends Roo.bootstrap.TextArea
19367  * Bootstrap HtmlEditor class
19368
19369  * @constructor
19370  * Create a new HtmlEditor
19371  * @param {Object} config The config object
19372  */
19373
19374 Roo.bootstrap.HtmlEditor = function(config){
19375     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
19376     if (!this.toolbars) {
19377         this.toolbars = [];
19378     }
19379     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
19380     this.addEvents({
19381             /**
19382              * @event initialize
19383              * Fires when the editor is fully initialized (including the iframe)
19384              * @param {HtmlEditor} this
19385              */
19386             initialize: true,
19387             /**
19388              * @event activate
19389              * Fires when the editor is first receives the focus. Any insertion must wait
19390              * until after this event.
19391              * @param {HtmlEditor} this
19392              */
19393             activate: true,
19394              /**
19395              * @event beforesync
19396              * Fires before the textarea is updated with content from the editor iframe. Return false
19397              * to cancel the sync.
19398              * @param {HtmlEditor} this
19399              * @param {String} html
19400              */
19401             beforesync: true,
19402              /**
19403              * @event beforepush
19404              * Fires before the iframe editor is updated with content from the textarea. Return false
19405              * to cancel the push.
19406              * @param {HtmlEditor} this
19407              * @param {String} html
19408              */
19409             beforepush: true,
19410              /**
19411              * @event sync
19412              * Fires when the textarea is updated with content from the editor iframe.
19413              * @param {HtmlEditor} this
19414              * @param {String} html
19415              */
19416             sync: true,
19417              /**
19418              * @event push
19419              * Fires when the iframe editor is updated with content from the textarea.
19420              * @param {HtmlEditor} this
19421              * @param {String} html
19422              */
19423             push: true,
19424              /**
19425              * @event editmodechange
19426              * Fires when the editor switches edit modes
19427              * @param {HtmlEditor} this
19428              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
19429              */
19430             editmodechange: true,
19431             /**
19432              * @event editorevent
19433              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19434              * @param {HtmlEditor} this
19435              */
19436             editorevent: true,
19437             /**
19438              * @event firstfocus
19439              * Fires when on first focus - needed by toolbars..
19440              * @param {HtmlEditor} this
19441              */
19442             firstfocus: true,
19443             /**
19444              * @event autosave
19445              * Auto save the htmlEditor value as a file into Events
19446              * @param {HtmlEditor} this
19447              */
19448             autosave: true,
19449             /**
19450              * @event savedpreview
19451              * preview the saved version of htmlEditor
19452              * @param {HtmlEditor} this
19453              */
19454             savedpreview: true
19455         });
19456 };
19457
19458
19459 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
19460     
19461     
19462       /**
19463      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
19464      */
19465     toolbars : false,
19466    
19467      /**
19468      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19469      *                        Roo.resizable.
19470      */
19471     resizable : false,
19472      /**
19473      * @cfg {Number} height (in pixels)
19474      */   
19475     height: 300,
19476    /**
19477      * @cfg {Number} width (in pixels)
19478      */   
19479     width: false,
19480     
19481     /**
19482      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19483      * 
19484      */
19485     stylesheets: false,
19486     
19487     // id of frame..
19488     frameId: false,
19489     
19490     // private properties
19491     validationEvent : false,
19492     deferHeight: true,
19493     initialized : false,
19494     activated : false,
19495     
19496     onFocus : Roo.emptyFn,
19497     iframePad:3,
19498     hideMode:'offsets',
19499     
19500     
19501     tbContainer : false,
19502     
19503     toolbarContainer :function() {
19504         return this.wrap.select('.x-html-editor-tb',true).first();
19505     },
19506
19507     /**
19508      * Protected method that will not generally be called directly. It
19509      * is called when the editor creates its toolbar. Override this method if you need to
19510      * add custom toolbar buttons.
19511      * @param {HtmlEditor} editor
19512      */
19513     createToolbar : function(){
19514         
19515         Roo.log("create toolbars");
19516         
19517         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
19518         this.toolbars[0].render(this.toolbarContainer());
19519         
19520         return;
19521         
19522 //        if (!editor.toolbars || !editor.toolbars.length) {
19523 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
19524 //        }
19525 //        
19526 //        for (var i =0 ; i < editor.toolbars.length;i++) {
19527 //            editor.toolbars[i] = Roo.factory(
19528 //                    typeof(editor.toolbars[i]) == 'string' ?
19529 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
19530 //                Roo.bootstrap.HtmlEditor);
19531 //            editor.toolbars[i].init(editor);
19532 //        }
19533     },
19534
19535      
19536     // private
19537     onRender : function(ct, position)
19538     {
19539        // Roo.log("Call onRender: " + this.xtype);
19540         var _t = this;
19541         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
19542       
19543         this.wrap = this.inputEl().wrap({
19544             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
19545         });
19546         
19547         this.editorcore.onRender(ct, position);
19548          
19549         if (this.resizable) {
19550             this.resizeEl = new Roo.Resizable(this.wrap, {
19551                 pinned : true,
19552                 wrap: true,
19553                 dynamic : true,
19554                 minHeight : this.height,
19555                 height: this.height,
19556                 handles : this.resizable,
19557                 width: this.width,
19558                 listeners : {
19559                     resize : function(r, w, h) {
19560                         _t.onResize(w,h); // -something
19561                     }
19562                 }
19563             });
19564             
19565         }
19566         this.createToolbar(this);
19567        
19568         
19569         if(!this.width && this.resizable){
19570             this.setSize(this.wrap.getSize());
19571         }
19572         if (this.resizeEl) {
19573             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
19574             // should trigger onReize..
19575         }
19576         
19577     },
19578
19579     // private
19580     onResize : function(w, h)
19581     {
19582         Roo.log('resize: ' +w + ',' + h );
19583         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
19584         var ew = false;
19585         var eh = false;
19586         
19587         if(this.inputEl() ){
19588             if(typeof w == 'number'){
19589                 var aw = w - this.wrap.getFrameWidth('lr');
19590                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
19591                 ew = aw;
19592             }
19593             if(typeof h == 'number'){
19594                  var tbh = -11;  // fixme it needs to tool bar size!
19595                 for (var i =0; i < this.toolbars.length;i++) {
19596                     // fixme - ask toolbars for heights?
19597                     tbh += this.toolbars[i].el.getHeight();
19598                     //if (this.toolbars[i].footer) {
19599                     //    tbh += this.toolbars[i].footer.el.getHeight();
19600                     //}
19601                 }
19602               
19603                 
19604                 
19605                 
19606                 
19607                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
19608                 ah -= 5; // knock a few pixes off for look..
19609                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
19610                 var eh = ah;
19611             }
19612         }
19613         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
19614         this.editorcore.onResize(ew,eh);
19615         
19616     },
19617
19618     /**
19619      * Toggles the editor between standard and source edit mode.
19620      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19621      */
19622     toggleSourceEdit : function(sourceEditMode)
19623     {
19624         this.editorcore.toggleSourceEdit(sourceEditMode);
19625         
19626         if(this.editorcore.sourceEditMode){
19627             Roo.log('editor - showing textarea');
19628             
19629 //            Roo.log('in');
19630 //            Roo.log(this.syncValue());
19631             this.syncValue();
19632             this.inputEl().removeClass(['hide', 'x-hidden']);
19633             this.inputEl().dom.removeAttribute('tabIndex');
19634             this.inputEl().focus();
19635         }else{
19636             Roo.log('editor - hiding textarea');
19637 //            Roo.log('out')
19638 //            Roo.log(this.pushValue()); 
19639             this.pushValue();
19640             
19641             this.inputEl().addClass(['hide', 'x-hidden']);
19642             this.inputEl().dom.setAttribute('tabIndex', -1);
19643             //this.deferFocus();
19644         }
19645          
19646         if(this.resizable){
19647             this.setSize(this.wrap.getSize());
19648         }
19649         
19650         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
19651     },
19652  
19653     // private (for BoxComponent)
19654     adjustSize : Roo.BoxComponent.prototype.adjustSize,
19655
19656     // private (for BoxComponent)
19657     getResizeEl : function(){
19658         return this.wrap;
19659     },
19660
19661     // private (for BoxComponent)
19662     getPositionEl : function(){
19663         return this.wrap;
19664     },
19665
19666     // private
19667     initEvents : function(){
19668         this.originalValue = this.getValue();
19669     },
19670
19671 //    /**
19672 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19673 //     * @method
19674 //     */
19675 //    markInvalid : Roo.emptyFn,
19676 //    /**
19677 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19678 //     * @method
19679 //     */
19680 //    clearInvalid : Roo.emptyFn,
19681
19682     setValue : function(v){
19683         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
19684         this.editorcore.pushValue();
19685     },
19686
19687      
19688     // private
19689     deferFocus : function(){
19690         this.focus.defer(10, this);
19691     },
19692
19693     // doc'ed in Field
19694     focus : function(){
19695         this.editorcore.focus();
19696         
19697     },
19698       
19699
19700     // private
19701     onDestroy : function(){
19702         
19703         
19704         
19705         if(this.rendered){
19706             
19707             for (var i =0; i < this.toolbars.length;i++) {
19708                 // fixme - ask toolbars for heights?
19709                 this.toolbars[i].onDestroy();
19710             }
19711             
19712             this.wrap.dom.innerHTML = '';
19713             this.wrap.remove();
19714         }
19715     },
19716
19717     // private
19718     onFirstFocus : function(){
19719         //Roo.log("onFirstFocus");
19720         this.editorcore.onFirstFocus();
19721          for (var i =0; i < this.toolbars.length;i++) {
19722             this.toolbars[i].onFirstFocus();
19723         }
19724         
19725     },
19726     
19727     // private
19728     syncValue : function()
19729     {   
19730         this.editorcore.syncValue();
19731     },
19732     
19733     pushValue : function()
19734     {   
19735         this.editorcore.pushValue();
19736     }
19737      
19738     
19739     // hide stuff that is not compatible
19740     /**
19741      * @event blur
19742      * @hide
19743      */
19744     /**
19745      * @event change
19746      * @hide
19747      */
19748     /**
19749      * @event focus
19750      * @hide
19751      */
19752     /**
19753      * @event specialkey
19754      * @hide
19755      */
19756     /**
19757      * @cfg {String} fieldClass @hide
19758      */
19759     /**
19760      * @cfg {String} focusClass @hide
19761      */
19762     /**
19763      * @cfg {String} autoCreate @hide
19764      */
19765     /**
19766      * @cfg {String} inputType @hide
19767      */
19768     /**
19769      * @cfg {String} invalidClass @hide
19770      */
19771     /**
19772      * @cfg {String} invalidText @hide
19773      */
19774     /**
19775      * @cfg {String} msgFx @hide
19776      */
19777     /**
19778      * @cfg {String} validateOnBlur @hide
19779      */
19780 });
19781  
19782     
19783    
19784    
19785    
19786       
19787 Roo.namespace('Roo.bootstrap.htmleditor');
19788 /**
19789  * @class Roo.bootstrap.HtmlEditorToolbar1
19790  * Basic Toolbar
19791  * 
19792  * Usage:
19793  *
19794  new Roo.bootstrap.HtmlEditor({
19795     ....
19796     toolbars : [
19797         new Roo.bootstrap.HtmlEditorToolbar1({
19798             disable : { fonts: 1 , format: 1, ..., ... , ...],
19799             btns : [ .... ]
19800         })
19801     }
19802      
19803  * 
19804  * @cfg {Object} disable List of elements to disable..
19805  * @cfg {Array} btns List of additional buttons.
19806  * 
19807  * 
19808  * NEEDS Extra CSS? 
19809  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
19810  */
19811  
19812 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
19813 {
19814     
19815     Roo.apply(this, config);
19816     
19817     // default disabled, based on 'good practice'..
19818     this.disable = this.disable || {};
19819     Roo.applyIf(this.disable, {
19820         fontSize : true,
19821         colors : true,
19822         specialElements : true
19823     });
19824     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
19825     
19826     this.editor = config.editor;
19827     this.editorcore = config.editor.editorcore;
19828     
19829     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
19830     
19831     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
19832     // dont call parent... till later.
19833 }
19834 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
19835      
19836     bar : true,
19837     
19838     editor : false,
19839     editorcore : false,
19840     
19841     
19842     formats : [
19843         "p" ,  
19844         "h1","h2","h3","h4","h5","h6", 
19845         "pre", "code", 
19846         "abbr", "acronym", "address", "cite", "samp", "var",
19847         'div','span'
19848     ],
19849     
19850     onRender : function(ct, position)
19851     {
19852        // Roo.log("Call onRender: " + this.xtype);
19853         
19854        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
19855        Roo.log(this.el);
19856        this.el.dom.style.marginBottom = '0';
19857        var _this = this;
19858        var editorcore = this.editorcore;
19859        var editor= this.editor;
19860        
19861        var children = [];
19862        var btn = function(id,cmd , toggle, handler){
19863        
19864             var  event = toggle ? 'toggle' : 'click';
19865        
19866             var a = {
19867                 size : 'sm',
19868                 xtype: 'Button',
19869                 xns: Roo.bootstrap,
19870                 glyphicon : id,
19871                 cmd : id || cmd,
19872                 enableToggle:toggle !== false,
19873                 //html : 'submit'
19874                 pressed : toggle ? false : null,
19875                 listeners : {}
19876             }
19877             a.listeners[toggle ? 'toggle' : 'click'] = function() {
19878                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
19879             }
19880             children.push(a);
19881             return a;
19882        }
19883         
19884         var style = {
19885                 xtype: 'Button',
19886                 size : 'sm',
19887                 xns: Roo.bootstrap,
19888                 glyphicon : 'font',
19889                 //html : 'submit'
19890                 menu : {
19891                     xtype: 'Menu',
19892                     xns: Roo.bootstrap,
19893                     items:  []
19894                 }
19895         };
19896         Roo.each(this.formats, function(f) {
19897             style.menu.items.push({
19898                 xtype :'MenuItem',
19899                 xns: Roo.bootstrap,
19900                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
19901                 tagname : f,
19902                 listeners : {
19903                     click : function()
19904                     {
19905                         editorcore.insertTag(this.tagname);
19906                         editor.focus();
19907                     }
19908                 }
19909                 
19910             });
19911         });
19912          children.push(style);   
19913             
19914             
19915         btn('bold',false,true);
19916         btn('italic',false,true);
19917         btn('align-left', 'justifyleft',true);
19918         btn('align-center', 'justifycenter',true);
19919         btn('align-right' , 'justifyright',true);
19920         btn('link', false, false, function(btn) {
19921             //Roo.log("create link?");
19922             var url = prompt(this.createLinkText, this.defaultLinkValue);
19923             if(url && url != 'http:/'+'/'){
19924                 this.editorcore.relayCmd('createlink', url);
19925             }
19926         }),
19927         btn('list','insertunorderedlist',true);
19928         btn('pencil', false,true, function(btn){
19929                 Roo.log(this);
19930                 
19931                 this.toggleSourceEdit(btn.pressed);
19932         });
19933         /*
19934         var cog = {
19935                 xtype: 'Button',
19936                 size : 'sm',
19937                 xns: Roo.bootstrap,
19938                 glyphicon : 'cog',
19939                 //html : 'submit'
19940                 menu : {
19941                     xtype: 'Menu',
19942                     xns: Roo.bootstrap,
19943                     items:  []
19944                 }
19945         };
19946         
19947         cog.menu.items.push({
19948             xtype :'MenuItem',
19949             xns: Roo.bootstrap,
19950             html : Clean styles,
19951             tagname : f,
19952             listeners : {
19953                 click : function()
19954                 {
19955                     editorcore.insertTag(this.tagname);
19956                     editor.focus();
19957                 }
19958             }
19959             
19960         });
19961        */
19962         
19963          
19964        this.xtype = 'NavSimplebar';
19965         
19966         for(var i=0;i< children.length;i++) {
19967             
19968             this.buttons.add(this.addxtypeChild(children[i]));
19969             
19970         }
19971         
19972         editor.on('editorevent', this.updateToolbar, this);
19973     },
19974     onBtnClick : function(id)
19975     {
19976        this.editorcore.relayCmd(id);
19977        this.editorcore.focus();
19978     },
19979     
19980     /**
19981      * Protected method that will not generally be called directly. It triggers
19982      * a toolbar update by reading the markup state of the current selection in the editor.
19983      */
19984     updateToolbar: function(){
19985
19986         if(!this.editorcore.activated){
19987             this.editor.onFirstFocus(); // is this neeed?
19988             return;
19989         }
19990
19991         var btns = this.buttons; 
19992         var doc = this.editorcore.doc;
19993         btns.get('bold').setActive(doc.queryCommandState('bold'));
19994         btns.get('italic').setActive(doc.queryCommandState('italic'));
19995         //btns.get('underline').setActive(doc.queryCommandState('underline'));
19996         
19997         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
19998         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
19999         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
20000         
20001         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
20002         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
20003          /*
20004         
20005         var ans = this.editorcore.getAllAncestors();
20006         if (this.formatCombo) {
20007             
20008             
20009             var store = this.formatCombo.store;
20010             this.formatCombo.setValue("");
20011             for (var i =0; i < ans.length;i++) {
20012                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
20013                     // select it..
20014                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
20015                     break;
20016                 }
20017             }
20018         }
20019         
20020         
20021         
20022         // hides menus... - so this cant be on a menu...
20023         Roo.bootstrap.MenuMgr.hideAll();
20024         */
20025         Roo.bootstrap.MenuMgr.hideAll();
20026         //this.editorsyncValue();
20027     },
20028     onFirstFocus: function() {
20029         this.buttons.each(function(item){
20030            item.enable();
20031         });
20032     },
20033     toggleSourceEdit : function(sourceEditMode){
20034         
20035           
20036         if(sourceEditMode){
20037             Roo.log("disabling buttons");
20038            this.buttons.each( function(item){
20039                 if(item.cmd != 'pencil'){
20040                     item.disable();
20041                 }
20042             });
20043           
20044         }else{
20045             Roo.log("enabling buttons");
20046             if(this.editorcore.initialized){
20047                 this.buttons.each( function(item){
20048                     item.enable();
20049                 });
20050             }
20051             
20052         }
20053         Roo.log("calling toggole on editor");
20054         // tell the editor that it's been pressed..
20055         this.editor.toggleSourceEdit(sourceEditMode);
20056        
20057     }
20058 });
20059
20060
20061
20062
20063
20064 /**
20065  * @class Roo.bootstrap.Table.AbstractSelectionModel
20066  * @extends Roo.util.Observable
20067  * Abstract base class for grid SelectionModels.  It provides the interface that should be
20068  * implemented by descendant classes.  This class should not be directly instantiated.
20069  * @constructor
20070  */
20071 Roo.bootstrap.Table.AbstractSelectionModel = function(){
20072     this.locked = false;
20073     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
20074 };
20075
20076
20077 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
20078     /** @ignore Called by the grid automatically. Do not call directly. */
20079     init : function(grid){
20080         this.grid = grid;
20081         this.initEvents();
20082     },
20083
20084     /**
20085      * Locks the selections.
20086      */
20087     lock : function(){
20088         this.locked = true;
20089     },
20090
20091     /**
20092      * Unlocks the selections.
20093      */
20094     unlock : function(){
20095         this.locked = false;
20096     },
20097
20098     /**
20099      * Returns true if the selections are locked.
20100      * @return {Boolean}
20101      */
20102     isLocked : function(){
20103         return this.locked;
20104     }
20105 });
20106 /**
20107  * @extends Roo.bootstrap.Table.AbstractSelectionModel
20108  * @class Roo.bootstrap.Table.RowSelectionModel
20109  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
20110  * It supports multiple selections and keyboard selection/navigation. 
20111  * @constructor
20112  * @param {Object} config
20113  */
20114
20115 Roo.bootstrap.Table.RowSelectionModel = function(config){
20116     Roo.apply(this, config);
20117     this.selections = new Roo.util.MixedCollection(false, function(o){
20118         return o.id;
20119     });
20120
20121     this.last = false;
20122     this.lastActive = false;
20123
20124     this.addEvents({
20125         /**
20126              * @event selectionchange
20127              * Fires when the selection changes
20128              * @param {SelectionModel} this
20129              */
20130             "selectionchange" : true,
20131         /**
20132              * @event afterselectionchange
20133              * Fires after the selection changes (eg. by key press or clicking)
20134              * @param {SelectionModel} this
20135              */
20136             "afterselectionchange" : true,
20137         /**
20138              * @event beforerowselect
20139              * Fires when a row is selected being selected, return false to cancel.
20140              * @param {SelectionModel} this
20141              * @param {Number} rowIndex The selected index
20142              * @param {Boolean} keepExisting False if other selections will be cleared
20143              */
20144             "beforerowselect" : true,
20145         /**
20146              * @event rowselect
20147              * Fires when a row is selected.
20148              * @param {SelectionModel} this
20149              * @param {Number} rowIndex The selected index
20150              * @param {Roo.data.Record} r The record
20151              */
20152             "rowselect" : true,
20153         /**
20154              * @event rowdeselect
20155              * Fires when a row is deselected.
20156              * @param {SelectionModel} this
20157              * @param {Number} rowIndex The selected index
20158              */
20159         "rowdeselect" : true
20160     });
20161     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
20162     this.locked = false;
20163 };
20164
20165 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
20166     /**
20167      * @cfg {Boolean} singleSelect
20168      * True to allow selection of only one row at a time (defaults to false)
20169      */
20170     singleSelect : false,
20171
20172     // private
20173     initEvents : function(){
20174
20175         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
20176             this.grid.on("mousedown", this.handleMouseDown, this);
20177         }else{ // allow click to work like normal
20178             this.grid.on("rowclick", this.handleDragableRowClick, this);
20179         }
20180
20181         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
20182             "up" : function(e){
20183                 if(!e.shiftKey){
20184                     this.selectPrevious(e.shiftKey);
20185                 }else if(this.last !== false && this.lastActive !== false){
20186                     var last = this.last;
20187                     this.selectRange(this.last,  this.lastActive-1);
20188                     this.grid.getView().focusRow(this.lastActive);
20189                     if(last !== false){
20190                         this.last = last;
20191                     }
20192                 }else{
20193                     this.selectFirstRow();
20194                 }
20195                 this.fireEvent("afterselectionchange", this);
20196             },
20197             "down" : function(e){
20198                 if(!e.shiftKey){
20199                     this.selectNext(e.shiftKey);
20200                 }else if(this.last !== false && this.lastActive !== false){
20201                     var last = this.last;
20202                     this.selectRange(this.last,  this.lastActive+1);
20203                     this.grid.getView().focusRow(this.lastActive);
20204                     if(last !== false){
20205                         this.last = last;
20206                     }
20207                 }else{
20208                     this.selectFirstRow();
20209                 }
20210                 this.fireEvent("afterselectionchange", this);
20211             },
20212             scope: this
20213         });
20214
20215         var view = this.grid.view;
20216         view.on("refresh", this.onRefresh, this);
20217         view.on("rowupdated", this.onRowUpdated, this);
20218         view.on("rowremoved", this.onRemove, this);
20219     },
20220
20221     // private
20222     onRefresh : function(){
20223         var ds = this.grid.dataSource, i, v = this.grid.view;
20224         var s = this.selections;
20225         s.each(function(r){
20226             if((i = ds.indexOfId(r.id)) != -1){
20227                 v.onRowSelect(i);
20228             }else{
20229                 s.remove(r);
20230             }
20231         });
20232     },
20233
20234     // private
20235     onRemove : function(v, index, r){
20236         this.selections.remove(r);
20237     },
20238
20239     // private
20240     onRowUpdated : function(v, index, r){
20241         if(this.isSelected(r)){
20242             v.onRowSelect(index);
20243         }
20244     },
20245
20246     /**
20247      * Select records.
20248      * @param {Array} records The records to select
20249      * @param {Boolean} keepExisting (optional) True to keep existing selections
20250      */
20251     selectRecords : function(records, keepExisting){
20252         if(!keepExisting){
20253             this.clearSelections();
20254         }
20255         var ds = this.grid.dataSource;
20256         for(var i = 0, len = records.length; i < len; i++){
20257             this.selectRow(ds.indexOf(records[i]), true);
20258         }
20259     },
20260
20261     /**
20262      * Gets the number of selected rows.
20263      * @return {Number}
20264      */
20265     getCount : function(){
20266         return this.selections.length;
20267     },
20268
20269     /**
20270      * Selects the first row in the grid.
20271      */
20272     selectFirstRow : function(){
20273         this.selectRow(0);
20274     },
20275
20276     /**
20277      * Select the last row.
20278      * @param {Boolean} keepExisting (optional) True to keep existing selections
20279      */
20280     selectLastRow : function(keepExisting){
20281         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
20282     },
20283
20284     /**
20285      * Selects the row immediately following the last selected row.
20286      * @param {Boolean} keepExisting (optional) True to keep existing selections
20287      */
20288     selectNext : function(keepExisting){
20289         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
20290             this.selectRow(this.last+1, keepExisting);
20291             this.grid.getView().focusRow(this.last);
20292         }
20293     },
20294
20295     /**
20296      * Selects the row that precedes the last selected row.
20297      * @param {Boolean} keepExisting (optional) True to keep existing selections
20298      */
20299     selectPrevious : function(keepExisting){
20300         if(this.last){
20301             this.selectRow(this.last-1, keepExisting);
20302             this.grid.getView().focusRow(this.last);
20303         }
20304     },
20305
20306     /**
20307      * Returns the selected records
20308      * @return {Array} Array of selected records
20309      */
20310     getSelections : function(){
20311         return [].concat(this.selections.items);
20312     },
20313
20314     /**
20315      * Returns the first selected record.
20316      * @return {Record}
20317      */
20318     getSelected : function(){
20319         return this.selections.itemAt(0);
20320     },
20321
20322
20323     /**
20324      * Clears all selections.
20325      */
20326     clearSelections : function(fast){
20327         if(this.locked) return;
20328         if(fast !== true){
20329             var ds = this.grid.dataSource;
20330             var s = this.selections;
20331             s.each(function(r){
20332                 this.deselectRow(ds.indexOfId(r.id));
20333             }, this);
20334             s.clear();
20335         }else{
20336             this.selections.clear();
20337         }
20338         this.last = false;
20339     },
20340
20341
20342     /**
20343      * Selects all rows.
20344      */
20345     selectAll : function(){
20346         if(this.locked) return;
20347         this.selections.clear();
20348         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
20349             this.selectRow(i, true);
20350         }
20351     },
20352
20353     /**
20354      * Returns True if there is a selection.
20355      * @return {Boolean}
20356      */
20357     hasSelection : function(){
20358         return this.selections.length > 0;
20359     },
20360
20361     /**
20362      * Returns True if the specified row is selected.
20363      * @param {Number/Record} record The record or index of the record to check
20364      * @return {Boolean}
20365      */
20366     isSelected : function(index){
20367         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
20368         return (r && this.selections.key(r.id) ? true : false);
20369     },
20370
20371     /**
20372      * Returns True if the specified record id is selected.
20373      * @param {String} id The id of record to check
20374      * @return {Boolean}
20375      */
20376     isIdSelected : function(id){
20377         return (this.selections.key(id) ? true : false);
20378     },
20379
20380     // private
20381     handleMouseDown : function(e, t){
20382         var view = this.grid.getView(), rowIndex;
20383         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
20384             return;
20385         };
20386         if(e.shiftKey && this.last !== false){
20387             var last = this.last;
20388             this.selectRange(last, rowIndex, e.ctrlKey);
20389             this.last = last; // reset the last
20390             view.focusRow(rowIndex);
20391         }else{
20392             var isSelected = this.isSelected(rowIndex);
20393             if(e.button !== 0 && isSelected){
20394                 view.focusRow(rowIndex);
20395             }else if(e.ctrlKey && isSelected){
20396                 this.deselectRow(rowIndex);
20397             }else if(!isSelected){
20398                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
20399                 view.focusRow(rowIndex);
20400             }
20401         }
20402         this.fireEvent("afterselectionchange", this);
20403     },
20404     // private
20405     handleDragableRowClick :  function(grid, rowIndex, e) 
20406     {
20407         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
20408             this.selectRow(rowIndex, false);
20409             grid.view.focusRow(rowIndex);
20410              this.fireEvent("afterselectionchange", this);
20411         }
20412     },
20413     
20414     /**
20415      * Selects multiple rows.
20416      * @param {Array} rows Array of the indexes of the row to select
20417      * @param {Boolean} keepExisting (optional) True to keep existing selections
20418      */
20419     selectRows : function(rows, keepExisting){
20420         if(!keepExisting){
20421             this.clearSelections();
20422         }
20423         for(var i = 0, len = rows.length; i < len; i++){
20424             this.selectRow(rows[i], true);
20425         }
20426     },
20427
20428     /**
20429      * Selects a range of rows. All rows in between startRow and endRow are also selected.
20430      * @param {Number} startRow The index of the first row in the range
20431      * @param {Number} endRow The index of the last row in the range
20432      * @param {Boolean} keepExisting (optional) True to retain existing selections
20433      */
20434     selectRange : function(startRow, endRow, keepExisting){
20435         if(this.locked) return;
20436         if(!keepExisting){
20437             this.clearSelections();
20438         }
20439         if(startRow <= endRow){
20440             for(var i = startRow; i <= endRow; i++){
20441                 this.selectRow(i, true);
20442             }
20443         }else{
20444             for(var i = startRow; i >= endRow; i--){
20445                 this.selectRow(i, true);
20446             }
20447         }
20448     },
20449
20450     /**
20451      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
20452      * @param {Number} startRow The index of the first row in the range
20453      * @param {Number} endRow The index of the last row in the range
20454      */
20455     deselectRange : function(startRow, endRow, preventViewNotify){
20456         if(this.locked) return;
20457         for(var i = startRow; i <= endRow; i++){
20458             this.deselectRow(i, preventViewNotify);
20459         }
20460     },
20461
20462     /**
20463      * Selects a row.
20464      * @param {Number} row The index of the row to select
20465      * @param {Boolean} keepExisting (optional) True to keep existing selections
20466      */
20467     selectRow : function(index, keepExisting, preventViewNotify){
20468         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
20469         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
20470             if(!keepExisting || this.singleSelect){
20471                 this.clearSelections();
20472             }
20473             var r = this.grid.dataSource.getAt(index);
20474             this.selections.add(r);
20475             this.last = this.lastActive = index;
20476             if(!preventViewNotify){
20477                 this.grid.getView().onRowSelect(index);
20478             }
20479             this.fireEvent("rowselect", this, index, r);
20480             this.fireEvent("selectionchange", this);
20481         }
20482     },
20483
20484     /**
20485      * Deselects a row.
20486      * @param {Number} row The index of the row to deselect
20487      */
20488     deselectRow : function(index, preventViewNotify){
20489         if(this.locked) return;
20490         if(this.last == index){
20491             this.last = false;
20492         }
20493         if(this.lastActive == index){
20494             this.lastActive = false;
20495         }
20496         var r = this.grid.dataSource.getAt(index);
20497         this.selections.remove(r);
20498         if(!preventViewNotify){
20499             this.grid.getView().onRowDeselect(index);
20500         }
20501         this.fireEvent("rowdeselect", this, index);
20502         this.fireEvent("selectionchange", this);
20503     },
20504
20505     // private
20506     restoreLast : function(){
20507         if(this._last){
20508             this.last = this._last;
20509         }
20510     },
20511
20512     // private
20513     acceptsNav : function(row, col, cm){
20514         return !cm.isHidden(col) && cm.isCellEditable(col, row);
20515     },
20516
20517     // private
20518     onEditorKey : function(field, e){
20519         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
20520         if(k == e.TAB){
20521             e.stopEvent();
20522             ed.completeEdit();
20523             if(e.shiftKey){
20524                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
20525             }else{
20526                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
20527             }
20528         }else if(k == e.ENTER && !e.ctrlKey){
20529             e.stopEvent();
20530             ed.completeEdit();
20531             if(e.shiftKey){
20532                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
20533             }else{
20534                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
20535             }
20536         }else if(k == e.ESC){
20537             ed.cancelEdit();
20538         }
20539         if(newCell){
20540             g.startEditing(newCell[0], newCell[1]);
20541         }
20542     }
20543 });/*
20544  * Based on:
20545  * Ext JS Library 1.1.1
20546  * Copyright(c) 2006-2007, Ext JS, LLC.
20547  *
20548  * Originally Released Under LGPL - original licence link has changed is not relivant.
20549  *
20550  * Fork - LGPL
20551  * <script type="text/javascript">
20552  */
20553  
20554 /**
20555  * @class Roo.bootstrap.PagingToolbar
20556  * @extends Roo.Row
20557  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
20558  * @constructor
20559  * Create a new PagingToolbar
20560  * @param {Object} config The config object
20561  */
20562 Roo.bootstrap.PagingToolbar = function(config)
20563 {
20564     // old args format still supported... - xtype is prefered..
20565         // created from xtype...
20566     var ds = config.dataSource;
20567     this.toolbarItems = [];
20568     if (config.items) {
20569         this.toolbarItems = config.items;
20570 //        config.items = [];
20571     }
20572     
20573     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
20574     this.ds = ds;
20575     this.cursor = 0;
20576     if (ds) { 
20577         this.bind(ds);
20578     }
20579     
20580     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
20581     
20582 };
20583
20584 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
20585     /**
20586      * @cfg {Roo.data.Store} dataSource
20587      * The underlying data store providing the paged data
20588      */
20589     /**
20590      * @cfg {String/HTMLElement/Element} container
20591      * container The id or element that will contain the toolbar
20592      */
20593     /**
20594      * @cfg {Boolean} displayInfo
20595      * True to display the displayMsg (defaults to false)
20596      */
20597     /**
20598      * @cfg {Number} pageSize
20599      * The number of records to display per page (defaults to 20)
20600      */
20601     pageSize: 20,
20602     /**
20603      * @cfg {String} displayMsg
20604      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
20605      */
20606     displayMsg : 'Displaying {0} - {1} of {2}',
20607     /**
20608      * @cfg {String} emptyMsg
20609      * The message to display when no records are found (defaults to "No data to display")
20610      */
20611     emptyMsg : 'No data to display',
20612     /**
20613      * Customizable piece of the default paging text (defaults to "Page")
20614      * @type String
20615      */
20616     beforePageText : "Page",
20617     /**
20618      * Customizable piece of the default paging text (defaults to "of %0")
20619      * @type String
20620      */
20621     afterPageText : "of {0}",
20622     /**
20623      * Customizable piece of the default paging text (defaults to "First Page")
20624      * @type String
20625      */
20626     firstText : "First Page",
20627     /**
20628      * Customizable piece of the default paging text (defaults to "Previous Page")
20629      * @type String
20630      */
20631     prevText : "Previous Page",
20632     /**
20633      * Customizable piece of the default paging text (defaults to "Next Page")
20634      * @type String
20635      */
20636     nextText : "Next Page",
20637     /**
20638      * Customizable piece of the default paging text (defaults to "Last Page")
20639      * @type String
20640      */
20641     lastText : "Last Page",
20642     /**
20643      * Customizable piece of the default paging text (defaults to "Refresh")
20644      * @type String
20645      */
20646     refreshText : "Refresh",
20647
20648     buttons : false,
20649     // private
20650     onRender : function(ct, position) 
20651     {
20652         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
20653         this.navgroup.parentId = this.id;
20654         this.navgroup.onRender(this.el, null);
20655         // add the buttons to the navgroup
20656         
20657         if(this.displayInfo){
20658             Roo.log(this.el.select('ul.navbar-nav',true).first());
20659             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
20660             this.displayEl = this.el.select('.x-paging-info', true).first();
20661 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
20662 //            this.displayEl = navel.el.select('span',true).first();
20663         }
20664         
20665         var _this = this;
20666         
20667         if(this.buttons){
20668             Roo.each(_this.buttons, function(e){
20669                Roo.factory(e).onRender(_this.el, null);
20670             });
20671         }
20672             
20673         Roo.each(_this.toolbarItems, function(e) {
20674             _this.navgroup.addItem(e);
20675         });
20676         
20677         
20678         this.first = this.navgroup.addItem({
20679             tooltip: this.firstText,
20680             cls: "prev",
20681             icon : 'fa fa-backward',
20682             disabled: true,
20683             preventDefault: true,
20684             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
20685         });
20686         
20687         this.prev =  this.navgroup.addItem({
20688             tooltip: this.prevText,
20689             cls: "prev",
20690             icon : 'fa fa-step-backward',
20691             disabled: true,
20692             preventDefault: true,
20693             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
20694         });
20695     //this.addSeparator();
20696         
20697         
20698         var field = this.navgroup.addItem( {
20699             tagtype : 'span',
20700             cls : 'x-paging-position',
20701             
20702             html : this.beforePageText  +
20703                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
20704                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
20705          } ); //?? escaped?
20706         
20707         this.field = field.el.select('input', true).first();
20708         this.field.on("keydown", this.onPagingKeydown, this);
20709         this.field.on("focus", function(){this.dom.select();});
20710     
20711     
20712         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
20713         //this.field.setHeight(18);
20714         //this.addSeparator();
20715         this.next = this.navgroup.addItem({
20716             tooltip: this.nextText,
20717             cls: "next",
20718             html : ' <i class="fa fa-step-forward">',
20719             disabled: true,
20720             preventDefault: true,
20721             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
20722         });
20723         this.last = this.navgroup.addItem({
20724             tooltip: this.lastText,
20725             icon : 'fa fa-forward',
20726             cls: "next",
20727             disabled: true,
20728             preventDefault: true,
20729             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
20730         });
20731     //this.addSeparator();
20732         this.loading = this.navgroup.addItem({
20733             tooltip: this.refreshText,
20734             icon: 'fa fa-refresh',
20735             preventDefault: true,
20736             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
20737         });
20738
20739     },
20740
20741     // private
20742     updateInfo : function(){
20743         if(this.displayEl){
20744             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
20745             var msg = count == 0 ?
20746                 this.emptyMsg :
20747                 String.format(
20748                     this.displayMsg,
20749                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
20750                 );
20751             this.displayEl.update(msg);
20752         }
20753     },
20754
20755     // private
20756     onLoad : function(ds, r, o){
20757        this.cursor = o.params ? o.params.start : 0;
20758        var d = this.getPageData(),
20759             ap = d.activePage,
20760             ps = d.pages;
20761         
20762        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
20763        this.field.dom.value = ap;
20764        this.first.setDisabled(ap == 1);
20765        this.prev.setDisabled(ap == 1);
20766        this.next.setDisabled(ap == ps);
20767        this.last.setDisabled(ap == ps);
20768        this.loading.enable();
20769        this.updateInfo();
20770     },
20771
20772     // private
20773     getPageData : function(){
20774         var total = this.ds.getTotalCount();
20775         return {
20776             total : total,
20777             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
20778             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
20779         };
20780     },
20781
20782     // private
20783     onLoadError : function(){
20784         this.loading.enable();
20785     },
20786
20787     // private
20788     onPagingKeydown : function(e){
20789         var k = e.getKey();
20790         var d = this.getPageData();
20791         if(k == e.RETURN){
20792             var v = this.field.dom.value, pageNum;
20793             if(!v || isNaN(pageNum = parseInt(v, 10))){
20794                 this.field.dom.value = d.activePage;
20795                 return;
20796             }
20797             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
20798             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20799             e.stopEvent();
20800         }
20801         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))
20802         {
20803           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
20804           this.field.dom.value = pageNum;
20805           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
20806           e.stopEvent();
20807         }
20808         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20809         {
20810           var v = this.field.dom.value, pageNum; 
20811           var increment = (e.shiftKey) ? 10 : 1;
20812           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20813             increment *= -1;
20814           if(!v || isNaN(pageNum = parseInt(v, 10))) {
20815             this.field.dom.value = d.activePage;
20816             return;
20817           }
20818           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
20819           {
20820             this.field.dom.value = parseInt(v, 10) + increment;
20821             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
20822             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20823           }
20824           e.stopEvent();
20825         }
20826     },
20827
20828     // private
20829     beforeLoad : function(){
20830         if(this.loading){
20831             this.loading.disable();
20832         }
20833     },
20834
20835     // private
20836     onClick : function(which){
20837         
20838         var ds = this.ds;
20839         if (!ds) {
20840             return;
20841         }
20842         
20843         switch(which){
20844             case "first":
20845                 ds.load({params:{start: 0, limit: this.pageSize}});
20846             break;
20847             case "prev":
20848                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
20849             break;
20850             case "next":
20851                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
20852             break;
20853             case "last":
20854                 var total = ds.getTotalCount();
20855                 var extra = total % this.pageSize;
20856                 var lastStart = extra ? (total - extra) : total-this.pageSize;
20857                 ds.load({params:{start: lastStart, limit: this.pageSize}});
20858             break;
20859             case "refresh":
20860                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
20861             break;
20862         }
20863     },
20864
20865     /**
20866      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
20867      * @param {Roo.data.Store} store The data store to unbind
20868      */
20869     unbind : function(ds){
20870         ds.un("beforeload", this.beforeLoad, this);
20871         ds.un("load", this.onLoad, this);
20872         ds.un("loadexception", this.onLoadError, this);
20873         ds.un("remove", this.updateInfo, this);
20874         ds.un("add", this.updateInfo, this);
20875         this.ds = undefined;
20876     },
20877
20878     /**
20879      * Binds the paging toolbar to the specified {@link Roo.data.Store}
20880      * @param {Roo.data.Store} store The data store to bind
20881      */
20882     bind : function(ds){
20883         ds.on("beforeload", this.beforeLoad, this);
20884         ds.on("load", this.onLoad, this);
20885         ds.on("loadexception", this.onLoadError, this);
20886         ds.on("remove", this.updateInfo, this);
20887         ds.on("add", this.updateInfo, this);
20888         this.ds = ds;
20889     }
20890 });/*
20891  * - LGPL
20892  *
20893  * element
20894  * 
20895  */
20896
20897 /**
20898  * @class Roo.bootstrap.MessageBar
20899  * @extends Roo.bootstrap.Component
20900  * Bootstrap MessageBar class
20901  * @cfg {String} html contents of the MessageBar
20902  * @cfg {String} weight (info | success | warning | danger) default info
20903  * @cfg {String} beforeClass insert the bar before the given class
20904  * @cfg {Boolean} closable (true | false) default false
20905  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
20906  * 
20907  * @constructor
20908  * Create a new Element
20909  * @param {Object} config The config object
20910  */
20911
20912 Roo.bootstrap.MessageBar = function(config){
20913     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
20914 };
20915
20916 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
20917     
20918     html: '',
20919     weight: 'info',
20920     closable: false,
20921     fixed: false,
20922     beforeClass: 'bootstrap-sticky-wrap',
20923     
20924     getAutoCreate : function(){
20925         
20926         var cfg = {
20927             tag: 'div',
20928             cls: 'alert alert-dismissable alert-' + this.weight,
20929             cn: [
20930                 {
20931                     tag: 'span',
20932                     cls: 'message',
20933                     html: this.html || ''
20934                 }
20935             ]
20936         }
20937         
20938         if(this.fixed){
20939             cfg.cls += ' alert-messages-fixed';
20940         }
20941         
20942         if(this.closable){
20943             cfg.cn.push({
20944                 tag: 'button',
20945                 cls: 'close',
20946                 html: 'x'
20947             });
20948         }
20949         
20950         return cfg;
20951     },
20952     
20953     onRender : function(ct, position)
20954     {
20955         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20956         
20957         if(!this.el){
20958             var cfg = Roo.apply({},  this.getAutoCreate());
20959             cfg.id = Roo.id();
20960             
20961             if (this.cls) {
20962                 cfg.cls += ' ' + this.cls;
20963             }
20964             if (this.style) {
20965                 cfg.style = this.style;
20966             }
20967             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
20968             
20969             this.el.setVisibilityMode(Roo.Element.DISPLAY);
20970         }
20971         
20972         this.el.select('>button.close').on('click', this.hide, this);
20973         
20974     },
20975     
20976     show : function()
20977     {
20978         if (!this.rendered) {
20979             this.render();
20980         }
20981         
20982         this.el.show();
20983         
20984         this.fireEvent('show', this);
20985         
20986     },
20987     
20988     hide : function()
20989     {
20990         if (!this.rendered) {
20991             this.render();
20992         }
20993         
20994         this.el.hide();
20995         
20996         this.fireEvent('hide', this);
20997     },
20998     
20999     update : function()
21000     {
21001 //        var e = this.el.dom.firstChild;
21002 //        
21003 //        if(this.closable){
21004 //            e = e.nextSibling;
21005 //        }
21006 //        
21007 //        e.data = this.html || '';
21008
21009         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
21010     }
21011    
21012 });
21013
21014  
21015
21016      /*
21017  * - LGPL
21018  *
21019  * Graph
21020  * 
21021  */
21022
21023
21024 /**
21025  * @class Roo.bootstrap.Graph
21026  * @extends Roo.bootstrap.Component
21027  * Bootstrap Graph class
21028 > Prameters
21029  -sm {number} sm 4
21030  -md {number} md 5
21031  @cfg {String} graphtype  bar | vbar | pie
21032  @cfg {number} g_x coodinator | centre x (pie)
21033  @cfg {number} g_y coodinator | centre y (pie)
21034  @cfg {number} g_r radius (pie)
21035  @cfg {number} g_height height of the chart (respected by all elements in the set)
21036  @cfg {number} g_width width of the chart (respected by all elements in the set)
21037  @cfg {Object} title The title of the chart
21038     
21039  -{Array}  values
21040  -opts (object) options for the chart 
21041      o {
21042      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
21043      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
21044      o vgutter (number)
21045      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.
21046      o stacked (boolean) whether or not to tread values as in a stacked bar chart
21047      o to
21048      o stretch (boolean)
21049      o }
21050  -opts (object) options for the pie
21051      o{
21052      o cut
21053      o startAngle (number)
21054      o endAngle (number)
21055      } 
21056  *
21057  * @constructor
21058  * Create a new Input
21059  * @param {Object} config The config object
21060  */
21061
21062 Roo.bootstrap.Graph = function(config){
21063     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
21064     
21065     this.addEvents({
21066         // img events
21067         /**
21068          * @event click
21069          * The img click event for the img.
21070          * @param {Roo.EventObject} e
21071          */
21072         "click" : true
21073     });
21074 };
21075
21076 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
21077     
21078     sm: 4,
21079     md: 5,
21080     graphtype: 'bar',
21081     g_height: 250,
21082     g_width: 400,
21083     g_x: 50,
21084     g_y: 50,
21085     g_r: 30,
21086     opts:{
21087         //g_colors: this.colors,
21088         g_type: 'soft',
21089         g_gutter: '20%'
21090
21091     },
21092     title : false,
21093
21094     getAutoCreate : function(){
21095         
21096         var cfg = {
21097             tag: 'div',
21098             html : null
21099         }
21100         
21101         
21102         return  cfg;
21103     },
21104
21105     onRender : function(ct,position){
21106         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
21107         this.raphael = Raphael(this.el.dom);
21108         
21109                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21110                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21111                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21112                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
21113                 /*
21114                 r.text(160, 10, "Single Series Chart").attr(txtattr);
21115                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
21116                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
21117                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
21118                 
21119                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
21120                 r.barchart(330, 10, 300, 220, data1);
21121                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
21122                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
21123                 */
21124                 
21125                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21126                 // r.barchart(30, 30, 560, 250,  xdata, {
21127                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
21128                 //     axis : "0 0 1 1",
21129                 //     axisxlabels :  xdata
21130                 //     //yvalues : cols,
21131                    
21132                 // });
21133 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21134 //        
21135 //        this.load(null,xdata,{
21136 //                axis : "0 0 1 1",
21137 //                axisxlabels :  xdata
21138 //                });
21139
21140     },
21141
21142     load : function(graphtype,xdata,opts){
21143         this.raphael.clear();
21144         if(!graphtype) {
21145             graphtype = this.graphtype;
21146         }
21147         if(!opts){
21148             opts = this.opts;
21149         }
21150         var r = this.raphael,
21151             fin = function () {
21152                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
21153             },
21154             fout = function () {
21155                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
21156             },
21157             pfin = function() {
21158                 this.sector.stop();
21159                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
21160
21161                 if (this.label) {
21162                     this.label[0].stop();
21163                     this.label[0].attr({ r: 7.5 });
21164                     this.label[1].attr({ "font-weight": 800 });
21165                 }
21166             },
21167             pfout = function() {
21168                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
21169
21170                 if (this.label) {
21171                     this.label[0].animate({ r: 5 }, 500, "bounce");
21172                     this.label[1].attr({ "font-weight": 400 });
21173                 }
21174             };
21175
21176         switch(graphtype){
21177             case 'bar':
21178                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21179                 break;
21180             case 'hbar':
21181                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21182                 break;
21183             case 'pie':
21184 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
21185 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
21186 //            
21187                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
21188                 
21189                 break;
21190
21191         }
21192         
21193         if(this.title){
21194             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
21195         }
21196         
21197     },
21198     
21199     setTitle: function(o)
21200     {
21201         this.title = o;
21202     },
21203     
21204     initEvents: function() {
21205         
21206         if(!this.href){
21207             this.el.on('click', this.onClick, this);
21208         }
21209     },
21210     
21211     onClick : function(e)
21212     {
21213         Roo.log('img onclick');
21214         this.fireEvent('click', this, e);
21215     }
21216    
21217 });
21218
21219  
21220 /*
21221  * - LGPL
21222  *
21223  * numberBox
21224  * 
21225  */
21226 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21227
21228 /**
21229  * @class Roo.bootstrap.dash.NumberBox
21230  * @extends Roo.bootstrap.Component
21231  * Bootstrap NumberBox class
21232  * @cfg {String} headline Box headline
21233  * @cfg {String} content Box content
21234  * @cfg {String} icon Box icon
21235  * @cfg {String} footer Footer text
21236  * @cfg {String} fhref Footer href
21237  * 
21238  * @constructor
21239  * Create a new NumberBox
21240  * @param {Object} config The config object
21241  */
21242
21243
21244 Roo.bootstrap.dash.NumberBox = function(config){
21245     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
21246     
21247 };
21248
21249 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
21250     
21251     headline : '',
21252     content : '',
21253     icon : '',
21254     footer : '',
21255     fhref : '',
21256     ficon : '',
21257     
21258     getAutoCreate : function(){
21259         
21260         var cfg = {
21261             tag : 'div',
21262             cls : 'small-box ',
21263             cn : [
21264                 {
21265                     tag : 'div',
21266                     cls : 'inner',
21267                     cn :[
21268                         {
21269                             tag : 'h3',
21270                             cls : 'roo-headline',
21271                             html : this.headline
21272                         },
21273                         {
21274                             tag : 'p',
21275                             cls : 'roo-content',
21276                             html : this.content
21277                         }
21278                     ]
21279                 }
21280             ]
21281         }
21282         
21283         if(this.icon){
21284             cfg.cn.push({
21285                 tag : 'div',
21286                 cls : 'icon',
21287                 cn :[
21288                     {
21289                         tag : 'i',
21290                         cls : 'ion ' + this.icon
21291                     }
21292                 ]
21293             });
21294         }
21295         
21296         if(this.footer){
21297             var footer = {
21298                 tag : 'a',
21299                 cls : 'small-box-footer',
21300                 href : this.fhref || '#',
21301                 html : this.footer
21302             };
21303             
21304             cfg.cn.push(footer);
21305             
21306         }
21307         
21308         return  cfg;
21309     },
21310
21311     onRender : function(ct,position){
21312         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
21313
21314
21315        
21316                 
21317     },
21318
21319     setHeadline: function (value)
21320     {
21321         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
21322     },
21323     
21324     setFooter: function (value, href)
21325     {
21326         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
21327         
21328         if(href){
21329             this.el.select('a.small-box-footer',true).first().attr('href', href);
21330         }
21331         
21332     },
21333
21334     setContent: function (value)
21335     {
21336         this.el.select('.roo-content',true).first().dom.innerHTML = value;
21337     },
21338
21339     initEvents: function() 
21340     {   
21341         
21342     }
21343     
21344 });
21345
21346  
21347 /*
21348  * - LGPL
21349  *
21350  * TabBox
21351  * 
21352  */
21353 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21354
21355 /**
21356  * @class Roo.bootstrap.dash.TabBox
21357  * @extends Roo.bootstrap.Component
21358  * Bootstrap TabBox class
21359  * @cfg {String} title Title of the TabBox
21360  * @cfg {String} icon Icon of the TabBox
21361  * @cfg {Boolean} showtabs (true|false) show the tabs default true
21362  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
21363  * 
21364  * @constructor
21365  * Create a new TabBox
21366  * @param {Object} config The config object
21367  */
21368
21369
21370 Roo.bootstrap.dash.TabBox = function(config){
21371     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
21372     this.addEvents({
21373         // raw events
21374         /**
21375          * @event addpane
21376          * When a pane is added
21377          * @param {Roo.bootstrap.dash.TabPane} pane
21378          */
21379         "addpane" : true,
21380         /**
21381          * @event activatepane
21382          * When a pane is activated
21383          * @param {Roo.bootstrap.dash.TabPane} pane
21384          */
21385         "activatepane" : true
21386         
21387          
21388     });
21389     
21390     this.panes = [];
21391 };
21392
21393 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
21394
21395     title : '',
21396     icon : false,
21397     showtabs : true,
21398     tabScrollable : false,
21399     
21400     getChildContainer : function()
21401     {
21402         return this.el.select('.tab-content', true).first();
21403     },
21404     
21405     getAutoCreate : function(){
21406         
21407         var header = {
21408             tag: 'li',
21409             cls: 'pull-left header',
21410             html: this.title,
21411             cn : []
21412         };
21413         
21414         if(this.icon){
21415             header.cn.push({
21416                 tag: 'i',
21417                 cls: 'fa ' + this.icon
21418             });
21419         }
21420         
21421         var h = {
21422             tag: 'ul',
21423             cls: 'nav nav-tabs pull-right',
21424             cn: [
21425                 header
21426             ]
21427         };
21428         
21429         if(this.tabScrollable){
21430             h = {
21431                 tag: 'div',
21432                 cls: 'tab-header',
21433                 cn: [
21434                     {
21435                         tag: 'ul',
21436                         cls: 'nav nav-tabs pull-right',
21437                         cn: [
21438                             header
21439                         ]
21440                     }
21441                 ]
21442             }
21443         }
21444         
21445         var cfg = {
21446             tag: 'div',
21447             cls: 'nav-tabs-custom',
21448             cn: [
21449                 h,
21450                 {
21451                     tag: 'div',
21452                     cls: 'tab-content no-padding',
21453                     cn: []
21454                 }
21455             ]
21456         }
21457
21458         return  cfg;
21459     },
21460     initEvents : function()
21461     {
21462         //Roo.log('add add pane handler');
21463         this.on('addpane', this.onAddPane, this);
21464     },
21465      /**
21466      * Updates the box title
21467      * @param {String} html to set the title to.
21468      */
21469     setTitle : function(value)
21470     {
21471         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
21472     },
21473     onAddPane : function(pane)
21474     {
21475         this.panes.push(pane);
21476         //Roo.log('addpane');
21477         //Roo.log(pane);
21478         // tabs are rendere left to right..
21479         if(!this.showtabs){
21480             return;
21481         }
21482         
21483         var ctr = this.el.select('.nav-tabs', true).first();
21484          
21485          
21486         var existing = ctr.select('.nav-tab',true);
21487         var qty = existing.getCount();;
21488         
21489         
21490         var tab = ctr.createChild({
21491             tag : 'li',
21492             cls : 'nav-tab' + (qty ? '' : ' active'),
21493             cn : [
21494                 {
21495                     tag : 'a',
21496                     href:'#',
21497                     html : pane.title
21498                 }
21499             ]
21500         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
21501         pane.tab = tab;
21502         
21503         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
21504         if (!qty) {
21505             pane.el.addClass('active');
21506         }
21507         
21508                 
21509     },
21510     onTabClick : function(ev,un,ob,pane)
21511     {
21512         //Roo.log('tab - prev default');
21513         ev.preventDefault();
21514         
21515         
21516         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
21517         pane.tab.addClass('active');
21518         //Roo.log(pane.title);
21519         this.getChildContainer().select('.tab-pane',true).removeClass('active');
21520         // technically we should have a deactivate event.. but maybe add later.
21521         // and it should not de-activate the selected tab...
21522         this.fireEvent('activatepane', pane);
21523         pane.el.addClass('active');
21524         pane.fireEvent('activate');
21525         
21526         
21527     },
21528     
21529     getActivePane : function()
21530     {
21531         var r = false;
21532         Roo.each(this.panes, function(p) {
21533             if(p.el.hasClass('active')){
21534                 r = p;
21535                 return false;
21536             }
21537             
21538             return;
21539         });
21540         
21541         return r;
21542     }
21543     
21544     
21545 });
21546
21547  
21548 /*
21549  * - LGPL
21550  *
21551  * Tab pane
21552  * 
21553  */
21554 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21555 /**
21556  * @class Roo.bootstrap.TabPane
21557  * @extends Roo.bootstrap.Component
21558  * Bootstrap TabPane class
21559  * @cfg {Boolean} active (false | true) Default false
21560  * @cfg {String} title title of panel
21561
21562  * 
21563  * @constructor
21564  * Create a new TabPane
21565  * @param {Object} config The config object
21566  */
21567
21568 Roo.bootstrap.dash.TabPane = function(config){
21569     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
21570     
21571     this.addEvents({
21572         // raw events
21573         /**
21574          * @event activate
21575          * When a pane is activated
21576          * @param {Roo.bootstrap.dash.TabPane} pane
21577          */
21578         "activate" : true
21579          
21580     });
21581 };
21582
21583 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
21584     
21585     active : false,
21586     title : '',
21587     
21588     // the tabBox that this is attached to.
21589     tab : false,
21590      
21591     getAutoCreate : function() 
21592     {
21593         var cfg = {
21594             tag: 'div',
21595             cls: 'tab-pane'
21596         }
21597         
21598         if(this.active){
21599             cfg.cls += ' active';
21600         }
21601         
21602         return cfg;
21603     },
21604     initEvents  : function()
21605     {
21606         //Roo.log('trigger add pane handler');
21607         this.parent().fireEvent('addpane', this)
21608     },
21609     
21610      /**
21611      * Updates the tab title 
21612      * @param {String} html to set the title to.
21613      */
21614     setTitle: function(str)
21615     {
21616         if (!this.tab) {
21617             return;
21618         }
21619         this.title = str;
21620         this.tab.select('a', true).first().dom.innerHTML = str;
21621         
21622     }
21623     
21624     
21625     
21626 });
21627
21628  
21629
21630
21631  /*
21632  * - LGPL
21633  *
21634  * menu
21635  * 
21636  */
21637 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21638
21639 /**
21640  * @class Roo.bootstrap.menu.Menu
21641  * @extends Roo.bootstrap.Component
21642  * Bootstrap Menu class - container for Menu
21643  * @cfg {String} html Text of the menu
21644  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
21645  * @cfg {String} icon Font awesome icon
21646  * @cfg {String} pos Menu align to (top | bottom) default bottom
21647  * 
21648  * 
21649  * @constructor
21650  * Create a new Menu
21651  * @param {Object} config The config object
21652  */
21653
21654
21655 Roo.bootstrap.menu.Menu = function(config){
21656     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
21657     
21658     this.addEvents({
21659         /**
21660          * @event beforeshow
21661          * Fires before this menu is displayed
21662          * @param {Roo.bootstrap.menu.Menu} this
21663          */
21664         beforeshow : true,
21665         /**
21666          * @event beforehide
21667          * Fires before this menu is hidden
21668          * @param {Roo.bootstrap.menu.Menu} this
21669          */
21670         beforehide : true,
21671         /**
21672          * @event show
21673          * Fires after this menu is displayed
21674          * @param {Roo.bootstrap.menu.Menu} this
21675          */
21676         show : true,
21677         /**
21678          * @event hide
21679          * Fires after this menu is hidden
21680          * @param {Roo.bootstrap.menu.Menu} this
21681          */
21682         hide : true,
21683         /**
21684          * @event click
21685          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
21686          * @param {Roo.bootstrap.menu.Menu} this
21687          * @param {Roo.EventObject} e
21688          */
21689         click : true
21690     });
21691     
21692 };
21693
21694 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
21695     
21696     submenu : false,
21697     html : '',
21698     weight : 'default',
21699     icon : false,
21700     pos : 'bottom',
21701     
21702     
21703     getChildContainer : function() {
21704         if(this.isSubMenu){
21705             return this.el;
21706         }
21707         
21708         return this.el.select('ul.dropdown-menu', true).first();  
21709     },
21710     
21711     getAutoCreate : function()
21712     {
21713         var text = [
21714             {
21715                 tag : 'span',
21716                 cls : 'roo-menu-text',
21717                 html : this.html
21718             }
21719         ];
21720         
21721         if(this.icon){
21722             text.unshift({
21723                 tag : 'i',
21724                 cls : 'fa ' + this.icon
21725             })
21726         }
21727         
21728         
21729         var cfg = {
21730             tag : 'div',
21731             cls : 'btn-group',
21732             cn : [
21733                 {
21734                     tag : 'button',
21735                     cls : 'dropdown-button btn btn-' + this.weight,
21736                     cn : text
21737                 },
21738                 {
21739                     tag : 'button',
21740                     cls : 'dropdown-toggle btn btn-' + this.weight,
21741                     cn : [
21742                         {
21743                             tag : 'span',
21744                             cls : 'caret'
21745                         }
21746                     ]
21747                 },
21748                 {
21749                     tag : 'ul',
21750                     cls : 'dropdown-menu'
21751                 }
21752             ]
21753             
21754         };
21755         
21756         if(this.pos == 'top'){
21757             cfg.cls += ' dropup';
21758         }
21759         
21760         if(this.isSubMenu){
21761             cfg = {
21762                 tag : 'ul',
21763                 cls : 'dropdown-menu'
21764             }
21765         }
21766         
21767         return cfg;
21768     },
21769     
21770     onRender : function(ct, position)
21771     {
21772         this.isSubMenu = ct.hasClass('dropdown-submenu');
21773         
21774         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
21775     },
21776     
21777     initEvents : function() 
21778     {
21779         if(this.isSubMenu){
21780             return;
21781         }
21782         
21783         this.hidden = true;
21784         
21785         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
21786         this.triggerEl.on('click', this.onTriggerPress, this);
21787         
21788         this.buttonEl = this.el.select('button.dropdown-button', true).first();
21789         this.buttonEl.on('click', this.onClick, this);
21790         
21791     },
21792     
21793     list : function()
21794     {
21795         if(this.isSubMenu){
21796             return this.el;
21797         }
21798         
21799         return this.el.select('ul.dropdown-menu', true).first();
21800     },
21801     
21802     onClick : function(e)
21803     {
21804         this.fireEvent("click", this, e);
21805     },
21806     
21807     onTriggerPress  : function(e)
21808     {   
21809         if (this.isVisible()) {
21810             this.hide();
21811         } else {
21812             this.show();
21813         }
21814     },
21815     
21816     isVisible : function(){
21817         return !this.hidden;
21818     },
21819     
21820     show : function()
21821     {
21822         this.fireEvent("beforeshow", this);
21823         
21824         this.hidden = false;
21825         this.el.addClass('open');
21826         
21827         Roo.get(document).on("mouseup", this.onMouseUp, this);
21828         
21829         this.fireEvent("show", this);
21830         
21831         
21832     },
21833     
21834     hide : function()
21835     {
21836         this.fireEvent("beforehide", this);
21837         
21838         this.hidden = true;
21839         this.el.removeClass('open');
21840         
21841         Roo.get(document).un("mouseup", this.onMouseUp);
21842         
21843         this.fireEvent("hide", this);
21844     },
21845     
21846     onMouseUp : function()
21847     {
21848         this.hide();
21849     }
21850     
21851 });
21852
21853  
21854  /*
21855  * - LGPL
21856  *
21857  * menu item
21858  * 
21859  */
21860 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21861
21862 /**
21863  * @class Roo.bootstrap.menu.Item
21864  * @extends Roo.bootstrap.Component
21865  * Bootstrap MenuItem class
21866  * @cfg {Boolean} submenu (true | false) default false
21867  * @cfg {String} html text of the item
21868  * @cfg {String} href the link
21869  * @cfg {Boolean} disable (true | false) default false
21870  * @cfg {Boolean} preventDefault (true | false) default true
21871  * @cfg {String} icon Font awesome icon
21872  * @cfg {String} pos Submenu align to (left | right) default right 
21873  * 
21874  * 
21875  * @constructor
21876  * Create a new Item
21877  * @param {Object} config The config object
21878  */
21879
21880
21881 Roo.bootstrap.menu.Item = function(config){
21882     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
21883     this.addEvents({
21884         /**
21885          * @event mouseover
21886          * Fires when the mouse is hovering over this menu
21887          * @param {Roo.bootstrap.menu.Item} this
21888          * @param {Roo.EventObject} e
21889          */
21890         mouseover : true,
21891         /**
21892          * @event mouseout
21893          * Fires when the mouse exits this menu
21894          * @param {Roo.bootstrap.menu.Item} this
21895          * @param {Roo.EventObject} e
21896          */
21897         mouseout : true,
21898         // raw events
21899         /**
21900          * @event click
21901          * The raw click event for the entire grid.
21902          * @param {Roo.EventObject} e
21903          */
21904         click : true
21905     });
21906 };
21907
21908 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
21909     
21910     submenu : false,
21911     href : '',
21912     html : '',
21913     preventDefault: true,
21914     disable : false,
21915     icon : false,
21916     pos : 'right',
21917     
21918     getAutoCreate : function()
21919     {
21920         var text = [
21921             {
21922                 tag : 'span',
21923                 cls : 'roo-menu-item-text',
21924                 html : this.html
21925             }
21926         ];
21927         
21928         if(this.icon){
21929             text.unshift({
21930                 tag : 'i',
21931                 cls : 'fa ' + this.icon
21932             })
21933         }
21934         
21935         var cfg = {
21936             tag : 'li',
21937             cn : [
21938                 {
21939                     tag : 'a',
21940                     href : this.href || '#',
21941                     cn : text
21942                 }
21943             ]
21944         };
21945         
21946         if(this.disable){
21947             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
21948         }
21949         
21950         if(this.submenu){
21951             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
21952             
21953             if(this.pos == 'left'){
21954                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
21955             }
21956         }
21957         
21958         return cfg;
21959     },
21960     
21961     initEvents : function() 
21962     {
21963         this.el.on('mouseover', this.onMouseOver, this);
21964         this.el.on('mouseout', this.onMouseOut, this);
21965         
21966         this.el.select('a', true).first().on('click', this.onClick, this);
21967         
21968     },
21969     
21970     onClick : function(e)
21971     {
21972         if(this.preventDefault){
21973             e.preventDefault();
21974         }
21975         
21976         this.fireEvent("click", this, e);
21977     },
21978     
21979     onMouseOver : function(e)
21980     {
21981         if(this.submenu && this.pos == 'left'){
21982             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
21983         }
21984         
21985         this.fireEvent("mouseover", this, e);
21986     },
21987     
21988     onMouseOut : function(e)
21989     {
21990         this.fireEvent("mouseout", this, e);
21991     }
21992 });
21993
21994  
21995
21996  /*
21997  * - LGPL
21998  *
21999  * menu separator
22000  * 
22001  */
22002 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22003
22004 /**
22005  * @class Roo.bootstrap.menu.Separator
22006  * @extends Roo.bootstrap.Component
22007  * Bootstrap Separator class
22008  * 
22009  * @constructor
22010  * Create a new Separator
22011  * @param {Object} config The config object
22012  */
22013
22014
22015 Roo.bootstrap.menu.Separator = function(config){
22016     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
22017 };
22018
22019 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
22020     
22021     getAutoCreate : function(){
22022         var cfg = {
22023             tag : 'li',
22024             cls: 'divider'
22025         };
22026         
22027         return cfg;
22028     }
22029    
22030 });
22031
22032  
22033
22034  /*
22035  * - LGPL
22036  *
22037  * Tooltip
22038  * 
22039  */
22040
22041 /**
22042  * @class Roo.bootstrap.Tooltip
22043  * Bootstrap Tooltip class
22044  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
22045  * to determine which dom element triggers the tooltip.
22046  * 
22047  * It needs to add support for additional attributes like tooltip-position
22048  * 
22049  * @constructor
22050  * Create a new Toolti
22051  * @param {Object} config The config object
22052  */
22053
22054 Roo.bootstrap.Tooltip = function(config){
22055     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
22056 };
22057
22058 Roo.apply(Roo.bootstrap.Tooltip, {
22059     /**
22060      * @function init initialize tooltip monitoring.
22061      * @static
22062      */
22063     currentEl : false,
22064     currentTip : false,
22065     currentRegion : false,
22066     
22067     //  init : delay?
22068     
22069     init : function()
22070     {
22071         Roo.get(document).on('mouseover', this.enter ,this);
22072         Roo.get(document).on('mouseout', this.leave, this);
22073          
22074         
22075         this.currentTip = new Roo.bootstrap.Tooltip();
22076     },
22077     
22078     enter : function(ev)
22079     {
22080         var dom = ev.getTarget();
22081         
22082         //Roo.log(['enter',dom]);
22083         var el = Roo.fly(dom);
22084         if (this.currentEl) {
22085             //Roo.log(dom);
22086             //Roo.log(this.currentEl);
22087             //Roo.log(this.currentEl.contains(dom));
22088             if (this.currentEl == el) {
22089                 return;
22090             }
22091             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
22092                 return;
22093             }
22094
22095         }
22096         
22097         
22098         
22099         if (this.currentTip.el) {
22100             this.currentTip.el.hide(); // force hiding...
22101         }    
22102         //Roo.log(ev);
22103         var bindEl = el;
22104         
22105         // you can not look for children, as if el is the body.. then everythign is the child..
22106         if (!el.attr('tooltip')) { //
22107             if (!el.select("[tooltip]").elements.length) {
22108                 return;
22109             }
22110             // is the mouse over this child...?
22111             bindEl = el.select("[tooltip]").first();
22112             var xy = ev.getXY();
22113             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
22114                 //Roo.log("not in region.");
22115                 return;
22116             }
22117             //Roo.log("child element over..");
22118             
22119         }
22120         this.currentEl = bindEl;
22121         this.currentTip.bind(bindEl);
22122         this.currentRegion = Roo.lib.Region.getRegion(dom);
22123         this.currentTip.enter();
22124         
22125     },
22126     leave : function(ev)
22127     {
22128         var dom = ev.getTarget();
22129         //Roo.log(['leave',dom]);
22130         if (!this.currentEl) {
22131             return;
22132         }
22133         
22134         
22135         if (dom != this.currentEl.dom) {
22136             return;
22137         }
22138         var xy = ev.getXY();
22139         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
22140             return;
22141         }
22142         // only activate leave if mouse cursor is outside... bounding box..
22143         
22144         
22145         
22146         
22147         if (this.currentTip) {
22148             this.currentTip.leave();
22149         }
22150         //Roo.log('clear currentEl');
22151         this.currentEl = false;
22152         
22153         
22154     },
22155     alignment : {
22156         'left' : ['r-l', [-2,0], 'right'],
22157         'right' : ['l-r', [2,0], 'left'],
22158         'bottom' : ['t-b', [0,2], 'top'],
22159         'top' : [ 'b-t', [0,-2], 'bottom']
22160     }
22161     
22162 });
22163
22164
22165 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
22166     
22167     
22168     bindEl : false,
22169     
22170     delay : null, // can be { show : 300 , hide: 500}
22171     
22172     timeout : null,
22173     
22174     hoverState : null, //???
22175     
22176     placement : 'bottom', 
22177     
22178     getAutoCreate : function(){
22179     
22180         var cfg = {
22181            cls : 'tooltip',
22182            role : 'tooltip',
22183            cn : [
22184                 {
22185                     cls : 'tooltip-arrow'
22186                 },
22187                 {
22188                     cls : 'tooltip-inner'
22189                 }
22190            ]
22191         };
22192         
22193         return cfg;
22194     },
22195     bind : function(el)
22196     {
22197         this.bindEl = el;
22198     },
22199       
22200     
22201     enter : function () {
22202        
22203         if (this.timeout != null) {
22204             clearTimeout(this.timeout);
22205         }
22206         
22207         this.hoverState = 'in';
22208          //Roo.log("enter - show");
22209         if (!this.delay || !this.delay.show) {
22210             this.show();
22211             return;
22212         }
22213         var _t = this;
22214         this.timeout = setTimeout(function () {
22215             if (_t.hoverState == 'in') {
22216                 _t.show();
22217             }
22218         }, this.delay.show);
22219     },
22220     leave : function()
22221     {
22222         clearTimeout(this.timeout);
22223     
22224         this.hoverState = 'out';
22225          if (!this.delay || !this.delay.hide) {
22226             this.hide();
22227             return;
22228         }
22229        
22230         var _t = this;
22231         this.timeout = setTimeout(function () {
22232             //Roo.log("leave - timeout");
22233             
22234             if (_t.hoverState == 'out') {
22235                 _t.hide();
22236                 Roo.bootstrap.Tooltip.currentEl = false;
22237             }
22238         }, delay);
22239     },
22240     
22241     show : function ()
22242     {
22243         if (!this.el) {
22244             this.render(document.body);
22245         }
22246         // set content.
22247         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
22248         
22249         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
22250         
22251         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
22252         
22253         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
22254         
22255         var placement = typeof this.placement == 'function' ?
22256             this.placement.call(this, this.el, on_el) :
22257             this.placement;
22258             
22259         var autoToken = /\s?auto?\s?/i;
22260         var autoPlace = autoToken.test(placement);
22261         if (autoPlace) {
22262             placement = placement.replace(autoToken, '') || 'top';
22263         }
22264         
22265         //this.el.detach()
22266         //this.el.setXY([0,0]);
22267         this.el.show();
22268         //this.el.dom.style.display='block';
22269         this.el.addClass(placement);
22270         
22271         //this.el.appendTo(on_el);
22272         
22273         var p = this.getPosition();
22274         var box = this.el.getBox();
22275         
22276         if (autoPlace) {
22277             // fixme..
22278         }
22279         var align = Roo.bootstrap.Tooltip.alignment[placement];
22280         this.el.alignTo(this.bindEl, align[0],align[1]);
22281         //var arrow = this.el.select('.arrow',true).first();
22282         //arrow.set(align[2], 
22283         
22284         this.el.addClass('in fade');
22285         this.hoverState = null;
22286         
22287         if (this.el.hasClass('fade')) {
22288             // fade it?
22289         }
22290         
22291     },
22292     hide : function()
22293     {
22294          
22295         if (!this.el) {
22296             return;
22297         }
22298         //this.el.setXY([0,0]);
22299         this.el.removeClass('in');
22300         //this.el.hide();
22301         
22302     }
22303     
22304 });
22305  
22306
22307  /*
22308  * - LGPL
22309  *
22310  * Location Picker
22311  * 
22312  */
22313
22314 /**
22315  * @class Roo.bootstrap.LocationPicker
22316  * @extends Roo.bootstrap.Component
22317  * Bootstrap LocationPicker class
22318  * @cfg {Number} latitude Position when init default 0
22319  * @cfg {Number} longitude Position when init default 0
22320  * @cfg {Number} zoom default 15
22321  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
22322  * @cfg {Boolean} mapTypeControl default false
22323  * @cfg {Boolean} disableDoubleClickZoom default false
22324  * @cfg {Boolean} scrollwheel default true
22325  * @cfg {Boolean} streetViewControl default false
22326  * @cfg {Number} radius default 0
22327  * @cfg {String} locationName
22328  * @cfg {Boolean} draggable default true
22329  * @cfg {Boolean} enableAutocomplete default false
22330  * @cfg {Boolean} enableReverseGeocode default true
22331  * @cfg {String} markerTitle
22332  * 
22333  * @constructor
22334  * Create a new LocationPicker
22335  * @param {Object} config The config object
22336  */
22337
22338
22339 Roo.bootstrap.LocationPicker = function(config){
22340     
22341     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
22342     
22343     this.addEvents({
22344         /**
22345          * @event initial
22346          * Fires when the picker initialized.
22347          * @param {Roo.bootstrap.LocationPicker} this
22348          * @param {Google Location} location
22349          */
22350         initial : true,
22351         /**
22352          * @event positionchanged
22353          * Fires when the picker position changed.
22354          * @param {Roo.bootstrap.LocationPicker} this
22355          * @param {Google Location} location
22356          */
22357         positionchanged : true,
22358         /**
22359          * @event resize
22360          * Fires when the map resize.
22361          * @param {Roo.bootstrap.LocationPicker} this
22362          */
22363         resize : true,
22364         /**
22365          * @event show
22366          * Fires when the map show.
22367          * @param {Roo.bootstrap.LocationPicker} this
22368          */
22369         show : true,
22370         /**
22371          * @event hide
22372          * Fires when the map hide.
22373          * @param {Roo.bootstrap.LocationPicker} this
22374          */
22375         hide : true,
22376         /**
22377          * @event mapClick
22378          * Fires when click the map.
22379          * @param {Roo.bootstrap.LocationPicker} this
22380          * @param {Map event} e
22381          */
22382         mapClick : true,
22383         /**
22384          * @event mapRightClick
22385          * Fires when right click the map.
22386          * @param {Roo.bootstrap.LocationPicker} this
22387          * @param {Map event} e
22388          */
22389         mapRightClick : true,
22390         /**
22391          * @event markerClick
22392          * Fires when click the marker.
22393          * @param {Roo.bootstrap.LocationPicker} this
22394          * @param {Map event} e
22395          */
22396         markerClick : true,
22397         /**
22398          * @event markerRightClick
22399          * Fires when right click the marker.
22400          * @param {Roo.bootstrap.LocationPicker} this
22401          * @param {Map event} e
22402          */
22403         markerRightClick : true,
22404         /**
22405          * @event OverlayViewDraw
22406          * Fires when OverlayView Draw
22407          * @param {Roo.bootstrap.LocationPicker} this
22408          */
22409         OverlayViewDraw : true,
22410         /**
22411          * @event OverlayViewOnAdd
22412          * Fires when OverlayView Draw
22413          * @param {Roo.bootstrap.LocationPicker} this
22414          */
22415         OverlayViewOnAdd : true,
22416         /**
22417          * @event OverlayViewOnRemove
22418          * Fires when OverlayView Draw
22419          * @param {Roo.bootstrap.LocationPicker} this
22420          */
22421         OverlayViewOnRemove : true,
22422         /**
22423          * @event OverlayViewShow
22424          * Fires when OverlayView Draw
22425          * @param {Roo.bootstrap.LocationPicker} this
22426          * @param {Pixel} cpx
22427          */
22428         OverlayViewShow : true,
22429         /**
22430          * @event OverlayViewHide
22431          * Fires when OverlayView Draw
22432          * @param {Roo.bootstrap.LocationPicker} this
22433          */
22434         OverlayViewHide : true
22435     });
22436         
22437 };
22438
22439 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
22440     
22441     gMapContext: false,
22442     
22443     latitude: 0,
22444     longitude: 0,
22445     zoom: 15,
22446     mapTypeId: false,
22447     mapTypeControl: false,
22448     disableDoubleClickZoom: false,
22449     scrollwheel: true,
22450     streetViewControl: false,
22451     radius: 0,
22452     locationName: '',
22453     draggable: true,
22454     enableAutocomplete: false,
22455     enableReverseGeocode: true,
22456     markerTitle: '',
22457     
22458     getAutoCreate: function()
22459     {
22460
22461         var cfg = {
22462             tag: 'div',
22463             cls: 'roo-location-picker'
22464         };
22465         
22466         return cfg
22467     },
22468     
22469     initEvents: function(ct, position)
22470     {       
22471         if(!this.el.getWidth() || this.isApplied()){
22472             return;
22473         }
22474         
22475         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22476         
22477         this.initial();
22478     },
22479     
22480     initial: function()
22481     {
22482         if(!this.mapTypeId){
22483             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
22484         }
22485         
22486         this.gMapContext = this.GMapContext();
22487         
22488         this.initOverlayView();
22489         
22490         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
22491         
22492         var _this = this;
22493                 
22494         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
22495             _this.setPosition(_this.gMapContext.marker.position);
22496         });
22497         
22498         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
22499             _this.fireEvent('mapClick', this, event);
22500             
22501         });
22502
22503         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
22504             _this.fireEvent('mapRightClick', this, event);
22505             
22506         });
22507         
22508         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
22509             _this.fireEvent('markerClick', this, event);
22510             
22511         });
22512
22513         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
22514             _this.fireEvent('markerRightClick', this, event);
22515             
22516         });
22517         
22518         this.setPosition(this.gMapContext.location);
22519         
22520         this.fireEvent('initial', this, this.gMapContext.location);
22521     },
22522     
22523     initOverlayView: function()
22524     {
22525         var _this = this;
22526         
22527         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
22528             
22529             draw: function()
22530             {
22531                 _this.fireEvent('OverlayViewDraw', _this);
22532             },
22533             
22534             onAdd: function()
22535             {
22536                 _this.fireEvent('OverlayViewOnAdd', _this);
22537             },
22538             
22539             onRemove: function()
22540             {
22541                 _this.fireEvent('OverlayViewOnRemove', _this);
22542             },
22543             
22544             show: function(cpx)
22545             {
22546                 _this.fireEvent('OverlayViewShow', _this, cpx);
22547             },
22548             
22549             hide: function()
22550             {
22551                 _this.fireEvent('OverlayViewHide', _this);
22552             }
22553             
22554         });
22555     },
22556     
22557     fromLatLngToContainerPixel: function(event)
22558     {
22559         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
22560     },
22561     
22562     isApplied: function() 
22563     {
22564         return this.getGmapContext() == false ? false : true;
22565     },
22566     
22567     getGmapContext: function() 
22568     {
22569         return this.gMapContext
22570     },
22571     
22572     GMapContext: function() 
22573     {
22574         var position = new google.maps.LatLng(this.latitude, this.longitude);
22575         
22576         var _map = new google.maps.Map(this.el.dom, {
22577             center: position,
22578             zoom: this.zoom,
22579             mapTypeId: this.mapTypeId,
22580             mapTypeControl: this.mapTypeControl,
22581             disableDoubleClickZoom: this.disableDoubleClickZoom,
22582             scrollwheel: this.scrollwheel,
22583             streetViewControl: this.streetViewControl,
22584             locationName: this.locationName,
22585             draggable: this.draggable,
22586             enableAutocomplete: this.enableAutocomplete,
22587             enableReverseGeocode: this.enableReverseGeocode
22588         });
22589         
22590         var _marker = new google.maps.Marker({
22591             position: position,
22592             map: _map,
22593             title: this.markerTitle,
22594             draggable: this.draggable
22595         });
22596         
22597         return {
22598             map: _map,
22599             marker: _marker,
22600             circle: null,
22601             location: position,
22602             radius: this.radius,
22603             locationName: this.locationName,
22604             addressComponents: {
22605                 formatted_address: null,
22606                 addressLine1: null,
22607                 addressLine2: null,
22608                 streetName: null,
22609                 streetNumber: null,
22610                 city: null,
22611                 district: null,
22612                 state: null,
22613                 stateOrProvince: null
22614             },
22615             settings: this,
22616             domContainer: this.el.dom,
22617             geodecoder: new google.maps.Geocoder()
22618         };
22619     },
22620     
22621     drawCircle: function(center, radius, options) 
22622     {
22623         if (this.gMapContext.circle != null) {
22624             this.gMapContext.circle.setMap(null);
22625         }
22626         if (radius > 0) {
22627             radius *= 1;
22628             options = Roo.apply({}, options, {
22629                 strokeColor: "#0000FF",
22630                 strokeOpacity: .35,
22631                 strokeWeight: 2,
22632                 fillColor: "#0000FF",
22633                 fillOpacity: .2
22634             });
22635             
22636             options.map = this.gMapContext.map;
22637             options.radius = radius;
22638             options.center = center;
22639             this.gMapContext.circle = new google.maps.Circle(options);
22640             return this.gMapContext.circle;
22641         }
22642         
22643         return null;
22644     },
22645     
22646     setPosition: function(location) 
22647     {
22648         this.gMapContext.location = location;
22649         this.gMapContext.marker.setPosition(location);
22650         this.gMapContext.map.panTo(location);
22651         this.drawCircle(location, this.gMapContext.radius, {});
22652         
22653         var _this = this;
22654         
22655         if (this.gMapContext.settings.enableReverseGeocode) {
22656             this.gMapContext.geodecoder.geocode({
22657                 latLng: this.gMapContext.location
22658             }, function(results, status) {
22659                 
22660                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
22661                     _this.gMapContext.locationName = results[0].formatted_address;
22662                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
22663                     
22664                     _this.fireEvent('positionchanged', this, location);
22665                 }
22666             });
22667             
22668             return;
22669         }
22670         
22671         this.fireEvent('positionchanged', this, location);
22672     },
22673     
22674     resize: function()
22675     {
22676         google.maps.event.trigger(this.gMapContext.map, "resize");
22677         
22678         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
22679         
22680         this.fireEvent('resize', this);
22681     },
22682     
22683     setPositionByLatLng: function(latitude, longitude)
22684     {
22685         this.setPosition(new google.maps.LatLng(latitude, longitude));
22686     },
22687     
22688     getCurrentPosition: function() 
22689     {
22690         return {
22691             latitude: this.gMapContext.location.lat(),
22692             longitude: this.gMapContext.location.lng()
22693         };
22694     },
22695     
22696     getAddressName: function() 
22697     {
22698         return this.gMapContext.locationName;
22699     },
22700     
22701     getAddressComponents: function() 
22702     {
22703         return this.gMapContext.addressComponents;
22704     },
22705     
22706     address_component_from_google_geocode: function(address_components) 
22707     {
22708         var result = {};
22709         
22710         for (var i = 0; i < address_components.length; i++) {
22711             var component = address_components[i];
22712             if (component.types.indexOf("postal_code") >= 0) {
22713                 result.postalCode = component.short_name;
22714             } else if (component.types.indexOf("street_number") >= 0) {
22715                 result.streetNumber = component.short_name;
22716             } else if (component.types.indexOf("route") >= 0) {
22717                 result.streetName = component.short_name;
22718             } else if (component.types.indexOf("neighborhood") >= 0) {
22719                 result.city = component.short_name;
22720             } else if (component.types.indexOf("locality") >= 0) {
22721                 result.city = component.short_name;
22722             } else if (component.types.indexOf("sublocality") >= 0) {
22723                 result.district = component.short_name;
22724             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
22725                 result.stateOrProvince = component.short_name;
22726             } else if (component.types.indexOf("country") >= 0) {
22727                 result.country = component.short_name;
22728             }
22729         }
22730         
22731         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
22732         result.addressLine2 = "";
22733         return result;
22734     },
22735     
22736     setZoomLevel: function(zoom)
22737     {
22738         this.gMapContext.map.setZoom(zoom);
22739     },
22740     
22741     show: function()
22742     {
22743         if(!this.el){
22744             return;
22745         }
22746         
22747         this.el.show();
22748         
22749         this.resize();
22750         
22751         this.fireEvent('show', this);
22752     },
22753     
22754     hide: function()
22755     {
22756         if(!this.el){
22757             return;
22758         }
22759         
22760         this.el.hide();
22761         
22762         this.fireEvent('hide', this);
22763     }
22764     
22765 });
22766
22767 Roo.apply(Roo.bootstrap.LocationPicker, {
22768     
22769     OverlayView : function(map, options)
22770     {
22771         options = options || {};
22772         
22773         this.setMap(map);
22774     }
22775     
22776     
22777 });/*
22778  * - LGPL
22779  *
22780  * Alert
22781  * 
22782  */
22783
22784 /**
22785  * @class Roo.bootstrap.Alert
22786  * @extends Roo.bootstrap.Component
22787  * Bootstrap Alert class
22788  * @cfg {String} title The title of alert
22789  * @cfg {String} html The content of alert
22790  * @cfg {String} weight (  success | info | warning | danger )
22791  * @cfg {String} faicon font-awesomeicon
22792  * 
22793  * @constructor
22794  * Create a new alert
22795  * @param {Object} config The config object
22796  */
22797
22798
22799 Roo.bootstrap.Alert = function(config){
22800     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
22801     
22802 };
22803
22804 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
22805     
22806     title: '',
22807     html: '',
22808     weight: false,
22809     faicon: false,
22810     
22811     getAutoCreate : function()
22812     {
22813         
22814         var cfg = {
22815             tag : 'div',
22816             cls : 'alert',
22817             cn : [
22818                 {
22819                     tag : 'i',
22820                     cls : 'roo-alert-icon'
22821                     
22822                 },
22823                 {
22824                     tag : 'b',
22825                     cls : 'roo-alert-title',
22826                     html : this.title
22827                 },
22828                 {
22829                     tag : 'span',
22830                     cls : 'roo-alert-text',
22831                     html : this.html
22832                 }
22833             ]
22834         };
22835         
22836         if(this.faicon){
22837             cfg.cn[0].cls += ' fa ' + this.faicon;
22838         }
22839         
22840         if(this.weight){
22841             cfg.cls += ' alert-' + this.weight;
22842         }
22843         
22844         return cfg;
22845     },
22846     
22847     initEvents: function() 
22848     {
22849         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22850     },
22851     
22852     setTitle : function(str)
22853     {
22854         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
22855     },
22856     
22857     setText : function(str)
22858     {
22859         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
22860     },
22861     
22862     setWeight : function(weight)
22863     {
22864         if(this.weight){
22865             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
22866         }
22867         
22868         this.weight = weight;
22869         
22870         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
22871     },
22872     
22873     setIcon : function(icon)
22874     {
22875         if(this.faicon){
22876             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
22877         }
22878         
22879         this.faicon = icon
22880         
22881         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
22882     },
22883     
22884     hide: function() 
22885     {
22886         this.el.hide();   
22887     },
22888     
22889     show: function() 
22890     {  
22891         this.el.show();   
22892     }
22893     
22894 });
22895
22896