c325b21d16e3a340e4fd13b8671a80a08314e377
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129        
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         this.initEvents();
141         
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229         return ret;
230     },
231     
232     addxtypeChild : function (tree, cntr)
233     {
234         Roo.debug && Roo.log('addxtypeChild:' + cntr);
235         var cn = this;
236         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
237         
238         
239         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
240                     (typeof(tree['flexy:foreach']) != 'undefined');
241           
242         
243         
244          skip_children = false;
245         // render the element if it's not BODY.
246         if (tree.xtype != 'Body') {
247            
248             cn = Roo.factory(tree);
249            
250             cn.parentType = this.xtype; //??
251             cn.parentId = this.id;
252             
253             var build_from_html =  Roo.XComponent.build_from_html;
254             
255             
256             // does the container contain child eleemnts with 'xtype' attributes.
257             // that match this xtype..
258             // note - when we render we create these as well..
259             // so we should check to see if body has xtype set.
260             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
261                
262                 var self_cntr_el = Roo.get(this[cntr](false));
263                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
264                 if (echild) { 
265                     Roo.log(Roo.XComponent.build_from_html);
266                     Roo.log("got echild:");
267                     Roo.log(echild);
268                 }
269                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
270                 // and are not displayed -this causes this to use up the wrong element when matching.
271                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
272                 
273                 
274                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
275                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
276                   
277                   
278                   
279                     cn.el = echild;
280                   //  Roo.log("GOT");
281                     //echild.dom.removeAttribute('xtype');
282                 } else {
283                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
284                     Roo.debug && Roo.log(self_cntr_el);
285                     Roo.debug && Roo.log(echild);
286                     Roo.debug && Roo.log(cn);
287                 }
288             }
289            
290             
291            
292             // if object has flexy:if - then it may or may not be rendered.
293             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
294                 // skip a flexy if element.
295                 Roo.debug && Roo.log('skipping render');
296                 Roo.debug && Roo.log(tree);
297                 if (!cn.el) {
298                     Roo.debug && Roo.log('skipping all children');
299                     skip_children = true;
300                 }
301                 
302              } else {
303                  
304                 // actually if flexy:foreach is found, we really want to create 
305                 // multiple copies here...
306                 //Roo.log('render');
307                 //Roo.log(this[cntr]());
308                 cn.render(this[cntr](true));
309              }
310             // then add the element..
311         }
312         
313         
314         // handle the kids..
315         
316         var nitems = [];
317         /*
318         if (typeof (tree.menu) != 'undefined') {
319             tree.menu.parentType = cn.xtype;
320             tree.menu.triggerEl = cn.el;
321             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
322             
323         }
324         */
325         if (!tree.items || !tree.items.length) {
326             cn.items = nitems;
327             return cn;
328         }
329         var items = tree.items;
330         delete tree.items;
331         
332         //Roo.log(items.length);
333             // add the items..
334         if (!skip_children) {    
335             for(var i =0;i < items.length;i++) {
336                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
337             }
338         }
339         
340         cn.items = nitems;
341         
342         this.fireEvent('childrenrendered', this);
343         
344         return cn;
345     } 
346     
347     
348 });
349
350  /*
351  * - LGPL
352  *
353  * Body
354  * 
355  */
356
357 /**
358  * @class Roo.bootstrap.Body
359  * @extends Roo.bootstrap.Component
360  * Bootstrap Body class
361  * 
362  * @constructor
363  * Create a new body
364  * @param {Object} config The config object
365  */
366
367 Roo.bootstrap.Body = function(config){
368     Roo.bootstrap.Body.superclass.constructor.call(this, config);
369     this.el = Roo.get(document.body);
370     if (this.cls && this.cls.length) {
371         Roo.get(document.body).addClass(this.cls);
372     }
373 };
374
375 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
376       
377         autoCreate : {
378         cls: 'container'
379     },
380     onRender : function(ct, position)
381     {
382        /* Roo.log("Roo.bootstrap.Body - onRender");
383         if (this.cls && this.cls.length) {
384             Roo.get(document.body).addClass(this.cls);
385         }
386         // style??? xttr???
387         */
388     }
389     
390     
391  
392    
393 });
394
395  /*
396  * - LGPL
397  *
398  * button group
399  * 
400  */
401
402
403 /**
404  * @class Roo.bootstrap.ButtonGroup
405  * @extends Roo.bootstrap.Component
406  * Bootstrap ButtonGroup class
407  * @cfg {String} size lg | sm | xs (default empty normal)
408  * @cfg {String} align vertical | justified  (default none)
409  * @cfg {String} direction up | down (default down)
410  * @cfg {Boolean} toolbar false | true
411  * @cfg {Boolean} btn true | false
412  * 
413  * 
414  * @constructor
415  * Create a new Input
416  * @param {Object} config The config object
417  */
418
419 Roo.bootstrap.ButtonGroup = function(config){
420     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
421 };
422
423 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
424     
425     size: '',
426     align: '',
427     direction: '',
428     toolbar: false,
429     btn: true,
430
431     getAutoCreate : function(){
432         var cfg = {
433             cls: 'btn-group',
434             html : null
435         }
436         
437         cfg.html = this.html || cfg.html;
438         
439         if (this.toolbar) {
440             cfg = {
441                 cls: 'btn-toolbar',
442                 html: null
443             }
444             
445             return cfg;
446         }
447         
448         if (['vertical','justified'].indexOf(this.align)!==-1) {
449             cfg.cls = 'btn-group-' + this.align;
450             
451             if (this.align == 'justified') {
452                 console.log(this.items);
453             }
454         }
455         
456         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
457             cfg.cls += ' btn-group-' + this.size;
458         }
459         
460         if (this.direction == 'up') {
461             cfg.cls += ' dropup' ;
462         }
463         
464         return cfg;
465     }
466    
467 });
468
469  /*
470  * - LGPL
471  *
472  * button
473  * 
474  */
475
476 /**
477  * @class Roo.bootstrap.Button
478  * @extends Roo.bootstrap.Component
479  * Bootstrap Button class
480  * @cfg {String} html The button content
481  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
482  * @cfg {String} size ( lg | sm | xs)
483  * @cfg {String} tag ( a | input | submit)
484  * @cfg {String} href empty or href
485  * @cfg {Boolean} disabled default false;
486  * @cfg {Boolean} isClose default false;
487  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
488  * @cfg {String} badge text for badge
489  * @cfg {String} theme default 
490  * @cfg {Boolean} inverse 
491  * @cfg {Boolean} toggle 
492  * @cfg {String} ontext text for on toggle state
493  * @cfg {String} offtext text for off toggle state
494  * @cfg {Boolean} defaulton 
495  * @cfg {Boolean} preventDefault  default true
496  * @cfg {Boolean} removeClass remove the standard class..
497  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
498  * 
499  * @constructor
500  * Create a new button
501  * @param {Object} config The config object
502  */
503
504
505 Roo.bootstrap.Button = function(config){
506     Roo.bootstrap.Button.superclass.constructor.call(this, config);
507     this.addEvents({
508         // raw events
509         /**
510          * @event click
511          * When a butotn is pressed
512          * @param {Roo.bootstrap.Button} this
513          * @param {Roo.EventObject} e
514          */
515         "click" : true,
516          /**
517          * @event toggle
518          * After the button has been toggles
519          * @param {Roo.EventObject} e
520          * @param {boolean} pressed (also available as button.pressed)
521          */
522         "toggle" : true
523     });
524 };
525
526 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
527     html: false,
528     active: false,
529     weight: '',
530     size: '',
531     tag: 'button',
532     href: '',
533     disabled: false,
534     isClose: false,
535     glyphicon: '',
536     badge: '',
537     theme: 'default',
538     inverse: false,
539     
540     toggle: false,
541     ontext: 'ON',
542     offtext: 'OFF',
543     defaulton: true,
544     preventDefault: true,
545     removeClass: false,
546     name: false,
547     target: false,
548     
549     
550     pressed : null,
551      
552     
553     getAutoCreate : function(){
554         
555         var cfg = {
556             tag : 'button',
557             cls : 'roo-button',
558             html: ''
559         };
560         
561         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
562             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
563             this.tag = 'button';
564         } else {
565             cfg.tag = this.tag;
566         }
567         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
568         
569         if (this.toggle == true) {
570             cfg={
571                 tag: 'div',
572                 cls: 'slider-frame roo-button',
573                 cn: [
574                     {
575                         tag: 'span',
576                         'data-on-text':'ON',
577                         'data-off-text':'OFF',
578                         cls: 'slider-button',
579                         html: this.offtext
580                     }
581                 ]
582             };
583             
584             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
585                 cfg.cls += ' '+this.weight;
586             }
587             
588             return cfg;
589         }
590         
591         if (this.isClose) {
592             cfg.cls += ' close';
593             
594             cfg["aria-hidden"] = true;
595             
596             cfg.html = "&times;";
597             
598             return cfg;
599         }
600         
601          
602         if (this.theme==='default') {
603             cfg.cls = 'btn roo-button';
604             
605             //if (this.parentType != 'Navbar') {
606             this.weight = this.weight.length ?  this.weight : 'default';
607             //}
608             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
609                 
610                 cfg.cls += ' btn-' + this.weight;
611             }
612         } else if (this.theme==='glow') {
613             
614             cfg.tag = 'a';
615             cfg.cls = 'btn-glow roo-button';
616             
617             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
618                 
619                 cfg.cls += ' ' + this.weight;
620             }
621         }
622    
623         
624         if (this.inverse) {
625             this.cls += ' inverse';
626         }
627         
628         
629         if (this.active) {
630             cfg.cls += ' active';
631         }
632         
633         if (this.disabled) {
634             cfg.disabled = 'disabled';
635         }
636         
637         if (this.items) {
638             Roo.log('changing to ul' );
639             cfg.tag = 'ul';
640             this.glyphicon = 'caret';
641         }
642         
643         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
644          
645         //gsRoo.log(this.parentType);
646         if (this.parentType === 'Navbar' && !this.parent().bar) {
647             Roo.log('changing to li?');
648             
649             cfg.tag = 'li';
650             
651             cfg.cls = '';
652             cfg.cn =  [{
653                 tag : 'a',
654                 cls : 'roo-button',
655                 html : this.html,
656                 href : this.href || '#'
657             }];
658             if (this.menu) {
659                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
660                 cfg.cls += ' dropdown';
661             }   
662             
663             delete cfg.html;
664             
665         }
666         
667        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
668         
669         if (this.glyphicon) {
670             cfg.html = ' ' + cfg.html;
671             
672             cfg.cn = [
673                 {
674                     tag: 'span',
675                     cls: 'glyphicon glyphicon-' + this.glyphicon
676                 }
677             ];
678         }
679         
680         if (this.badge) {
681             cfg.html += ' ';
682             
683             cfg.tag = 'a';
684             
685 //            cfg.cls='btn roo-button';
686             
687             cfg.href=this.href;
688             
689             var value = cfg.html;
690             
691             if(this.glyphicon){
692                 value = {
693                             tag: 'span',
694                             cls: 'glyphicon glyphicon-' + this.glyphicon,
695                             html: this.html
696                         };
697                 
698             }
699             
700             cfg.cn = [
701                 value,
702                 {
703                     tag: 'span',
704                     cls: 'badge',
705                     html: this.badge
706                 }
707             ];
708             
709             cfg.html='';
710         }
711         
712         if (this.menu) {
713             cfg.cls += ' dropdown';
714             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
715         }
716         
717         if (cfg.tag !== 'a' && this.href !== '') {
718             throw "Tag must be a to set href.";
719         } else if (this.href.length > 0) {
720             cfg.href = this.href;
721         }
722         
723         if(this.removeClass){
724             cfg.cls = '';
725         }
726         
727         if(this.target){
728             cfg.target = this.target;
729         }
730         
731         return cfg;
732     },
733     initEvents: function() {
734        // Roo.log('init events?');
735 //        Roo.log(this.el.dom);
736         // add the menu...
737         
738         if (typeof (this.menu) != 'undefined') {
739             this.menu.parentType = this.xtype;
740             this.menu.triggerEl = this.el;
741             this.addxtype(Roo.apply({}, this.menu));
742         }
743
744
745        if (this.el.hasClass('roo-button')) {
746             this.el.on('click', this.onClick, this);
747        } else {
748             this.el.select('.roo-button').on('click', this.onClick, this);
749        }
750        
751        if(this.removeClass){
752            this.el.on('click', this.onClick, this);
753        }
754        
755        this.el.enableDisplayMode();
756         
757     },
758     onClick : function(e)
759     {
760         if (this.disabled) {
761             return;
762         }
763         
764         
765         Roo.log('button on click ');
766         if(this.preventDefault){
767             e.preventDefault();
768         }
769         if (this.pressed === true || this.pressed === false) {
770             this.pressed = !this.pressed;
771             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
772             this.fireEvent('toggle', this, e, this.pressed);
773         }
774         
775         
776         this.fireEvent('click', this, e);
777     },
778     
779     /**
780      * Enables this button
781      */
782     enable : function()
783     {
784         this.disabled = false;
785         this.el.removeClass('disabled');
786     },
787     
788     /**
789      * Disable this button
790      */
791     disable : function()
792     {
793         this.disabled = true;
794         this.el.addClass('disabled');
795     },
796      /**
797      * sets the active state on/off, 
798      * @param {Boolean} state (optional) Force a particular state
799      */
800     setActive : function(v) {
801         
802         this.el[v ? 'addClass' : 'removeClass']('active');
803     },
804      /**
805      * toggles the current active state 
806      */
807     toggleActive : function()
808     {
809        var active = this.el.hasClass('active');
810        this.setActive(!active);
811        
812         
813     },
814     setText : function(str)
815     {
816         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
817     },
818     getText : function()
819     {
820         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
821     },
822     hide: function() {
823        
824      
825         this.el.hide();   
826     },
827     show: function() {
828        
829         this.el.show();   
830     }
831     
832     
833 });
834
835  /*
836  * - LGPL
837  *
838  * column
839  * 
840  */
841
842 /**
843  * @class Roo.bootstrap.Column
844  * @extends Roo.bootstrap.Component
845  * Bootstrap Column class
846  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
847  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
848  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
849  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
850  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
851  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
852  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
853  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
854  *
855  * 
856  * @cfg {Boolean} hidden (true|false) hide the element
857  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
858  * @cfg {String} fa (ban|check|...) font awesome icon
859  * @cfg {Number} fasize (1|2|....) font awsome size
860
861  * @cfg {String} icon (info-sign|check|...) glyphicon name
862
863  * @cfg {String} html content of column.
864  * 
865  * @constructor
866  * Create a new Column
867  * @param {Object} config The config object
868  */
869
870 Roo.bootstrap.Column = function(config){
871     Roo.bootstrap.Column.superclass.constructor.call(this, config);
872 };
873
874 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
875     
876     xs: false,
877     sm: false,
878     md: false,
879     lg: false,
880     xsoff: false,
881     smoff: false,
882     mdoff: false,
883     lgoff: false,
884     html: '',
885     offset: 0,
886     alert: false,
887     fa: false,
888     icon : false,
889     hidden : false,
890     fasize : 1,
891     
892     getAutoCreate : function(){
893         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
894         
895         cfg = {
896             tag: 'div',
897             cls: 'column'
898         };
899         
900         var settings=this;
901         ['xs','sm','md','lg'].map(function(size){
902             //Roo.log( size + ':' + settings[size]);
903             
904             if (settings[size+'off'] !== false) {
905                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
906             }
907             
908             if (settings[size] === false) {
909                 return;
910             }
911             Roo.log(settings[size]);
912             if (!settings[size]) { // 0 = hidden
913                 cfg.cls += ' hidden-' + size;
914                 return;
915             }
916             cfg.cls += ' col-' + size + '-' + settings[size];
917             
918         });
919         
920         if (this.hidden) {
921             cfg.cls += ' hidden';
922         }
923         
924         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
925             cfg.cls +=' alert alert-' + this.alert;
926         }
927         
928         
929         if (this.html.length) {
930             cfg.html = this.html;
931         }
932         if (this.fa) {
933             var fasize = '';
934             if (this.fasize > 1) {
935                 fasize = ' fa-' + this.fasize + 'x';
936             }
937             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
938             
939             
940         }
941         if (this.icon) {
942             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
943         }
944         
945         return cfg;
946     }
947    
948 });
949
950  
951
952  /*
953  * - LGPL
954  *
955  * page container.
956  * 
957  */
958
959
960 /**
961  * @class Roo.bootstrap.Container
962  * @extends Roo.bootstrap.Component
963  * Bootstrap Container class
964  * @cfg {Boolean} jumbotron is it a jumbotron element
965  * @cfg {String} html content of element
966  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
967  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
968  * @cfg {String} header content of header (for panel)
969  * @cfg {String} footer content of footer (for panel)
970  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
971  * @cfg {String} tag (header|aside|section) type of HTML tag.
972  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
973  * @cfg {String} fa (ban|check|...) font awesome icon
974  * @cfg {String} icon (info-sign|check|...) glyphicon name
975  * @cfg {Boolean} hidden (true|false) hide the element
976
977  *     
978  * @constructor
979  * Create a new Container
980  * @param {Object} config The config object
981  */
982
983 Roo.bootstrap.Container = function(config){
984     Roo.bootstrap.Container.superclass.constructor.call(this, config);
985 };
986
987 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
988     
989     jumbotron : false,
990     well: '',
991     panel : '',
992     header: '',
993     footer : '',
994     sticky: '',
995     tag : false,
996     alert : false,
997     fa: false,
998     icon : false,
999   
1000      
1001     getChildContainer : function() {
1002         
1003         if(!this.el){
1004             return false;
1005         }
1006         
1007         if (this.panel.length) {
1008             return this.el.select('.panel-body',true).first();
1009         }
1010         
1011         return this.el;
1012     },
1013     
1014     
1015     getAutoCreate : function(){
1016         
1017         var cfg = {
1018             tag : this.tag || 'div',
1019             html : '',
1020             cls : ''
1021         };
1022         if (this.jumbotron) {
1023             cfg.cls = 'jumbotron';
1024         }
1025         
1026         
1027         
1028         // - this is applied by the parent..
1029         //if (this.cls) {
1030         //    cfg.cls = this.cls + '';
1031         //}
1032         
1033         if (this.sticky.length) {
1034             
1035             var bd = Roo.get(document.body);
1036             if (!bd.hasClass('bootstrap-sticky')) {
1037                 bd.addClass('bootstrap-sticky');
1038                 Roo.select('html',true).setStyle('height', '100%');
1039             }
1040              
1041             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1042         }
1043         
1044         
1045         if (this.well.length) {
1046             switch (this.well) {
1047                 case 'lg':
1048                 case 'sm':
1049                     cfg.cls +=' well well-' +this.well;
1050                     break;
1051                 default:
1052                     cfg.cls +=' well';
1053                     break;
1054             }
1055         }
1056         
1057         if (this.hidden) {
1058             cfg.cls += ' hidden';
1059         }
1060         
1061         
1062         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1063             cfg.cls +=' alert alert-' + this.alert;
1064         }
1065         
1066         var body = cfg;
1067         
1068         if (this.panel.length) {
1069             cfg.cls += ' panel panel-' + this.panel;
1070             cfg.cn = [];
1071             if (this.header.length) {
1072                 cfg.cn.push({
1073                     
1074                     cls : 'panel-heading',
1075                     cn : [{
1076                         tag: 'h3',
1077                         cls : 'panel-title',
1078                         html : this.header
1079                     }]
1080                     
1081                 });
1082             }
1083             body = false;
1084             cfg.cn.push({
1085                 cls : 'panel-body',
1086                 html : this.html
1087             });
1088             
1089             
1090             if (this.footer.length) {
1091                 cfg.cn.push({
1092                     cls : 'panel-footer',
1093                     html : this.footer
1094                     
1095                 });
1096             }
1097             
1098         }
1099         
1100         if (body) {
1101             body.html = this.html || cfg.html;
1102             // prefix with the icons..
1103             if (this.fa) {
1104                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1105             }
1106             if (this.icon) {
1107                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1108             }
1109             
1110             
1111         }
1112         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1113             cfg.cls =  'container';
1114         }
1115         
1116         return cfg;
1117     },
1118     
1119     titleEl : function()
1120     {
1121         if(!this.el || !this.panel.length || !this.header.length){
1122             return;
1123         }
1124         
1125         return this.el.select('.panel-title',true).first();
1126     },
1127     
1128     setTitle : function(v)
1129     {
1130         var titleEl = this.titleEl();
1131         
1132         if(!titleEl){
1133             return;
1134         }
1135         
1136         titleEl.dom.innerHTML = v;
1137     },
1138     
1139     getTitle : function()
1140     {
1141         
1142         var titleEl = this.titleEl();
1143         
1144         if(!titleEl){
1145             return '';
1146         }
1147         
1148         return titleEl.dom.innerHTML;
1149     },
1150     
1151     show : function() {
1152         this.el.removeClass('hidden');
1153     },
1154     hide: function() {
1155         if (!this.el.hasClass('hidden')) {
1156             this.el.addClass('hidden');
1157         }
1158         
1159     }
1160    
1161 });
1162
1163  /*
1164  * - LGPL
1165  *
1166  * image
1167  * 
1168  */
1169
1170
1171 /**
1172  * @class Roo.bootstrap.Img
1173  * @extends Roo.bootstrap.Component
1174  * Bootstrap Img class
1175  * @cfg {Boolean} imgResponsive false | true
1176  * @cfg {String} border rounded | circle | thumbnail
1177  * @cfg {String} src image source
1178  * @cfg {String} alt image alternative text
1179  * @cfg {String} href a tag href
1180  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1181  * 
1182  * @constructor
1183  * Create a new Input
1184  * @param {Object} config The config object
1185  */
1186
1187 Roo.bootstrap.Img = function(config){
1188     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1189     
1190     this.addEvents({
1191         // img events
1192         /**
1193          * @event click
1194          * The img click event for the img.
1195          * @param {Roo.EventObject} e
1196          */
1197         "click" : true
1198     });
1199 };
1200
1201 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1202     
1203     imgResponsive: true,
1204     border: '',
1205     src: '',
1206     href: false,
1207     target: false,
1208
1209     getAutoCreate : function(){
1210         
1211         var cfg = {
1212             tag: 'img',
1213             cls: (this.imgResponsive) ? 'img-responsive' : '',
1214             html : null
1215         }
1216         
1217         cfg.html = this.html || cfg.html;
1218         
1219         cfg.src = this.src || cfg.src;
1220         
1221         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1222             cfg.cls += ' img-' + this.border;
1223         }
1224         
1225         if(this.alt){
1226             cfg.alt = this.alt;
1227         }
1228         
1229         if(this.href){
1230             var a = {
1231                 tag: 'a',
1232                 href: this.href,
1233                 cn: [
1234                     cfg
1235                 ]
1236             }
1237             
1238             if(this.target){
1239                 a.target = this.target;
1240             }
1241             
1242         }
1243         
1244         
1245         return (this.href) ? a : cfg;
1246     },
1247     
1248     initEvents: function() {
1249         
1250         if(!this.href){
1251             this.el.on('click', this.onClick, this);
1252         }
1253     },
1254     
1255     onClick : function(e)
1256     {
1257         Roo.log('img onclick');
1258         this.fireEvent('click', this, e);
1259     }
1260    
1261 });
1262
1263  /*
1264  * - LGPL
1265  *
1266  * image
1267  * 
1268  */
1269
1270
1271 /**
1272  * @class Roo.bootstrap.Link
1273  * @extends Roo.bootstrap.Component
1274  * Bootstrap Link Class
1275  * @cfg {String} alt image alternative text
1276  * @cfg {String} href a tag href
1277  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1278  * @cfg {String} html the content of the link.
1279  * @cfg {String} anchor name for the anchor link
1280
1281  * @cfg {Boolean} preventDefault (true | false) default false
1282
1283  * 
1284  * @constructor
1285  * Create a new Input
1286  * @param {Object} config The config object
1287  */
1288
1289 Roo.bootstrap.Link = function(config){
1290     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1291     
1292     this.addEvents({
1293         // img events
1294         /**
1295          * @event click
1296          * The img click event for the img.
1297          * @param {Roo.EventObject} e
1298          */
1299         "click" : true
1300     });
1301 };
1302
1303 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1304     
1305     href: false,
1306     target: false,
1307     preventDefault: false,
1308     anchor : false,
1309     alt : false,
1310
1311     getAutoCreate : function()
1312     {
1313         
1314         var cfg = {
1315             tag: 'a'
1316         };
1317         // anchor's do not require html/href...
1318         if (this.anchor === false) {
1319             cfg.html = this.html || 'html-missing';
1320             cfg.href = this.href || '#';
1321         } else {
1322             cfg.name = this.anchor;
1323             if (this.html !== false) {
1324                 cfg.html = this.html;
1325             }
1326             if (this.href !== false) {
1327                 cfg.href = this.href;
1328             }
1329         }
1330         
1331         if(this.alt !== false){
1332             cfg.alt = this.alt;
1333         }
1334         
1335         
1336         if(this.target !== false) {
1337             cfg.target = this.target;
1338         }
1339         
1340         return cfg;
1341     },
1342     
1343     initEvents: function() {
1344         
1345         if(!this.href || this.preventDefault){
1346             this.el.on('click', this.onClick, this);
1347         }
1348     },
1349     
1350     onClick : function(e)
1351     {
1352         if(this.preventDefault){
1353             e.preventDefault();
1354         }
1355         //Roo.log('img onclick');
1356         this.fireEvent('click', this, e);
1357     }
1358    
1359 });
1360
1361  /*
1362  * - LGPL
1363  *
1364  * header
1365  * 
1366  */
1367
1368 /**
1369  * @class Roo.bootstrap.Header
1370  * @extends Roo.bootstrap.Component
1371  * Bootstrap Header class
1372  * @cfg {String} html content of header
1373  * @cfg {Number} level (1|2|3|4|5|6) default 1
1374  * 
1375  * @constructor
1376  * Create a new Header
1377  * @param {Object} config The config object
1378  */
1379
1380
1381 Roo.bootstrap.Header  = function(config){
1382     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1383 };
1384
1385 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1386     
1387     //href : false,
1388     html : false,
1389     level : 1,
1390     
1391     
1392     
1393     getAutoCreate : function(){
1394         
1395         
1396         
1397         var cfg = {
1398             tag: 'h' + (1 *this.level),
1399             html: this.html || ''
1400         } ;
1401         
1402         return cfg;
1403     }
1404    
1405 });
1406
1407  
1408
1409  /*
1410  * Based on:
1411  * Ext JS Library 1.1.1
1412  * Copyright(c) 2006-2007, Ext JS, LLC.
1413  *
1414  * Originally Released Under LGPL - original licence link has changed is not relivant.
1415  *
1416  * Fork - LGPL
1417  * <script type="text/javascript">
1418  */
1419  
1420 /**
1421  * @class Roo.bootstrap.MenuMgr
1422  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1423  * @singleton
1424  */
1425 Roo.bootstrap.MenuMgr = function(){
1426    var menus, active, groups = {}, attached = false, lastShow = new Date();
1427
1428    // private - called when first menu is created
1429    function init(){
1430        menus = {};
1431        active = new Roo.util.MixedCollection();
1432        Roo.get(document).addKeyListener(27, function(){
1433            if(active.length > 0){
1434                hideAll();
1435            }
1436        });
1437    }
1438
1439    // private
1440    function hideAll(){
1441        if(active && active.length > 0){
1442            var c = active.clone();
1443            c.each(function(m){
1444                m.hide();
1445            });
1446        }
1447    }
1448
1449    // private
1450    function onHide(m){
1451        active.remove(m);
1452        if(active.length < 1){
1453            Roo.get(document).un("mouseup", onMouseDown);
1454             
1455            attached = false;
1456        }
1457    }
1458
1459    // private
1460    function onShow(m){
1461        var last = active.last();
1462        lastShow = new Date();
1463        active.add(m);
1464        if(!attached){
1465           Roo.get(document).on("mouseup", onMouseDown);
1466            
1467            attached = true;
1468        }
1469        if(m.parentMenu){
1470           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1471           m.parentMenu.activeChild = m;
1472        }else if(last && last.isVisible()){
1473           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1474        }
1475    }
1476
1477    // private
1478    function onBeforeHide(m){
1479        if(m.activeChild){
1480            m.activeChild.hide();
1481        }
1482        if(m.autoHideTimer){
1483            clearTimeout(m.autoHideTimer);
1484            delete m.autoHideTimer;
1485        }
1486    }
1487
1488    // private
1489    function onBeforeShow(m){
1490        var pm = m.parentMenu;
1491        if(!pm && !m.allowOtherMenus){
1492            hideAll();
1493        }else if(pm && pm.activeChild && active != m){
1494            pm.activeChild.hide();
1495        }
1496    }
1497
1498    // private
1499    function onMouseDown(e){
1500         Roo.log("on MouseDown");
1501         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1502            hideAll();
1503         }
1504         
1505         
1506    }
1507
1508    // private
1509    function onBeforeCheck(mi, state){
1510        if(state){
1511            var g = groups[mi.group];
1512            for(var i = 0, l = g.length; i < l; i++){
1513                if(g[i] != mi){
1514                    g[i].setChecked(false);
1515                }
1516            }
1517        }
1518    }
1519
1520    return {
1521
1522        /**
1523         * Hides all menus that are currently visible
1524         */
1525        hideAll : function(){
1526             hideAll();  
1527        },
1528
1529        // private
1530        register : function(menu){
1531            if(!menus){
1532                init();
1533            }
1534            menus[menu.id] = menu;
1535            menu.on("beforehide", onBeforeHide);
1536            menu.on("hide", onHide);
1537            menu.on("beforeshow", onBeforeShow);
1538            menu.on("show", onShow);
1539            var g = menu.group;
1540            if(g && menu.events["checkchange"]){
1541                if(!groups[g]){
1542                    groups[g] = [];
1543                }
1544                groups[g].push(menu);
1545                menu.on("checkchange", onCheck);
1546            }
1547        },
1548
1549         /**
1550          * Returns a {@link Roo.menu.Menu} object
1551          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1552          * be used to generate and return a new Menu instance.
1553          */
1554        get : function(menu){
1555            if(typeof menu == "string"){ // menu id
1556                return menus[menu];
1557            }else if(menu.events){  // menu instance
1558                return menu;
1559            }
1560            /*else if(typeof menu.length == 'number'){ // array of menu items?
1561                return new Roo.bootstrap.Menu({items:menu});
1562            }else{ // otherwise, must be a config
1563                return new Roo.bootstrap.Menu(menu);
1564            }
1565            */
1566            return false;
1567        },
1568
1569        // private
1570        unregister : function(menu){
1571            delete menus[menu.id];
1572            menu.un("beforehide", onBeforeHide);
1573            menu.un("hide", onHide);
1574            menu.un("beforeshow", onBeforeShow);
1575            menu.un("show", onShow);
1576            var g = menu.group;
1577            if(g && menu.events["checkchange"]){
1578                groups[g].remove(menu);
1579                menu.un("checkchange", onCheck);
1580            }
1581        },
1582
1583        // private
1584        registerCheckable : function(menuItem){
1585            var g = menuItem.group;
1586            if(g){
1587                if(!groups[g]){
1588                    groups[g] = [];
1589                }
1590                groups[g].push(menuItem);
1591                menuItem.on("beforecheckchange", onBeforeCheck);
1592            }
1593        },
1594
1595        // private
1596        unregisterCheckable : function(menuItem){
1597            var g = menuItem.group;
1598            if(g){
1599                groups[g].remove(menuItem);
1600                menuItem.un("beforecheckchange", onBeforeCheck);
1601            }
1602        }
1603    };
1604 }();/*
1605  * - LGPL
1606  *
1607  * menu
1608  * 
1609  */
1610
1611 /**
1612  * @class Roo.bootstrap.Menu
1613  * @extends Roo.bootstrap.Component
1614  * Bootstrap Menu class - container for MenuItems
1615  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1616  * 
1617  * @constructor
1618  * Create a new Menu
1619  * @param {Object} config The config object
1620  */
1621
1622
1623 Roo.bootstrap.Menu = function(config){
1624     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1625     if (this.registerMenu) {
1626         Roo.bootstrap.MenuMgr.register(this);
1627     }
1628     this.addEvents({
1629         /**
1630          * @event beforeshow
1631          * Fires before this menu is displayed
1632          * @param {Roo.menu.Menu} this
1633          */
1634         beforeshow : true,
1635         /**
1636          * @event beforehide
1637          * Fires before this menu is hidden
1638          * @param {Roo.menu.Menu} this
1639          */
1640         beforehide : true,
1641         /**
1642          * @event show
1643          * Fires after this menu is displayed
1644          * @param {Roo.menu.Menu} this
1645          */
1646         show : true,
1647         /**
1648          * @event hide
1649          * Fires after this menu is hidden
1650          * @param {Roo.menu.Menu} this
1651          */
1652         hide : true,
1653         /**
1654          * @event click
1655          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1656          * @param {Roo.menu.Menu} this
1657          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1658          * @param {Roo.EventObject} e
1659          */
1660         click : true,
1661         /**
1662          * @event mouseover
1663          * Fires when the mouse is hovering over this menu
1664          * @param {Roo.menu.Menu} this
1665          * @param {Roo.EventObject} e
1666          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1667          */
1668         mouseover : true,
1669         /**
1670          * @event mouseout
1671          * Fires when the mouse exits this menu
1672          * @param {Roo.menu.Menu} this
1673          * @param {Roo.EventObject} e
1674          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1675          */
1676         mouseout : true,
1677         /**
1678          * @event itemclick
1679          * Fires when a menu item contained in this menu is clicked
1680          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1681          * @param {Roo.EventObject} e
1682          */
1683         itemclick: true
1684     });
1685     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1686 };
1687
1688 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1689     
1690    /// html : false,
1691     //align : '',
1692     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1693     type: false,
1694     /**
1695      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1696      */
1697     registerMenu : true,
1698     
1699     menuItems :false, // stores the menu items..
1700     
1701     hidden:true,
1702     
1703     parentMenu : false,
1704     
1705     getChildContainer : function() {
1706         return this.el;  
1707     },
1708     
1709     getAutoCreate : function(){
1710          
1711         //if (['right'].indexOf(this.align)!==-1) {
1712         //    cfg.cn[1].cls += ' pull-right'
1713         //}
1714         
1715         
1716         var cfg = {
1717             tag : 'ul',
1718             cls : 'dropdown-menu' ,
1719             style : 'z-index:1000'
1720             
1721         }
1722         
1723         if (this.type === 'submenu') {
1724             cfg.cls = 'submenu active';
1725         }
1726         if (this.type === 'treeview') {
1727             cfg.cls = 'treeview-menu';
1728         }
1729         
1730         return cfg;
1731     },
1732     initEvents : function() {
1733         
1734        // Roo.log("ADD event");
1735        // Roo.log(this.triggerEl.dom);
1736         this.triggerEl.on('click', this.onTriggerPress, this);
1737         this.triggerEl.addClass('dropdown-toggle');
1738         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1739
1740         this.el.on("mouseover", this.onMouseOver, this);
1741         this.el.on("mouseout", this.onMouseOut, this);
1742         
1743         
1744     },
1745     findTargetItem : function(e){
1746         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1747         if(!t){
1748             return false;
1749         }
1750         //Roo.log(t);         Roo.log(t.id);
1751         if(t && t.id){
1752             //Roo.log(this.menuitems);
1753             return this.menuitems.get(t.id);
1754             
1755             //return this.items.get(t.menuItemId);
1756         }
1757         
1758         return false;
1759     },
1760     onClick : function(e){
1761         Roo.log("menu.onClick");
1762         var t = this.findTargetItem(e);
1763         if(!t || t.isContainer){
1764             return;
1765         }
1766         Roo.log(e);
1767         /*
1768         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1769             if(t == this.activeItem && t.shouldDeactivate(e)){
1770                 this.activeItem.deactivate();
1771                 delete this.activeItem;
1772                 return;
1773             }
1774             if(t.canActivate){
1775                 this.setActiveItem(t, true);
1776             }
1777             return;
1778             
1779             
1780         }
1781         */
1782        
1783         Roo.log('pass click event');
1784         
1785         t.onClick(e);
1786         
1787         this.fireEvent("click", this, t, e);
1788         
1789         this.hide();
1790     },
1791      onMouseOver : function(e){
1792         var t  = this.findTargetItem(e);
1793         //Roo.log(t);
1794         //if(t){
1795         //    if(t.canActivate && !t.disabled){
1796         //        this.setActiveItem(t, true);
1797         //    }
1798         //}
1799         
1800         this.fireEvent("mouseover", this, e, t);
1801     },
1802     isVisible : function(){
1803         return !this.hidden;
1804     },
1805      onMouseOut : function(e){
1806         var t  = this.findTargetItem(e);
1807         
1808         //if(t ){
1809         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1810         //        this.activeItem.deactivate();
1811         //        delete this.activeItem;
1812         //    }
1813         //}
1814         this.fireEvent("mouseout", this, e, t);
1815     },
1816     
1817     
1818     /**
1819      * Displays this menu relative to another element
1820      * @param {String/HTMLElement/Roo.Element} element The element to align to
1821      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1822      * the element (defaults to this.defaultAlign)
1823      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1824      */
1825     show : function(el, pos, parentMenu){
1826         this.parentMenu = parentMenu;
1827         if(!this.el){
1828             this.render();
1829         }
1830         this.fireEvent("beforeshow", this);
1831         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1832     },
1833      /**
1834      * Displays this menu at a specific xy position
1835      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1836      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1837      */
1838     showAt : function(xy, parentMenu, /* private: */_e){
1839         this.parentMenu = parentMenu;
1840         if(!this.el){
1841             this.render();
1842         }
1843         if(_e !== false){
1844             this.fireEvent("beforeshow", this);
1845             //xy = this.el.adjustForConstraints(xy);
1846         }
1847         
1848         //this.el.show();
1849         this.hideMenuItems();
1850         this.hidden = false;
1851         this.triggerEl.addClass('open');
1852         
1853         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
1854             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
1855         }
1856         
1857         this.el.setXY(xy);
1858         this.focus();
1859         this.fireEvent("show", this);
1860     },
1861     
1862     focus : function(){
1863         return;
1864         if(!this.hidden){
1865             this.doFocus.defer(50, this);
1866         }
1867     },
1868
1869     doFocus : function(){
1870         if(!this.hidden){
1871             this.focusEl.focus();
1872         }
1873     },
1874
1875     /**
1876      * Hides this menu and optionally all parent menus
1877      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1878      */
1879     hide : function(deep){
1880         
1881         this.hideMenuItems();
1882         if(this.el && this.isVisible()){
1883             this.fireEvent("beforehide", this);
1884             if(this.activeItem){
1885                 this.activeItem.deactivate();
1886                 this.activeItem = null;
1887             }
1888             this.triggerEl.removeClass('open');;
1889             this.hidden = true;
1890             this.fireEvent("hide", this);
1891         }
1892         if(deep === true && this.parentMenu){
1893             this.parentMenu.hide(true);
1894         }
1895     },
1896     
1897     onTriggerPress  : function(e)
1898     {
1899         
1900         Roo.log('trigger press');
1901         //Roo.log(e.getTarget());
1902        // Roo.log(this.triggerEl.dom);
1903         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1904             return;
1905         }
1906         if (this.isVisible()) {
1907             Roo.log('hide');
1908             this.hide();
1909         } else {
1910             this.show(this.triggerEl, false, false);
1911         }
1912         
1913         
1914     },
1915     
1916          
1917        
1918     
1919     hideMenuItems : function()
1920     {
1921         //$(backdrop).remove()
1922         Roo.select('.open',true).each(function(aa) {
1923             
1924             aa.removeClass('open');
1925           //var parent = getParent($(this))
1926           //var relatedTarget = { relatedTarget: this }
1927           
1928            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1929           //if (e.isDefaultPrevented()) return
1930            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1931         })
1932     },
1933     addxtypeChild : function (tree, cntr) {
1934         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1935           
1936         this.menuitems.add(comp);
1937         return comp;
1938
1939     },
1940     getEl : function()
1941     {
1942         Roo.log(this.el);
1943         return this.el;
1944     }
1945 });
1946
1947  
1948  /*
1949  * - LGPL
1950  *
1951  * menu item
1952  * 
1953  */
1954
1955
1956 /**
1957  * @class Roo.bootstrap.MenuItem
1958  * @extends Roo.bootstrap.Component
1959  * Bootstrap MenuItem class
1960  * @cfg {String} html the menu label
1961  * @cfg {String} href the link
1962  * @cfg {Boolean} preventDefault (true | false) default true
1963  * @cfg {Boolean} isContainer (true | false) default false
1964  * 
1965  * 
1966  * @constructor
1967  * Create a new MenuItem
1968  * @param {Object} config The config object
1969  */
1970
1971
1972 Roo.bootstrap.MenuItem = function(config){
1973     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1974     this.addEvents({
1975         // raw events
1976         /**
1977          * @event click
1978          * The raw click event for the entire grid.
1979          * @param {Roo.bootstrap.MenuItem} this
1980          * @param {Roo.EventObject} e
1981          */
1982         "click" : true
1983     });
1984 };
1985
1986 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1987     
1988     href : false,
1989     html : false,
1990     preventDefault: true,
1991     isContainer : false,
1992     
1993     getAutoCreate : function(){
1994         
1995         if(this.isContainer){
1996             return {
1997                 tag: 'li',
1998                 cls: 'dropdown-menu-item'
1999             };
2000         }
2001         
2002         var cfg= {
2003             tag: 'li',
2004             cls: 'dropdown-menu-item',
2005             cn: [
2006                     {
2007                         tag : 'a',
2008                         href : '#',
2009                         html : 'Link'
2010                     }
2011                 ]
2012         };
2013         if (this.parent().type == 'treeview') {
2014             cfg.cls = 'treeview-menu';
2015         }
2016         
2017         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2018         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2019         return cfg;
2020     },
2021     
2022     initEvents: function() {
2023         
2024         //this.el.select('a').on('click', this.onClick, this);
2025         
2026     },
2027     onClick : function(e)
2028     {
2029         Roo.log('item on click ');
2030         //if(this.preventDefault){
2031         //    e.preventDefault();
2032         //}
2033         //this.parent().hideMenuItems();
2034         
2035         this.fireEvent('click', this, e);
2036     },
2037     getEl : function()
2038     {
2039         return this.el;
2040     }
2041 });
2042
2043  
2044
2045  /*
2046  * - LGPL
2047  *
2048  * menu separator
2049  * 
2050  */
2051
2052
2053 /**
2054  * @class Roo.bootstrap.MenuSeparator
2055  * @extends Roo.bootstrap.Component
2056  * Bootstrap MenuSeparator class
2057  * 
2058  * @constructor
2059  * Create a new MenuItem
2060  * @param {Object} config The config object
2061  */
2062
2063
2064 Roo.bootstrap.MenuSeparator = function(config){
2065     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2066 };
2067
2068 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2069     
2070     getAutoCreate : function(){
2071         var cfg = {
2072             cls: 'divider',
2073             tag : 'li'
2074         };
2075         
2076         return cfg;
2077     }
2078    
2079 });
2080
2081  
2082
2083  
2084 /*
2085 * Licence: LGPL
2086 */
2087
2088 /**
2089  * @class Roo.bootstrap.Modal
2090  * @extends Roo.bootstrap.Component
2091  * Bootstrap Modal class
2092  * @cfg {String} title Title of dialog
2093  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2094  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2095  * @cfg {Boolean} specificTitle default false
2096  * @cfg {Array} buttons Array of buttons or standard button set..
2097  * @cfg {String} buttonPosition (left|right|center) default right
2098  * @cfg {Boolean} animate default true
2099  * @cfg {Boolean} allow_close default true
2100  * 
2101  * @constructor
2102  * Create a new Modal Dialog
2103  * @param {Object} config The config object
2104  */
2105
2106 Roo.bootstrap.Modal = function(config){
2107     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2108     this.addEvents({
2109         // raw events
2110         /**
2111          * @event btnclick
2112          * The raw btnclick event for the button
2113          * @param {Roo.EventObject} e
2114          */
2115         "btnclick" : true
2116     });
2117     this.buttons = this.buttons || [];
2118      
2119     if (this.tmpl) {
2120         this.tmpl = Roo.factory(this.tmpl);
2121     }
2122     
2123 };
2124
2125 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2126     
2127     title : 'test dialog',
2128    
2129     buttons : false,
2130     
2131     // set on load...
2132      
2133     html: false,
2134     
2135     tmp: false,
2136     
2137     specificTitle: false,
2138     
2139     buttonPosition: 'right',
2140     
2141     allow_close : true,
2142     
2143     animate : true,
2144     
2145     
2146      // private
2147     bodyEl:  false,
2148     footerEl:  false,
2149     titleEl:  false,
2150     closeEl:  false,
2151     
2152     
2153     onRender : function(ct, position)
2154     {
2155         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2156      
2157         if(!this.el){
2158             var cfg = Roo.apply({},  this.getAutoCreate());
2159             cfg.id = Roo.id();
2160             //if(!cfg.name){
2161             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2162             //}
2163             //if (!cfg.name.length) {
2164             //    delete cfg.name;
2165            // }
2166             if (this.cls) {
2167                 cfg.cls += ' ' + this.cls;
2168             }
2169             if (this.style) {
2170                 cfg.style = this.style;
2171             }
2172             this.el = Roo.get(document.body).createChild(cfg, position);
2173         }
2174         //var type = this.el.dom.type;
2175         
2176         
2177         
2178         
2179         if(this.tabIndex !== undefined){
2180             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2181         }
2182         
2183         
2184         this.bodyEl = this.el.select('.modal-body',true).first();
2185         this.closeEl = this.el.select('.modal-header .close', true).first();
2186         this.footerEl = this.el.select('.modal-footer',true).first();
2187         this.titleEl = this.el.select('.modal-title',true).first();
2188         
2189         
2190          
2191         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2192         this.maskEl.enableDisplayMode("block");
2193         this.maskEl.hide();
2194         //this.el.addClass("x-dlg-modal");
2195     
2196         if (this.buttons.length) {
2197             Roo.each(this.buttons, function(bb) {
2198                 b = Roo.apply({}, bb);
2199                 b.xns = b.xns || Roo.bootstrap;
2200                 b.xtype = b.xtype || 'Button';
2201                 if (typeof(b.listeners) == 'undefined') {
2202                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2203                 }
2204                 
2205                 var btn = Roo.factory(b);
2206                 
2207                 btn.onRender(this.el.select('.modal-footer div').first());
2208                 
2209             },this);
2210         }
2211         // render the children.
2212         var nitems = [];
2213         
2214         if(typeof(this.items) != 'undefined'){
2215             var items = this.items;
2216             delete this.items;
2217
2218             for(var i =0;i < items.length;i++) {
2219                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2220             }
2221         }
2222         
2223         this.items = nitems;
2224         
2225         // where are these used - they used to be body/close/footer
2226         
2227        
2228         this.initEvents();
2229         //this.el.addClass([this.fieldClass, this.cls]);
2230         
2231     },
2232     getAutoCreate : function(){
2233         
2234         
2235         var bdy = {
2236                 cls : 'modal-body',
2237                 html : this.html || ''
2238         };
2239         
2240         var title = {
2241             tag: 'h4',
2242             cls : 'modal-title',
2243             html : this.title
2244         };
2245         
2246         if(this.specificTitle){
2247             title = this.title;
2248             
2249         };
2250         
2251         var header = [];
2252         if (this.allow_close) {
2253             header.push({
2254                 tag: 'button',
2255                 cls : 'close',
2256                 html : '&times'
2257             });
2258         }
2259         header.push(title);
2260         
2261         var modal = {
2262             cls: "modal",
2263             style : 'display: none',
2264             cn : [
2265                 {
2266                     cls: "modal-dialog",
2267                     cn : [
2268                         {
2269                             cls : "modal-content",
2270                             cn : [
2271                                 {
2272                                     cls : 'modal-header',
2273                                     cn : header
2274                                 },
2275                                 bdy,
2276                                 {
2277                                     cls : 'modal-footer',
2278                                     cn : [
2279                                         {
2280                                             tag: 'div',
2281                                             cls: 'btn-' + this.buttonPosition
2282                                         }
2283                                     ]
2284                                     
2285                                 }
2286                                 
2287                                 
2288                             ]
2289                             
2290                         }
2291                     ]
2292                         
2293                 }
2294             ]
2295         };
2296         
2297         if(this.animate){
2298             modal.cls += ' fade';
2299         }
2300         
2301         return modal;
2302           
2303     },
2304     getChildContainer : function() {
2305          
2306          return this.bodyEl;
2307         
2308     },
2309     getButtonContainer : function() {
2310          return this.el.select('.modal-footer div',true).first();
2311         
2312     },
2313     initEvents : function()
2314     {
2315         if (this.allow_close) {
2316             this.closeEl.on('click', this.hide, this);
2317         }
2318
2319     },
2320     show : function() {
2321         
2322         if (!this.rendered) {
2323             this.render();
2324         }
2325         
2326         this.el.setStyle('display', 'block');
2327         
2328         if(this.animate){
2329             var _this = this;
2330             (function(){ _this.el.addClass('in'); }).defer(50);
2331         }else{
2332             this.el.addClass('in');
2333         }
2334         
2335         // not sure how we can show data in here.. 
2336         //if (this.tmpl) {
2337         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2338         //}
2339         
2340         Roo.get(document.body).addClass("x-body-masked");
2341         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2342         this.maskEl.show();
2343         this.el.setStyle('zIndex', '10001');
2344        
2345         this.fireEvent('show', this);
2346         
2347         
2348     },
2349     hide : function()
2350     {
2351         this.maskEl.hide();
2352         Roo.get(document.body).removeClass("x-body-masked");
2353         this.el.removeClass('in');
2354         
2355         if(this.animate){
2356             var _this = this;
2357             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2358         }else{
2359             this.el.setStyle('display', 'none');
2360         }
2361         
2362         this.fireEvent('hide', this);
2363     },
2364     
2365     addButton : function(str, cb)
2366     {
2367          
2368         
2369         var b = Roo.apply({}, { html : str } );
2370         b.xns = b.xns || Roo.bootstrap;
2371         b.xtype = b.xtype || 'Button';
2372         if (typeof(b.listeners) == 'undefined') {
2373             b.listeners = { click : cb.createDelegate(this)  };
2374         }
2375         
2376         var btn = Roo.factory(b);
2377            
2378         btn.onRender(this.el.select('.modal-footer div').first());
2379         
2380         return btn;   
2381        
2382     },
2383     
2384     setDefaultButton : function(btn)
2385     {
2386         //this.el.select('.modal-footer').()
2387     },
2388     resizeTo: function(w,h)
2389     {
2390         // skip..
2391     },
2392     setContentSize  : function(w, h)
2393     {
2394         
2395     },
2396     onButtonClick: function(btn,e)
2397     {
2398         //Roo.log([a,b,c]);
2399         this.fireEvent('btnclick', btn.name, e);
2400     },
2401      /**
2402      * Set the title of the Dialog
2403      * @param {String} str new Title
2404      */
2405     setTitle: function(str) {
2406         this.titleEl.dom.innerHTML = str;    
2407     },
2408     /**
2409      * Set the body of the Dialog
2410      * @param {String} str new Title
2411      */
2412     setBody: function(str) {
2413         this.bodyEl.dom.innerHTML = str;    
2414     },
2415     /**
2416      * Set the body of the Dialog using the template
2417      * @param {Obj} data - apply this data to the template and replace the body contents.
2418      */
2419     applyBody: function(obj)
2420     {
2421         if (!this.tmpl) {
2422             Roo.log("Error - using apply Body without a template");
2423             //code
2424         }
2425         this.tmpl.overwrite(this.bodyEl, obj);
2426     }
2427     
2428 });
2429
2430
2431 Roo.apply(Roo.bootstrap.Modal,  {
2432     /**
2433          * Button config that displays a single OK button
2434          * @type Object
2435          */
2436         OK :  [{
2437             name : 'ok',
2438             weight : 'primary',
2439             html : 'OK'
2440         }], 
2441         /**
2442          * Button config that displays Yes and No buttons
2443          * @type Object
2444          */
2445         YESNO : [
2446             {
2447                 name  : 'no',
2448                 html : 'No'
2449             },
2450             {
2451                 name  :'yes',
2452                 weight : 'primary',
2453                 html : 'Yes'
2454             }
2455         ],
2456         
2457         /**
2458          * Button config that displays OK and Cancel buttons
2459          * @type Object
2460          */
2461         OKCANCEL : [
2462             {
2463                name : 'cancel',
2464                 html : 'Cancel'
2465             },
2466             {
2467                 name : 'ok',
2468                 weight : 'primary',
2469                 html : 'OK'
2470             }
2471         ],
2472         /**
2473          * Button config that displays Yes, No and Cancel buttons
2474          * @type Object
2475          */
2476         YESNOCANCEL : [
2477             {
2478                 name : 'yes',
2479                 weight : 'primary',
2480                 html : 'Yes'
2481             },
2482             {
2483                 name : 'no',
2484                 html : 'No'
2485             },
2486             {
2487                 name : 'cancel',
2488                 html : 'Cancel'
2489             }
2490         ]
2491 });
2492  
2493  /*
2494  * - LGPL
2495  *
2496  * messagebox - can be used as a replace
2497  * 
2498  */
2499 /**
2500  * @class Roo.MessageBox
2501  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2502  * Example usage:
2503  *<pre><code>
2504 // Basic alert:
2505 Roo.Msg.alert('Status', 'Changes saved successfully.');
2506
2507 // Prompt for user data:
2508 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2509     if (btn == 'ok'){
2510         // process text value...
2511     }
2512 });
2513
2514 // Show a dialog using config options:
2515 Roo.Msg.show({
2516    title:'Save Changes?',
2517    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2518    buttons: Roo.Msg.YESNOCANCEL,
2519    fn: processResult,
2520    animEl: 'elId'
2521 });
2522 </code></pre>
2523  * @singleton
2524  */
2525 Roo.bootstrap.MessageBox = function(){
2526     var dlg, opt, mask, waitTimer;
2527     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2528     var buttons, activeTextEl, bwidth;
2529
2530     
2531     // private
2532     var handleButton = function(button){
2533         dlg.hide();
2534         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2535     };
2536
2537     // private
2538     var handleHide = function(){
2539         if(opt && opt.cls){
2540             dlg.el.removeClass(opt.cls);
2541         }
2542         //if(waitTimer){
2543         //    Roo.TaskMgr.stop(waitTimer);
2544         //    waitTimer = null;
2545         //}
2546     };
2547
2548     // private
2549     var updateButtons = function(b){
2550         var width = 0;
2551         if(!b){
2552             buttons["ok"].hide();
2553             buttons["cancel"].hide();
2554             buttons["yes"].hide();
2555             buttons["no"].hide();
2556             //dlg.footer.dom.style.display = 'none';
2557             return width;
2558         }
2559         dlg.footerEl.dom.style.display = '';
2560         for(var k in buttons){
2561             if(typeof buttons[k] != "function"){
2562                 if(b[k]){
2563                     buttons[k].show();
2564                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2565                     width += buttons[k].el.getWidth()+15;
2566                 }else{
2567                     buttons[k].hide();
2568                 }
2569             }
2570         }
2571         return width;
2572     };
2573
2574     // private
2575     var handleEsc = function(d, k, e){
2576         if(opt && opt.closable !== false){
2577             dlg.hide();
2578         }
2579         if(e){
2580             e.stopEvent();
2581         }
2582     };
2583
2584     return {
2585         /**
2586          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2587          * @return {Roo.BasicDialog} The BasicDialog element
2588          */
2589         getDialog : function(){
2590            if(!dlg){
2591                 dlg = new Roo.bootstrap.Modal( {
2592                     //draggable: true,
2593                     //resizable:false,
2594                     //constraintoviewport:false,
2595                     //fixedcenter:true,
2596                     //collapsible : false,
2597                     //shim:true,
2598                     //modal: true,
2599                   //  width:400,
2600                   //  height:100,
2601                     //buttonAlign:"center",
2602                     closeClick : function(){
2603                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2604                             handleButton("no");
2605                         }else{
2606                             handleButton("cancel");
2607                         }
2608                     }
2609                 });
2610                 dlg.render();
2611                 dlg.on("hide", handleHide);
2612                 mask = dlg.mask;
2613                 //dlg.addKeyListener(27, handleEsc);
2614                 buttons = {};
2615                 this.buttons = buttons;
2616                 var bt = this.buttonText;
2617                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2618                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2619                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2620                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2621                 Roo.log(buttons)
2622                 bodyEl = dlg.bodyEl.createChild({
2623
2624                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2625                         '<textarea class="roo-mb-textarea"></textarea>' +
2626                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2627                 });
2628                 msgEl = bodyEl.dom.firstChild;
2629                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2630                 textboxEl.enableDisplayMode();
2631                 textboxEl.addKeyListener([10,13], function(){
2632                     if(dlg.isVisible() && opt && opt.buttons){
2633                         if(opt.buttons.ok){
2634                             handleButton("ok");
2635                         }else if(opt.buttons.yes){
2636                             handleButton("yes");
2637                         }
2638                     }
2639                 });
2640                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2641                 textareaEl.enableDisplayMode();
2642                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2643                 progressEl.enableDisplayMode();
2644                 var pf = progressEl.dom.firstChild;
2645                 if (pf) {
2646                     pp = Roo.get(pf.firstChild);
2647                     pp.setHeight(pf.offsetHeight);
2648                 }
2649                 
2650             }
2651             return dlg;
2652         },
2653
2654         /**
2655          * Updates the message box body text
2656          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2657          * the XHTML-compliant non-breaking space character '&amp;#160;')
2658          * @return {Roo.MessageBox} This message box
2659          */
2660         updateText : function(text){
2661             if(!dlg.isVisible() && !opt.width){
2662                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2663             }
2664             msgEl.innerHTML = text || '&#160;';
2665       
2666             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2667             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2668             var w = Math.max(
2669                     Math.min(opt.width || cw , this.maxWidth), 
2670                     Math.max(opt.minWidth || this.minWidth, bwidth)
2671             );
2672             if(opt.prompt){
2673                 activeTextEl.setWidth(w);
2674             }
2675             if(dlg.isVisible()){
2676                 dlg.fixedcenter = false;
2677             }
2678             // to big, make it scroll. = But as usual stupid IE does not support
2679             // !important..
2680             
2681             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2682                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2683                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2684             } else {
2685                 bodyEl.dom.style.height = '';
2686                 bodyEl.dom.style.overflowY = '';
2687             }
2688             if (cw > w) {
2689                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2690             } else {
2691                 bodyEl.dom.style.overflowX = '';
2692             }
2693             
2694             dlg.setContentSize(w, bodyEl.getHeight());
2695             if(dlg.isVisible()){
2696                 dlg.fixedcenter = true;
2697             }
2698             return this;
2699         },
2700
2701         /**
2702          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2703          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2704          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2705          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2706          * @return {Roo.MessageBox} This message box
2707          */
2708         updateProgress : function(value, text){
2709             if(text){
2710                 this.updateText(text);
2711             }
2712             if (pp) { // weird bug on my firefox - for some reason this is not defined
2713                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2714             }
2715             return this;
2716         },        
2717
2718         /**
2719          * Returns true if the message box is currently displayed
2720          * @return {Boolean} True if the message box is visible, else false
2721          */
2722         isVisible : function(){
2723             return dlg && dlg.isVisible();  
2724         },
2725
2726         /**
2727          * Hides the message box if it is displayed
2728          */
2729         hide : function(){
2730             if(this.isVisible()){
2731                 dlg.hide();
2732             }  
2733         },
2734
2735         /**
2736          * Displays a new message box, or reinitializes an existing message box, based on the config options
2737          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2738          * The following config object properties are supported:
2739          * <pre>
2740 Property    Type             Description
2741 ----------  ---------------  ------------------------------------------------------------------------------------
2742 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2743                                    closes (defaults to undefined)
2744 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2745                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2746 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2747                                    progress and wait dialogs will ignore this property and always hide the
2748                                    close button as they can only be closed programmatically.
2749 cls               String           A custom CSS class to apply to the message box element
2750 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2751                                    displayed (defaults to 75)
2752 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2753                                    function will be btn (the name of the button that was clicked, if applicable,
2754                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2755                                    Progress and wait dialogs will ignore this option since they do not respond to
2756                                    user actions and can only be closed programmatically, so any required function
2757                                    should be called by the same code after it closes the dialog.
2758 icon              String           A CSS class that provides a background image to be used as an icon for
2759                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2760 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2761 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2762 modal             Boolean          False to allow user interaction with the page while the message box is
2763                                    displayed (defaults to true)
2764 msg               String           A string that will replace the existing message box body text (defaults
2765                                    to the XHTML-compliant non-breaking space character '&#160;')
2766 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2767 progress          Boolean          True to display a progress bar (defaults to false)
2768 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2769 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2770 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2771 title             String           The title text
2772 value             String           The string value to set into the active textbox element if displayed
2773 wait              Boolean          True to display a progress bar (defaults to false)
2774 width             Number           The width of the dialog in pixels
2775 </pre>
2776          *
2777          * Example usage:
2778          * <pre><code>
2779 Roo.Msg.show({
2780    title: 'Address',
2781    msg: 'Please enter your address:',
2782    width: 300,
2783    buttons: Roo.MessageBox.OKCANCEL,
2784    multiline: true,
2785    fn: saveAddress,
2786    animEl: 'addAddressBtn'
2787 });
2788 </code></pre>
2789          * @param {Object} config Configuration options
2790          * @return {Roo.MessageBox} This message box
2791          */
2792         show : function(options)
2793         {
2794             
2795             // this causes nightmares if you show one dialog after another
2796             // especially on callbacks..
2797              
2798             if(this.isVisible()){
2799                 
2800                 this.hide();
2801                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2802                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2803                 Roo.log("New Dialog Message:" +  options.msg )
2804                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2805                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2806                 
2807             }
2808             var d = this.getDialog();
2809             opt = options;
2810             d.setTitle(opt.title || "&#160;");
2811             d.closeEl.setDisplayed(opt.closable !== false);
2812             activeTextEl = textboxEl;
2813             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2814             if(opt.prompt){
2815                 if(opt.multiline){
2816                     textboxEl.hide();
2817                     textareaEl.show();
2818                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2819                         opt.multiline : this.defaultTextHeight);
2820                     activeTextEl = textareaEl;
2821                 }else{
2822                     textboxEl.show();
2823                     textareaEl.hide();
2824                 }
2825             }else{
2826                 textboxEl.hide();
2827                 textareaEl.hide();
2828             }
2829             progressEl.setDisplayed(opt.progress === true);
2830             this.updateProgress(0);
2831             activeTextEl.dom.value = opt.value || "";
2832             if(opt.prompt){
2833                 dlg.setDefaultButton(activeTextEl);
2834             }else{
2835                 var bs = opt.buttons;
2836                 var db = null;
2837                 if(bs && bs.ok){
2838                     db = buttons["ok"];
2839                 }else if(bs && bs.yes){
2840                     db = buttons["yes"];
2841                 }
2842                 dlg.setDefaultButton(db);
2843             }
2844             bwidth = updateButtons(opt.buttons);
2845             this.updateText(opt.msg);
2846             if(opt.cls){
2847                 d.el.addClass(opt.cls);
2848             }
2849             d.proxyDrag = opt.proxyDrag === true;
2850             d.modal = opt.modal !== false;
2851             d.mask = opt.modal !== false ? mask : false;
2852             if(!d.isVisible()){
2853                 // force it to the end of the z-index stack so it gets a cursor in FF
2854                 document.body.appendChild(dlg.el.dom);
2855                 d.animateTarget = null;
2856                 d.show(options.animEl);
2857             }
2858             return this;
2859         },
2860
2861         /**
2862          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2863          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2864          * and closing the message box when the process is complete.
2865          * @param {String} title The title bar text
2866          * @param {String} msg The message box body text
2867          * @return {Roo.MessageBox} This message box
2868          */
2869         progress : function(title, msg){
2870             this.show({
2871                 title : title,
2872                 msg : msg,
2873                 buttons: false,
2874                 progress:true,
2875                 closable:false,
2876                 minWidth: this.minProgressWidth,
2877                 modal : true
2878             });
2879             return this;
2880         },
2881
2882         /**
2883          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2884          * If a callback function is passed it will be called after the user clicks the button, and the
2885          * id of the button that was clicked will be passed as the only parameter to the callback
2886          * (could also be the top-right close button).
2887          * @param {String} title The title bar text
2888          * @param {String} msg The message box body text
2889          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2890          * @param {Object} scope (optional) The scope of the callback function
2891          * @return {Roo.MessageBox} This message box
2892          */
2893         alert : function(title, msg, fn, scope){
2894             this.show({
2895                 title : title,
2896                 msg : msg,
2897                 buttons: this.OK,
2898                 fn: fn,
2899                 scope : scope,
2900                 modal : true
2901             });
2902             return this;
2903         },
2904
2905         /**
2906          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2907          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2908          * You are responsible for closing the message box when the process is complete.
2909          * @param {String} msg The message box body text
2910          * @param {String} title (optional) The title bar text
2911          * @return {Roo.MessageBox} This message box
2912          */
2913         wait : function(msg, title){
2914             this.show({
2915                 title : title,
2916                 msg : msg,
2917                 buttons: false,
2918                 closable:false,
2919                 progress:true,
2920                 modal:true,
2921                 width:300,
2922                 wait:true
2923             });
2924             waitTimer = Roo.TaskMgr.start({
2925                 run: function(i){
2926                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2927                 },
2928                 interval: 1000
2929             });
2930             return this;
2931         },
2932
2933         /**
2934          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2935          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2936          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2937          * @param {String} title The title bar text
2938          * @param {String} msg The message box body text
2939          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2940          * @param {Object} scope (optional) The scope of the callback function
2941          * @return {Roo.MessageBox} This message box
2942          */
2943         confirm : function(title, msg, fn, scope){
2944             this.show({
2945                 title : title,
2946                 msg : msg,
2947                 buttons: this.YESNO,
2948                 fn: fn,
2949                 scope : scope,
2950                 modal : true
2951             });
2952             return this;
2953         },
2954
2955         /**
2956          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2957          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2958          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2959          * (could also be the top-right close button) and the text that was entered will be passed as the two
2960          * parameters to the callback.
2961          * @param {String} title The title bar text
2962          * @param {String} msg The message box body text
2963          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2964          * @param {Object} scope (optional) The scope of the callback function
2965          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2966          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2967          * @return {Roo.MessageBox} This message box
2968          */
2969         prompt : function(title, msg, fn, scope, multiline){
2970             this.show({
2971                 title : title,
2972                 msg : msg,
2973                 buttons: this.OKCANCEL,
2974                 fn: fn,
2975                 minWidth:250,
2976                 scope : scope,
2977                 prompt:true,
2978                 multiline: multiline,
2979                 modal : true
2980             });
2981             return this;
2982         },
2983
2984         /**
2985          * Button config that displays a single OK button
2986          * @type Object
2987          */
2988         OK : {ok:true},
2989         /**
2990          * Button config that displays Yes and No buttons
2991          * @type Object
2992          */
2993         YESNO : {yes:true, no:true},
2994         /**
2995          * Button config that displays OK and Cancel buttons
2996          * @type Object
2997          */
2998         OKCANCEL : {ok:true, cancel:true},
2999         /**
3000          * Button config that displays Yes, No and Cancel buttons
3001          * @type Object
3002          */
3003         YESNOCANCEL : {yes:true, no:true, cancel:true},
3004
3005         /**
3006          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3007          * @type Number
3008          */
3009         defaultTextHeight : 75,
3010         /**
3011          * The maximum width in pixels of the message box (defaults to 600)
3012          * @type Number
3013          */
3014         maxWidth : 600,
3015         /**
3016          * The minimum width in pixels of the message box (defaults to 100)
3017          * @type Number
3018          */
3019         minWidth : 100,
3020         /**
3021          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3022          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3023          * @type Number
3024          */
3025         minProgressWidth : 250,
3026         /**
3027          * An object containing the default button text strings that can be overriden for localized language support.
3028          * Supported properties are: ok, cancel, yes and no.
3029          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3030          * @type Object
3031          */
3032         buttonText : {
3033             ok : "OK",
3034             cancel : "Cancel",
3035             yes : "Yes",
3036             no : "No"
3037         }
3038     };
3039 }();
3040
3041 /**
3042  * Shorthand for {@link Roo.MessageBox}
3043  */
3044 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3045 Roo.Msg = Roo.Msg || Roo.MessageBox;
3046 /*
3047  * - LGPL
3048  *
3049  * navbar
3050  * 
3051  */
3052
3053 /**
3054  * @class Roo.bootstrap.Navbar
3055  * @extends Roo.bootstrap.Component
3056  * Bootstrap Navbar class
3057
3058  * @constructor
3059  * Create a new Navbar
3060  * @param {Object} config The config object
3061  */
3062
3063
3064 Roo.bootstrap.Navbar = function(config){
3065     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3066     
3067 };
3068
3069 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3070     
3071     
3072    
3073     // private
3074     navItems : false,
3075     loadMask : false,
3076     
3077     
3078     getAutoCreate : function(){
3079         
3080         
3081         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3082         
3083     },
3084     
3085     initEvents :function ()
3086     {
3087         //Roo.log(this.el.select('.navbar-toggle',true));
3088         this.el.select('.navbar-toggle',true).on('click', function() {
3089            // Roo.log('click');
3090             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3091         }, this);
3092         
3093         var mark = {
3094             tag: "div",
3095             cls:"x-dlg-mask"
3096         }
3097         
3098         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3099         
3100         var size = this.el.getSize();
3101         this.maskEl.setSize(size.width, size.height);
3102         this.maskEl.enableDisplayMode("block");
3103         this.maskEl.hide();
3104         
3105         if(this.loadMask){
3106             this.maskEl.show();
3107         }
3108     },
3109     
3110     
3111     getChildContainer : function()
3112     {
3113         if (this.el.select('.collapse').getCount()) {
3114             return this.el.select('.collapse',true).first();
3115         }
3116         
3117         return this.el;
3118     },
3119     
3120     mask : function()
3121     {
3122         this.maskEl.show();
3123     },
3124     
3125     unmask : function()
3126     {
3127         this.maskEl.hide();
3128     } 
3129     
3130     
3131     
3132     
3133 });
3134
3135
3136
3137  
3138
3139  /*
3140  * - LGPL
3141  *
3142  * navbar
3143  * 
3144  */
3145
3146 /**
3147  * @class Roo.bootstrap.NavSimplebar
3148  * @extends Roo.bootstrap.Navbar
3149  * Bootstrap Sidebar class
3150  *
3151  * @cfg {Boolean} inverse is inverted color
3152  * 
3153  * @cfg {String} type (nav | pills | tabs)
3154  * @cfg {Boolean} arrangement stacked | justified
3155  * @cfg {String} align (left | right) alignment
3156  * 
3157  * @cfg {Boolean} main (true|false) main nav bar? default false
3158  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3159  * 
3160  * @cfg {String} tag (header|footer|nav|div) default is nav 
3161
3162  * 
3163  * 
3164  * 
3165  * @constructor
3166  * Create a new Sidebar
3167  * @param {Object} config The config object
3168  */
3169
3170
3171 Roo.bootstrap.NavSimplebar = function(config){
3172     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3173 };
3174
3175 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3176     
3177     inverse: false,
3178     
3179     type: false,
3180     arrangement: '',
3181     align : false,
3182     
3183     
3184     
3185     main : false,
3186     
3187     
3188     tag : false,
3189     
3190     
3191     getAutoCreate : function(){
3192         
3193         
3194         var cfg = {
3195             tag : this.tag || 'div',
3196             cls : 'navbar'
3197         };
3198           
3199         
3200         cfg.cn = [
3201             {
3202                 cls: 'nav',
3203                 tag : 'ul'
3204             }
3205         ];
3206         
3207          
3208         this.type = this.type || 'nav';
3209         if (['tabs','pills'].indexOf(this.type)!==-1) {
3210             cfg.cn[0].cls += ' nav-' + this.type
3211         
3212         
3213         } else {
3214             if (this.type!=='nav') {
3215                 Roo.log('nav type must be nav/tabs/pills')
3216             }
3217             cfg.cn[0].cls += ' navbar-nav'
3218         }
3219         
3220         
3221         
3222         
3223         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3224             cfg.cn[0].cls += ' nav-' + this.arrangement;
3225         }
3226         
3227         
3228         if (this.align === 'right') {
3229             cfg.cn[0].cls += ' navbar-right';
3230         }
3231         
3232         if (this.inverse) {
3233             cfg.cls += ' navbar-inverse';
3234             
3235         }
3236         
3237         
3238         return cfg;
3239     
3240         
3241     }
3242     
3243     
3244     
3245 });
3246
3247
3248
3249  
3250
3251  
3252        /*
3253  * - LGPL
3254  *
3255  * navbar
3256  * 
3257  */
3258
3259 /**
3260  * @class Roo.bootstrap.NavHeaderbar
3261  * @extends Roo.bootstrap.NavSimplebar
3262  * Bootstrap Sidebar class
3263  *
3264  * @cfg {String} brand what is brand
3265  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3266  * @cfg {String} brand_href href of the brand
3267  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3268  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3269  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3270  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3271  * 
3272  * @constructor
3273  * Create a new Sidebar
3274  * @param {Object} config The config object
3275  */
3276
3277
3278 Roo.bootstrap.NavHeaderbar = function(config){
3279     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3280       
3281 };
3282
3283 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3284     
3285     position: '',
3286     brand: '',
3287     brand_href: false,
3288     srButton : true,
3289     autohide : false,
3290     desktopCenter : false,
3291    
3292     
3293     getAutoCreate : function(){
3294         
3295         var   cfg = {
3296             tag: this.nav || 'nav',
3297             cls: 'navbar',
3298             role: 'navigation',
3299             cn: []
3300         };
3301         
3302         var cn = cfg.cn;
3303         if (this.desktopCenter) {
3304             cn.push({cls : 'container', cn : []});
3305             cn = cn[0].cn;
3306         }
3307         
3308         if(this.srButton){
3309             cn.push({
3310                 tag: 'div',
3311                 cls: 'navbar-header',
3312                 cn: [
3313                     {
3314                         tag: 'button',
3315                         type: 'button',
3316                         cls: 'navbar-toggle',
3317                         'data-toggle': 'collapse',
3318                         cn: [
3319                             {
3320                                 tag: 'span',
3321                                 cls: 'sr-only',
3322                                 html: 'Toggle navigation'
3323                             },
3324                             {
3325                                 tag: 'span',
3326                                 cls: 'icon-bar'
3327                             },
3328                             {
3329                                 tag: 'span',
3330                                 cls: 'icon-bar'
3331                             },
3332                             {
3333                                 tag: 'span',
3334                                 cls: 'icon-bar'
3335                             }
3336                         ]
3337                     }
3338                 ]
3339             });
3340         }
3341         
3342         cn.push({
3343             tag: 'div',
3344             cls: 'collapse navbar-collapse',
3345             cn : []
3346         });
3347         
3348         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3349         
3350         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3351             cfg.cls += ' navbar-' + this.position;
3352             
3353             // tag can override this..
3354             
3355             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3356         }
3357         
3358         if (this.brand !== '') {
3359             cn[0].cn.push({
3360                 tag: 'a',
3361                 href: this.brand_href ? this.brand_href : '#',
3362                 cls: 'navbar-brand',
3363                 cn: [
3364                 this.brand
3365                 ]
3366             });
3367         }
3368         
3369         if(this.main){
3370             cfg.cls += ' main-nav';
3371         }
3372         
3373         
3374         return cfg;
3375
3376         
3377     },
3378     getHeaderChildContainer : function()
3379     {
3380         if (this.el.select('.navbar-header').getCount()) {
3381             return this.el.select('.navbar-header',true).first();
3382         }
3383         
3384         return this.getChildContainer();
3385     },
3386     
3387     
3388     initEvents : function()
3389     {
3390         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3391         
3392         if (this.autohide) {
3393             
3394             var prevScroll = 0;
3395             var ft = this.el;
3396             
3397             Roo.get(document).on('scroll',function(e) {
3398                 var ns = Roo.get(document).getScroll().top;
3399                 var os = prevScroll;
3400                 prevScroll = ns;
3401                 
3402                 if(ns > os){
3403                     ft.removeClass('slideDown');
3404                     ft.addClass('slideUp');
3405                     return;
3406                 }
3407                 ft.removeClass('slideUp');
3408                 ft.addClass('slideDown');
3409                  
3410               
3411           },this);
3412         }
3413     }    
3414           
3415       
3416     
3417     
3418 });
3419
3420
3421
3422  
3423
3424  /*
3425  * - LGPL
3426  *
3427  * navbar
3428  * 
3429  */
3430
3431 /**
3432  * @class Roo.bootstrap.NavSidebar
3433  * @extends Roo.bootstrap.Navbar
3434  * Bootstrap Sidebar class
3435  * 
3436  * @constructor
3437  * Create a new Sidebar
3438  * @param {Object} config The config object
3439  */
3440
3441
3442 Roo.bootstrap.NavSidebar = function(config){
3443     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3444 };
3445
3446 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3447     
3448     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3449     
3450     getAutoCreate : function(){
3451         
3452         
3453         return  {
3454             tag: 'div',
3455             cls: 'sidebar sidebar-nav'
3456         };
3457     
3458         
3459     }
3460     
3461     
3462     
3463 });
3464
3465
3466
3467  
3468
3469  /*
3470  * - LGPL
3471  *
3472  * nav group
3473  * 
3474  */
3475
3476 /**
3477  * @class Roo.bootstrap.NavGroup
3478  * @extends Roo.bootstrap.Component
3479  * Bootstrap NavGroup class
3480  * @cfg {String} align left | right
3481  * @cfg {Boolean} inverse false | true
3482  * @cfg {String} type (nav|pills|tab) default nav
3483  * @cfg {String} navId - reference Id for navbar.
3484
3485  * 
3486  * @constructor
3487  * Create a new nav group
3488  * @param {Object} config The config object
3489  */
3490
3491 Roo.bootstrap.NavGroup = function(config){
3492     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3493     this.navItems = [];
3494    
3495     Roo.bootstrap.NavGroup.register(this);
3496      this.addEvents({
3497         /**
3498              * @event changed
3499              * Fires when the active item changes
3500              * @param {Roo.bootstrap.NavGroup} this
3501              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3502              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3503          */
3504         'changed': true
3505      });
3506     
3507 };
3508
3509 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3510     
3511     align: '',
3512     inverse: false,
3513     form: false,
3514     type: 'nav',
3515     navId : '',
3516     // private
3517     
3518     navItems : false, 
3519     
3520     getAutoCreate : function()
3521     {
3522         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3523         
3524         cfg = {
3525             tag : 'ul',
3526             cls: 'nav' 
3527         }
3528         
3529         if (['tabs','pills'].indexOf(this.type)!==-1) {
3530             cfg.cls += ' nav-' + this.type
3531         } else {
3532             if (this.type!=='nav') {
3533                 Roo.log('nav type must be nav/tabs/pills')
3534             }
3535             cfg.cls += ' navbar-nav'
3536         }
3537         
3538         if (this.parent().sidebar) {
3539             cfg = {
3540                 tag: 'ul',
3541                 cls: 'dashboard-menu sidebar-menu'
3542             }
3543             
3544             return cfg;
3545         }
3546         
3547         if (this.form === true) {
3548             cfg = {
3549                 tag: 'form',
3550                 cls: 'navbar-form'
3551             }
3552             
3553             if (this.align === 'right') {
3554                 cfg.cls += ' navbar-right';
3555             } else {
3556                 cfg.cls += ' navbar-left';
3557             }
3558         }
3559         
3560         if (this.align === 'right') {
3561             cfg.cls += ' navbar-right';
3562         }
3563         
3564         if (this.inverse) {
3565             cfg.cls += ' navbar-inverse';
3566             
3567         }
3568         
3569         
3570         return cfg;
3571     },
3572     /**
3573     * sets the active Navigation item
3574     * @param {Roo.bootstrap.NavItem} the new current navitem
3575     */
3576     setActiveItem : function(item)
3577     {
3578         var prev = false;
3579         Roo.each(this.navItems, function(v){
3580             if (v == item) {
3581                 return ;
3582             }
3583             if (v.isActive()) {
3584                 v.setActive(false, true);
3585                 prev = v;
3586                 
3587             }
3588             
3589         });
3590
3591         item.setActive(true, true);
3592         this.fireEvent('changed', this, item, prev);
3593         
3594         
3595     },
3596     /**
3597     * gets the active Navigation item
3598     * @return {Roo.bootstrap.NavItem} the current navitem
3599     */
3600     getActive : function()
3601     {
3602         
3603         var prev = false;
3604         Roo.each(this.navItems, function(v){
3605             
3606             if (v.isActive()) {
3607                 prev = v;
3608                 
3609             }
3610             
3611         });
3612         return prev;
3613     },
3614     
3615     indexOfNav : function()
3616     {
3617         
3618         var prev = false;
3619         Roo.each(this.navItems, function(v,i){
3620             
3621             if (v.isActive()) {
3622                 prev = i;
3623                 
3624             }
3625             
3626         });
3627         return prev;
3628     },
3629     /**
3630     * adds a Navigation item
3631     * @param {Roo.bootstrap.NavItem} the navitem to add
3632     */
3633     addItem : function(cfg)
3634     {
3635         var cn = new Roo.bootstrap.NavItem(cfg);
3636         this.register(cn);
3637         cn.parentId = this.id;
3638         cn.onRender(this.el, null);
3639         return cn;
3640     },
3641     /**
3642     * register a Navigation item
3643     * @param {Roo.bootstrap.NavItem} the navitem to add
3644     */
3645     register : function(item)
3646     {
3647         this.navItems.push( item);
3648         item.navId = this.navId;
3649     
3650     },
3651     
3652     /**
3653     * clear all the Navigation item
3654     */
3655    
3656     clearAll : function()
3657     {
3658         this.navItems = [];
3659         this.el.dom.innerHTML = '';
3660     },
3661     
3662     getNavItem: function(tabId)
3663     {
3664         var ret = false;
3665         Roo.each(this.navItems, function(e) {
3666             if (e.tabId == tabId) {
3667                ret =  e;
3668                return false;
3669             }
3670             return true;
3671             
3672         });
3673         return ret;
3674     },
3675     
3676     setActiveNext : function()
3677     {
3678         var i = this.indexOfNav(this.getActive());
3679         if (i > this.navItems.length) {
3680             return;
3681         }
3682         this.setActiveItem(this.navItems[i+1]);
3683     },
3684     setActivePrev : function()
3685     {
3686         var i = this.indexOfNav(this.getActive());
3687         if (i  < 1) {
3688             return;
3689         }
3690         this.setActiveItem(this.navItems[i-1]);
3691     },
3692     clearWasActive : function(except) {
3693         Roo.each(this.navItems, function(e) {
3694             if (e.tabId != except.tabId && e.was_active) {
3695                e.was_active = false;
3696                return false;
3697             }
3698             return true;
3699             
3700         });
3701     },
3702     getWasActive : function ()
3703     {
3704         var r = false;
3705         Roo.each(this.navItems, function(e) {
3706             if (e.was_active) {
3707                r = e;
3708                return false;
3709             }
3710             return true;
3711             
3712         });
3713         return r;
3714     }
3715     
3716     
3717 });
3718
3719  
3720 Roo.apply(Roo.bootstrap.NavGroup, {
3721     
3722     groups: {},
3723      /**
3724     * register a Navigation Group
3725     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3726     */
3727     register : function(navgrp)
3728     {
3729         this.groups[navgrp.navId] = navgrp;
3730         
3731     },
3732     /**
3733     * fetch a Navigation Group based on the navigation ID
3734     * @param {string} the navgroup to add
3735     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3736     */
3737     get: function(navId) {
3738         if (typeof(this.groups[navId]) == 'undefined') {
3739             return false;
3740             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3741         }
3742         return this.groups[navId] ;
3743     }
3744     
3745     
3746     
3747 });
3748
3749  /*
3750  * - LGPL
3751  *
3752  * row
3753  * 
3754  */
3755
3756 /**
3757  * @class Roo.bootstrap.NavItem
3758  * @extends Roo.bootstrap.Component
3759  * Bootstrap Navbar.NavItem class
3760  * @cfg {String} href  link to
3761  * @cfg {String} html content of button
3762  * @cfg {String} badge text inside badge
3763  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3764  * @cfg {String} glyphicon name of glyphicon
3765  * @cfg {String} icon name of font awesome icon
3766  * @cfg {Boolean} active Is item active
3767  * @cfg {Boolean} disabled Is item disabled
3768  
3769  * @cfg {Boolean} preventDefault (true | false) default false
3770  * @cfg {String} tabId the tab that this item activates.
3771  * @cfg {String} tagtype (a|span) render as a href or span?
3772  * @cfg {Boolean} animateRef (true|false) link to element default false  
3773   
3774  * @constructor
3775  * Create a new Navbar Item
3776  * @param {Object} config The config object
3777  */
3778 Roo.bootstrap.NavItem = function(config){
3779     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3780     this.addEvents({
3781         // raw events
3782         /**
3783          * @event click
3784          * The raw click event for the entire grid.
3785          * @param {Roo.EventObject} e
3786          */
3787         "click" : true,
3788          /**
3789             * @event changed
3790             * Fires when the active item active state changes
3791             * @param {Roo.bootstrap.NavItem} this
3792             * @param {boolean} state the new state
3793              
3794          */
3795         'changed': true,
3796         /**
3797             * @event scrollto
3798             * Fires when scroll to element
3799             * @param {Roo.bootstrap.NavItem} this
3800             * @param {Object} options
3801             * @param {Roo.EventObject} e
3802              
3803          */
3804         'scrollto': true
3805     });
3806    
3807 };
3808
3809 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3810     
3811     href: false,
3812     html: '',
3813     badge: '',
3814     icon: false,
3815     glyphicon: false,
3816     active: false,
3817     preventDefault : false,
3818     tabId : false,
3819     tagtype : 'a',
3820     disabled : false,
3821     animateRef : false,
3822     was_active : false,
3823     
3824     getAutoCreate : function(){
3825          
3826         var cfg = {
3827             tag: 'li',
3828             cls: 'nav-item'
3829             
3830         }
3831         if (this.active) {
3832             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3833         }
3834         if (this.disabled) {
3835             cfg.cls += ' disabled';
3836         }
3837         
3838         if (this.href || this.html || this.glyphicon || this.icon) {
3839             cfg.cn = [
3840                 {
3841                     tag: this.tagtype,
3842                     href : this.href || "#",
3843                     html: this.html || ''
3844                 }
3845             ];
3846             
3847             if (this.icon) {
3848                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3849             }
3850
3851             if(this.glyphicon) {
3852                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3853             }
3854             
3855             if (this.menu) {
3856                 
3857                 cfg.cn[0].html += " <span class='caret'></span>";
3858              
3859             }
3860             
3861             if (this.badge !== '') {
3862                  
3863                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3864             }
3865         }
3866         
3867         
3868         
3869         return cfg;
3870     },
3871     initEvents: function() 
3872     {
3873         if (typeof (this.menu) != 'undefined') {
3874             this.menu.parentType = this.xtype;
3875             this.menu.triggerEl = this.el;
3876             this.menu = this.addxtype(Roo.apply({}, this.menu));
3877         }
3878         
3879         this.el.select('a',true).on('click', this.onClick, this);
3880         
3881         if(this.tagtype == 'span'){
3882             this.el.select('span',true).on('click', this.onClick, this);
3883         }
3884        
3885         // at this point parent should be available..
3886         this.parent().register(this);
3887     },
3888     
3889     onClick : function(e)
3890     {
3891         if(
3892                 this.preventDefault || 
3893                 this.href == '#' 
3894         ){
3895             
3896             e.preventDefault();
3897         }
3898         
3899         if (this.disabled) {
3900             return;
3901         }
3902         
3903         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3904         if (tg && tg.transition) {
3905             Roo.log("waiting for the transitionend");
3906             return;
3907         }
3908         
3909         
3910         
3911         //Roo.log("fire event clicked");
3912         if(this.fireEvent('click', this, e) === false){
3913             return;
3914         };
3915         
3916         if(this.tagtype == 'span'){
3917             return;
3918         }
3919         
3920         //Roo.log(this.href);
3921         var ael = this.el.select('a',true).first();
3922         //Roo.log(ael);
3923         
3924         if(ael && this.animateRef && this.href.indexOf('#') > -1){
3925             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
3926             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
3927                 return; // ignore... - it's a 'hash' to another page.
3928             }
3929             
3930             e.preventDefault();
3931             this.scrollToElement(e);
3932         }
3933         
3934         
3935         var p =  this.parent();
3936    
3937         if (['tabs','pills'].indexOf(p.type)!==-1) {
3938             if (typeof(p.setActiveItem) !== 'undefined') {
3939                 p.setActiveItem(this);
3940             }
3941         }
3942     },
3943     
3944     isActive: function () {
3945         return this.active
3946     },
3947     setActive : function(state, fire, is_was_active)
3948     {
3949         if (this.active && !state & this.navId) {
3950             this.was_active = true;
3951             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3952             if (nv) {
3953                 nv.clearWasActive(this);
3954             }
3955             
3956         }
3957         this.active = state;
3958         
3959         if (!state ) {
3960             this.el.removeClass('active');
3961         } else if (!this.el.hasClass('active')) {
3962             this.el.addClass('active');
3963         }
3964         if (fire) {
3965             this.fireEvent('changed', this, state);
3966         }
3967         
3968         // show a panel if it's registered and related..
3969         
3970         if (!this.navId || !this.tabId || !state || is_was_active) {
3971             return;
3972         }
3973         
3974         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3975         if (!tg) {
3976             return;
3977         }
3978         var pan = tg.getPanelByName(this.tabId);
3979         if (!pan) {
3980             return;
3981         }
3982         // if we can not flip to new panel - go back to old nav highlight..
3983         if (false == tg.showPanel(pan)) {
3984             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3985             if (nv) {
3986                 var onav = nv.getWasActive();
3987                 if (onav) {
3988                     onav.setActive(true, false, true);
3989                 }
3990             }
3991             
3992         }
3993         
3994         
3995         
3996     },
3997      // this should not be here...
3998     setDisabled : function(state)
3999     {
4000         this.disabled = state;
4001         if (!state ) {
4002             this.el.removeClass('disabled');
4003         } else if (!this.el.hasClass('disabled')) {
4004             this.el.addClass('disabled');
4005         }
4006         
4007     },
4008     
4009     /**
4010      * Fetch the element to display the tooltip on.
4011      * @return {Roo.Element} defaults to this.el
4012      */
4013     tooltipEl : function()
4014     {
4015         return this.el.select('' + this.tagtype + '', true).first();
4016     },
4017     
4018     scrollToElement : function(e)
4019     {
4020         var c = document.body;
4021         
4022         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4023         
4024         if(!target){
4025             return;
4026         }
4027
4028         var o = target.calcOffsetsTo(c);
4029         
4030         var options = {
4031             target : target,
4032             value : o[1]
4033         }
4034         
4035         this.fireEvent('scrollto', this, options, e);
4036         
4037         Roo.get(c).scrollTo('top', options.value, true);
4038         
4039         return;
4040     }
4041 });
4042  
4043
4044  /*
4045  * - LGPL
4046  *
4047  * sidebar item
4048  *
4049  *  li
4050  *    <span> icon </span>
4051  *    <span> text </span>
4052  *    <span>badge </span>
4053  */
4054
4055 /**
4056  * @class Roo.bootstrap.NavSidebarItem
4057  * @extends Roo.bootstrap.NavItem
4058  * Bootstrap Navbar.NavSidebarItem class
4059  * @constructor
4060  * Create a new Navbar Button
4061  * @param {Object} config The config object
4062  */
4063 Roo.bootstrap.NavSidebarItem = function(config){
4064     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4065     this.addEvents({
4066         // raw events
4067         /**
4068          * @event click
4069          * The raw click event for the entire grid.
4070          * @param {Roo.EventObject} e
4071          */
4072         "click" : true,
4073          /**
4074             * @event changed
4075             * Fires when the active item active state changes
4076             * @param {Roo.bootstrap.NavSidebarItem} this
4077             * @param {boolean} state the new state
4078              
4079          */
4080         'changed': true
4081     });
4082    
4083 };
4084
4085 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4086     
4087     
4088     getAutoCreate : function(){
4089         
4090         
4091         var a = {
4092                 tag: 'a',
4093                 href : this.href || '#',
4094                 cls: '',
4095                 html : '',
4096                 cn : []
4097         };
4098         var cfg = {
4099             tag: 'li',
4100             cls: '',
4101             cn: [ a ]
4102         }
4103         var span = {
4104             tag: 'span',
4105             html : this.html || ''
4106         }
4107         
4108         
4109         if (this.active) {
4110             cfg.cls += ' active';
4111         }
4112         
4113         // left icon..
4114         if (this.glyphicon || this.icon) {
4115             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4116             a.cn.push({ tag : 'i', cls : c }) ;
4117         }
4118         // html..
4119         a.cn.push(span);
4120         // then badge..
4121         if (this.badge !== '') {
4122             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4123         }
4124         // fi
4125         if (this.menu) {
4126             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4127             a.cls += 'dropdown-toggle treeview' ;
4128             
4129         }
4130         
4131         
4132         
4133         return cfg;
4134          
4135            
4136     }
4137    
4138      
4139  
4140 });
4141  
4142
4143  /*
4144  * - LGPL
4145  *
4146  * row
4147  * 
4148  */
4149
4150 /**
4151  * @class Roo.bootstrap.Row
4152  * @extends Roo.bootstrap.Component
4153  * Bootstrap Row class (contains columns...)
4154  * 
4155  * @constructor
4156  * Create a new Row
4157  * @param {Object} config The config object
4158  */
4159
4160 Roo.bootstrap.Row = function(config){
4161     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4162 };
4163
4164 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4165     
4166     getAutoCreate : function(){
4167        return {
4168             cls: 'row clearfix'
4169        };
4170     }
4171     
4172     
4173 });
4174
4175  
4176
4177  /*
4178  * - LGPL
4179  *
4180  * element
4181  * 
4182  */
4183
4184 /**
4185  * @class Roo.bootstrap.Element
4186  * @extends Roo.bootstrap.Component
4187  * Bootstrap Element class
4188  * @cfg {String} html contents of the element
4189  * @cfg {String} tag tag of the element
4190  * @cfg {String} cls class of the element
4191  * @cfg {Boolean} preventDefault (true|false) default false
4192  * @cfg {Boolean} clickable (true|false) default false
4193  * 
4194  * @constructor
4195  * Create a new Element
4196  * @param {Object} config The config object
4197  */
4198
4199 Roo.bootstrap.Element = function(config){
4200     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4201     
4202     this.addEvents({
4203         // raw events
4204         /**
4205          * @event click
4206          * When a element is chick
4207          * @param {Roo.bootstrap.Element} this
4208          * @param {Roo.EventObject} e
4209          */
4210         "click" : true
4211     });
4212 };
4213
4214 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4215     
4216     tag: 'div',
4217     cls: '',
4218     html: '',
4219     preventDefault: false, 
4220     clickable: false,
4221     
4222     getAutoCreate : function(){
4223         
4224         var cfg = {
4225             tag: this.tag,
4226             cls: this.cls,
4227             html: this.html
4228         }
4229         
4230         return cfg;
4231     },
4232     
4233     initEvents: function() 
4234     {
4235         Roo.bootstrap.Element.superclass.initEvents.call(this);
4236         
4237         if(this.clickable){
4238             this.el.on('click', this.onClick, this);
4239         }
4240         
4241     },
4242     
4243     onClick : function(e)
4244     {
4245         if(this.preventDefault){
4246             e.preventDefault();
4247         }
4248         
4249         this.fireEvent('click', this, e);
4250     },
4251     
4252     getValue : function()
4253     {
4254         return this.el.dom.innerHTML;
4255     },
4256     
4257     setValue : function(value)
4258     {
4259         this.el.dom.innerHTML = value;
4260     }
4261    
4262 });
4263
4264  
4265
4266  /*
4267  * - LGPL
4268  *
4269  * pagination
4270  * 
4271  */
4272
4273 /**
4274  * @class Roo.bootstrap.Pagination
4275  * @extends Roo.bootstrap.Component
4276  * Bootstrap Pagination class
4277  * @cfg {String} size xs | sm | md | lg
4278  * @cfg {Boolean} inverse false | true
4279  * 
4280  * @constructor
4281  * Create a new Pagination
4282  * @param {Object} config The config object
4283  */
4284
4285 Roo.bootstrap.Pagination = function(config){
4286     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4287 };
4288
4289 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4290     
4291     cls: false,
4292     size: false,
4293     inverse: false,
4294     
4295     getAutoCreate : function(){
4296         var cfg = {
4297             tag: 'ul',
4298                 cls: 'pagination'
4299         };
4300         if (this.inverse) {
4301             cfg.cls += ' inverse';
4302         }
4303         if (this.html) {
4304             cfg.html=this.html;
4305         }
4306         if (this.cls) {
4307             cfg.cls += " " + this.cls;
4308         }
4309         return cfg;
4310     }
4311    
4312 });
4313
4314  
4315
4316  /*
4317  * - LGPL
4318  *
4319  * Pagination item
4320  * 
4321  */
4322
4323
4324 /**
4325  * @class Roo.bootstrap.PaginationItem
4326  * @extends Roo.bootstrap.Component
4327  * Bootstrap PaginationItem class
4328  * @cfg {String} html text
4329  * @cfg {String} href the link
4330  * @cfg {Boolean} preventDefault (true | false) default true
4331  * @cfg {Boolean} active (true | false) default false
4332  * @cfg {Boolean} disabled default false
4333  * 
4334  * 
4335  * @constructor
4336  * Create a new PaginationItem
4337  * @param {Object} config The config object
4338  */
4339
4340
4341 Roo.bootstrap.PaginationItem = function(config){
4342     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4343     this.addEvents({
4344         // raw events
4345         /**
4346          * @event click
4347          * The raw click event for the entire grid.
4348          * @param {Roo.EventObject} e
4349          */
4350         "click" : true
4351     });
4352 };
4353
4354 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4355     
4356     href : false,
4357     html : false,
4358     preventDefault: true,
4359     active : false,
4360     cls : false,
4361     disabled: false,
4362     
4363     getAutoCreate : function(){
4364         var cfg= {
4365             tag: 'li',
4366             cn: [
4367                 {
4368                     tag : 'a',
4369                     href : this.href ? this.href : '#',
4370                     html : this.html ? this.html : ''
4371                 }
4372             ]
4373         };
4374         
4375         if(this.cls){
4376             cfg.cls = this.cls;
4377         }
4378         
4379         if(this.disabled){
4380             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4381         }
4382         
4383         if(this.active){
4384             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4385         }
4386         
4387         return cfg;
4388     },
4389     
4390     initEvents: function() {
4391         
4392         this.el.on('click', this.onClick, this);
4393         
4394     },
4395     onClick : function(e)
4396     {
4397         Roo.log('PaginationItem on click ');
4398         if(this.preventDefault){
4399             e.preventDefault();
4400         }
4401         
4402         if(this.disabled){
4403             return;
4404         }
4405         
4406         this.fireEvent('click', this, e);
4407     }
4408    
4409 });
4410
4411  
4412
4413  /*
4414  * - LGPL
4415  *
4416  * slider
4417  * 
4418  */
4419
4420
4421 /**
4422  * @class Roo.bootstrap.Slider
4423  * @extends Roo.bootstrap.Component
4424  * Bootstrap Slider class
4425  *    
4426  * @constructor
4427  * Create a new Slider
4428  * @param {Object} config The config object
4429  */
4430
4431 Roo.bootstrap.Slider = function(config){
4432     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4433 };
4434
4435 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4436     
4437     getAutoCreate : function(){
4438         
4439         var cfg = {
4440             tag: 'div',
4441             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4442             cn: [
4443                 {
4444                     tag: 'a',
4445                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4446                 }
4447             ]
4448         }
4449         
4450         return cfg;
4451     }
4452    
4453 });
4454
4455  /*
4456  * Based on:
4457  * Ext JS Library 1.1.1
4458  * Copyright(c) 2006-2007, Ext JS, LLC.
4459  *
4460  * Originally Released Under LGPL - original licence link has changed is not relivant.
4461  *
4462  * Fork - LGPL
4463  * <script type="text/javascript">
4464  */
4465  
4466
4467 /**
4468  * @class Roo.grid.ColumnModel
4469  * @extends Roo.util.Observable
4470  * This is the default implementation of a ColumnModel used by the Grid. It defines
4471  * the columns in the grid.
4472  * <br>Usage:<br>
4473  <pre><code>
4474  var colModel = new Roo.grid.ColumnModel([
4475         {header: "Ticker", width: 60, sortable: true, locked: true},
4476         {header: "Company Name", width: 150, sortable: true},
4477         {header: "Market Cap.", width: 100, sortable: true},
4478         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4479         {header: "Employees", width: 100, sortable: true, resizable: false}
4480  ]);
4481  </code></pre>
4482  * <p>
4483  
4484  * The config options listed for this class are options which may appear in each
4485  * individual column definition.
4486  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4487  * @constructor
4488  * @param {Object} config An Array of column config objects. See this class's
4489  * config objects for details.
4490 */
4491 Roo.grid.ColumnModel = function(config){
4492         /**
4493      * The config passed into the constructor
4494      */
4495     this.config = config;
4496     this.lookup = {};
4497
4498     // if no id, create one
4499     // if the column does not have a dataIndex mapping,
4500     // map it to the order it is in the config
4501     for(var i = 0, len = config.length; i < len; i++){
4502         var c = config[i];
4503         if(typeof c.dataIndex == "undefined"){
4504             c.dataIndex = i;
4505         }
4506         if(typeof c.renderer == "string"){
4507             c.renderer = Roo.util.Format[c.renderer];
4508         }
4509         if(typeof c.id == "undefined"){
4510             c.id = Roo.id();
4511         }
4512         if(c.editor && c.editor.xtype){
4513             c.editor  = Roo.factory(c.editor, Roo.grid);
4514         }
4515         if(c.editor && c.editor.isFormField){
4516             c.editor = new Roo.grid.GridEditor(c.editor);
4517         }
4518         this.lookup[c.id] = c;
4519     }
4520
4521     /**
4522      * The width of columns which have no width specified (defaults to 100)
4523      * @type Number
4524      */
4525     this.defaultWidth = 100;
4526
4527     /**
4528      * Default sortable of columns which have no sortable specified (defaults to false)
4529      * @type Boolean
4530      */
4531     this.defaultSortable = false;
4532
4533     this.addEvents({
4534         /**
4535              * @event widthchange
4536              * Fires when the width of a column changes.
4537              * @param {ColumnModel} this
4538              * @param {Number} columnIndex The column index
4539              * @param {Number} newWidth The new width
4540              */
4541             "widthchange": true,
4542         /**
4543              * @event headerchange
4544              * Fires when the text of a header changes.
4545              * @param {ColumnModel} this
4546              * @param {Number} columnIndex The column index
4547              * @param {Number} newText The new header text
4548              */
4549             "headerchange": true,
4550         /**
4551              * @event hiddenchange
4552              * Fires when a column is hidden or "unhidden".
4553              * @param {ColumnModel} this
4554              * @param {Number} columnIndex The column index
4555              * @param {Boolean} hidden true if hidden, false otherwise
4556              */
4557             "hiddenchange": true,
4558             /**
4559          * @event columnmoved
4560          * Fires when a column is moved.
4561          * @param {ColumnModel} this
4562          * @param {Number} oldIndex
4563          * @param {Number} newIndex
4564          */
4565         "columnmoved" : true,
4566         /**
4567          * @event columlockchange
4568          * Fires when a column's locked state is changed
4569          * @param {ColumnModel} this
4570          * @param {Number} colIndex
4571          * @param {Boolean} locked true if locked
4572          */
4573         "columnlockchange" : true
4574     });
4575     Roo.grid.ColumnModel.superclass.constructor.call(this);
4576 };
4577 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4578     /**
4579      * @cfg {String} header The header text to display in the Grid view.
4580      */
4581     /**
4582      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4583      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4584      * specified, the column's index is used as an index into the Record's data Array.
4585      */
4586     /**
4587      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4588      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4589      */
4590     /**
4591      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4592      * Defaults to the value of the {@link #defaultSortable} property.
4593      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4594      */
4595     /**
4596      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4597      */
4598     /**
4599      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4600      */
4601     /**
4602      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4603      */
4604     /**
4605      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4606      */
4607     /**
4608      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4609      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4610      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4611      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4612      */
4613        /**
4614      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4615      */
4616     /**
4617      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4618      */
4619     /**
4620      * @cfg {String} cursor (Optional)
4621      */
4622     /**
4623      * @cfg {String} tooltip (Optional)
4624      */
4625     /**
4626      * Returns the id of the column at the specified index.
4627      * @param {Number} index The column index
4628      * @return {String} the id
4629      */
4630     getColumnId : function(index){
4631         return this.config[index].id;
4632     },
4633
4634     /**
4635      * Returns the column for a specified id.
4636      * @param {String} id The column id
4637      * @return {Object} the column
4638      */
4639     getColumnById : function(id){
4640         return this.lookup[id];
4641     },
4642
4643     
4644     /**
4645      * Returns the column for a specified dataIndex.
4646      * @param {String} dataIndex The column dataIndex
4647      * @return {Object|Boolean} the column or false if not found
4648      */
4649     getColumnByDataIndex: function(dataIndex){
4650         var index = this.findColumnIndex(dataIndex);
4651         return index > -1 ? this.config[index] : false;
4652     },
4653     
4654     /**
4655      * Returns the index for a specified column id.
4656      * @param {String} id The column id
4657      * @return {Number} the index, or -1 if not found
4658      */
4659     getIndexById : function(id){
4660         for(var i = 0, len = this.config.length; i < len; i++){
4661             if(this.config[i].id == id){
4662                 return i;
4663             }
4664         }
4665         return -1;
4666     },
4667     
4668     /**
4669      * Returns the index for a specified column dataIndex.
4670      * @param {String} dataIndex The column dataIndex
4671      * @return {Number} the index, or -1 if not found
4672      */
4673     
4674     findColumnIndex : function(dataIndex){
4675         for(var i = 0, len = this.config.length; i < len; i++){
4676             if(this.config[i].dataIndex == dataIndex){
4677                 return i;
4678             }
4679         }
4680         return -1;
4681     },
4682     
4683     
4684     moveColumn : function(oldIndex, newIndex){
4685         var c = this.config[oldIndex];
4686         this.config.splice(oldIndex, 1);
4687         this.config.splice(newIndex, 0, c);
4688         this.dataMap = null;
4689         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4690     },
4691
4692     isLocked : function(colIndex){
4693         return this.config[colIndex].locked === true;
4694     },
4695
4696     setLocked : function(colIndex, value, suppressEvent){
4697         if(this.isLocked(colIndex) == value){
4698             return;
4699         }
4700         this.config[colIndex].locked = value;
4701         if(!suppressEvent){
4702             this.fireEvent("columnlockchange", this, colIndex, value);
4703         }
4704     },
4705
4706     getTotalLockedWidth : function(){
4707         var totalWidth = 0;
4708         for(var i = 0; i < this.config.length; i++){
4709             if(this.isLocked(i) && !this.isHidden(i)){
4710                 this.totalWidth += this.getColumnWidth(i);
4711             }
4712         }
4713         return totalWidth;
4714     },
4715
4716     getLockedCount : function(){
4717         for(var i = 0, len = this.config.length; i < len; i++){
4718             if(!this.isLocked(i)){
4719                 return i;
4720             }
4721         }
4722     },
4723
4724     /**
4725      * Returns the number of columns.
4726      * @return {Number}
4727      */
4728     getColumnCount : function(visibleOnly){
4729         if(visibleOnly === true){
4730             var c = 0;
4731             for(var i = 0, len = this.config.length; i < len; i++){
4732                 if(!this.isHidden(i)){
4733                     c++;
4734                 }
4735             }
4736             return c;
4737         }
4738         return this.config.length;
4739     },
4740
4741     /**
4742      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4743      * @param {Function} fn
4744      * @param {Object} scope (optional)
4745      * @return {Array} result
4746      */
4747     getColumnsBy : function(fn, scope){
4748         var r = [];
4749         for(var i = 0, len = this.config.length; i < len; i++){
4750             var c = this.config[i];
4751             if(fn.call(scope||this, c, i) === true){
4752                 r[r.length] = c;
4753             }
4754         }
4755         return r;
4756     },
4757
4758     /**
4759      * Returns true if the specified column is sortable.
4760      * @param {Number} col The column index
4761      * @return {Boolean}
4762      */
4763     isSortable : function(col){
4764         if(typeof this.config[col].sortable == "undefined"){
4765             return this.defaultSortable;
4766         }
4767         return this.config[col].sortable;
4768     },
4769
4770     /**
4771      * Returns the rendering (formatting) function defined for the column.
4772      * @param {Number} col The column index.
4773      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4774      */
4775     getRenderer : function(col){
4776         if(!this.config[col].renderer){
4777             return Roo.grid.ColumnModel.defaultRenderer;
4778         }
4779         return this.config[col].renderer;
4780     },
4781
4782     /**
4783      * Sets the rendering (formatting) function for a column.
4784      * @param {Number} col The column index
4785      * @param {Function} fn The function to use to process the cell's raw data
4786      * to return HTML markup for the grid view. The render function is called with
4787      * the following parameters:<ul>
4788      * <li>Data value.</li>
4789      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4790      * <li>css A CSS style string to apply to the table cell.</li>
4791      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4792      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4793      * <li>Row index</li>
4794      * <li>Column index</li>
4795      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4796      */
4797     setRenderer : function(col, fn){
4798         this.config[col].renderer = fn;
4799     },
4800
4801     /**
4802      * Returns the width for the specified column.
4803      * @param {Number} col The column index
4804      * @return {Number}
4805      */
4806     getColumnWidth : function(col){
4807         return this.config[col].width * 1 || this.defaultWidth;
4808     },
4809
4810     /**
4811      * Sets the width for a column.
4812      * @param {Number} col The column index
4813      * @param {Number} width The new width
4814      */
4815     setColumnWidth : function(col, width, suppressEvent){
4816         this.config[col].width = width;
4817         this.totalWidth = null;
4818         if(!suppressEvent){
4819              this.fireEvent("widthchange", this, col, width);
4820         }
4821     },
4822
4823     /**
4824      * Returns the total width of all columns.
4825      * @param {Boolean} includeHidden True to include hidden column widths
4826      * @return {Number}
4827      */
4828     getTotalWidth : function(includeHidden){
4829         if(!this.totalWidth){
4830             this.totalWidth = 0;
4831             for(var i = 0, len = this.config.length; i < len; i++){
4832                 if(includeHidden || !this.isHidden(i)){
4833                     this.totalWidth += this.getColumnWidth(i);
4834                 }
4835             }
4836         }
4837         return this.totalWidth;
4838     },
4839
4840     /**
4841      * Returns the header for the specified column.
4842      * @param {Number} col The column index
4843      * @return {String}
4844      */
4845     getColumnHeader : function(col){
4846         return this.config[col].header;
4847     },
4848
4849     /**
4850      * Sets the header for a column.
4851      * @param {Number} col The column index
4852      * @param {String} header The new header
4853      */
4854     setColumnHeader : function(col, header){
4855         this.config[col].header = header;
4856         this.fireEvent("headerchange", this, col, header);
4857     },
4858
4859     /**
4860      * Returns the tooltip for the specified column.
4861      * @param {Number} col The column index
4862      * @return {String}
4863      */
4864     getColumnTooltip : function(col){
4865             return this.config[col].tooltip;
4866     },
4867     /**
4868      * Sets the tooltip for a column.
4869      * @param {Number} col The column index
4870      * @param {String} tooltip The new tooltip
4871      */
4872     setColumnTooltip : function(col, tooltip){
4873             this.config[col].tooltip = tooltip;
4874     },
4875
4876     /**
4877      * Returns the dataIndex for the specified column.
4878      * @param {Number} col The column index
4879      * @return {Number}
4880      */
4881     getDataIndex : function(col){
4882         return this.config[col].dataIndex;
4883     },
4884
4885     /**
4886      * Sets the dataIndex for a column.
4887      * @param {Number} col The column index
4888      * @param {Number} dataIndex The new dataIndex
4889      */
4890     setDataIndex : function(col, dataIndex){
4891         this.config[col].dataIndex = dataIndex;
4892     },
4893
4894     
4895     
4896     /**
4897      * Returns true if the cell is editable.
4898      * @param {Number} colIndex The column index
4899      * @param {Number} rowIndex The row index
4900      * @return {Boolean}
4901      */
4902     isCellEditable : function(colIndex, rowIndex){
4903         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4904     },
4905
4906     /**
4907      * Returns the editor defined for the cell/column.
4908      * return false or null to disable editing.
4909      * @param {Number} colIndex The column index
4910      * @param {Number} rowIndex The row index
4911      * @return {Object}
4912      */
4913     getCellEditor : function(colIndex, rowIndex){
4914         return this.config[colIndex].editor;
4915     },
4916
4917     /**
4918      * Sets if a column is editable.
4919      * @param {Number} col The column index
4920      * @param {Boolean} editable True if the column is editable
4921      */
4922     setEditable : function(col, editable){
4923         this.config[col].editable = editable;
4924     },
4925
4926
4927     /**
4928      * Returns true if the column is hidden.
4929      * @param {Number} colIndex The column index
4930      * @return {Boolean}
4931      */
4932     isHidden : function(colIndex){
4933         return this.config[colIndex].hidden;
4934     },
4935
4936
4937     /**
4938      * Returns true if the column width cannot be changed
4939      */
4940     isFixed : function(colIndex){
4941         return this.config[colIndex].fixed;
4942     },
4943
4944     /**
4945      * Returns true if the column can be resized
4946      * @return {Boolean}
4947      */
4948     isResizable : function(colIndex){
4949         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4950     },
4951     /**
4952      * Sets if a column is hidden.
4953      * @param {Number} colIndex The column index
4954      * @param {Boolean} hidden True if the column is hidden
4955      */
4956     setHidden : function(colIndex, hidden){
4957         this.config[colIndex].hidden = hidden;
4958         this.totalWidth = null;
4959         this.fireEvent("hiddenchange", this, colIndex, hidden);
4960     },
4961
4962     /**
4963      * Sets the editor for a column.
4964      * @param {Number} col The column index
4965      * @param {Object} editor The editor object
4966      */
4967     setEditor : function(col, editor){
4968         this.config[col].editor = editor;
4969     }
4970 });
4971
4972 Roo.grid.ColumnModel.defaultRenderer = function(value){
4973         if(typeof value == "string" && value.length < 1){
4974             return "&#160;";
4975         }
4976         return value;
4977 };
4978
4979 // Alias for backwards compatibility
4980 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4981 /*
4982  * Based on:
4983  * Ext JS Library 1.1.1
4984  * Copyright(c) 2006-2007, Ext JS, LLC.
4985  *
4986  * Originally Released Under LGPL - original licence link has changed is not relivant.
4987  *
4988  * Fork - LGPL
4989  * <script type="text/javascript">
4990  */
4991  
4992 /**
4993  * @class Roo.LoadMask
4994  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4995  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4996  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4997  * element's UpdateManager load indicator and will be destroyed after the initial load.
4998  * @constructor
4999  * Create a new LoadMask
5000  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5001  * @param {Object} config The config object
5002  */
5003 Roo.LoadMask = function(el, config){
5004     this.el = Roo.get(el);
5005     Roo.apply(this, config);
5006     if(this.store){
5007         this.store.on('beforeload', this.onBeforeLoad, this);
5008         this.store.on('load', this.onLoad, this);
5009         this.store.on('loadexception', this.onLoadException, this);
5010         this.removeMask = false;
5011     }else{
5012         var um = this.el.getUpdateManager();
5013         um.showLoadIndicator = false; // disable the default indicator
5014         um.on('beforeupdate', this.onBeforeLoad, this);
5015         um.on('update', this.onLoad, this);
5016         um.on('failure', this.onLoad, this);
5017         this.removeMask = true;
5018     }
5019 };
5020
5021 Roo.LoadMask.prototype = {
5022     /**
5023      * @cfg {Boolean} removeMask
5024      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5025      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5026      */
5027     /**
5028      * @cfg {String} msg
5029      * The text to display in a centered loading message box (defaults to 'Loading...')
5030      */
5031     msg : 'Loading...',
5032     /**
5033      * @cfg {String} msgCls
5034      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5035      */
5036     msgCls : 'x-mask-loading',
5037
5038     /**
5039      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5040      * @type Boolean
5041      */
5042     disabled: false,
5043
5044     /**
5045      * Disables the mask to prevent it from being displayed
5046      */
5047     disable : function(){
5048        this.disabled = true;
5049     },
5050
5051     /**
5052      * Enables the mask so that it can be displayed
5053      */
5054     enable : function(){
5055         this.disabled = false;
5056     },
5057     
5058     onLoadException : function()
5059     {
5060         Roo.log(arguments);
5061         
5062         if (typeof(arguments[3]) != 'undefined') {
5063             Roo.MessageBox.alert("Error loading",arguments[3]);
5064         } 
5065         /*
5066         try {
5067             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5068                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5069             }   
5070         } catch(e) {
5071             
5072         }
5073         */
5074     
5075         
5076         
5077         this.el.unmask(this.removeMask);
5078     },
5079     // private
5080     onLoad : function()
5081     {
5082         this.el.unmask(this.removeMask);
5083     },
5084
5085     // private
5086     onBeforeLoad : function(){
5087         if(!this.disabled){
5088             this.el.mask(this.msg, this.msgCls);
5089         }
5090     },
5091
5092     // private
5093     destroy : function(){
5094         if(this.store){
5095             this.store.un('beforeload', this.onBeforeLoad, this);
5096             this.store.un('load', this.onLoad, this);
5097             this.store.un('loadexception', this.onLoadException, this);
5098         }else{
5099             var um = this.el.getUpdateManager();
5100             um.un('beforeupdate', this.onBeforeLoad, this);
5101             um.un('update', this.onLoad, this);
5102             um.un('failure', this.onLoad, this);
5103         }
5104     }
5105 };/*
5106  * - LGPL
5107  *
5108  * table
5109  * 
5110  */
5111
5112 /**
5113  * @class Roo.bootstrap.Table
5114  * @extends Roo.bootstrap.Component
5115  * Bootstrap Table class
5116  * @cfg {String} cls table class
5117  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5118  * @cfg {String} bgcolor Specifies the background color for a table
5119  * @cfg {Number} border Specifies whether the table cells should have borders or not
5120  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5121  * @cfg {Number} cellspacing Specifies the space between cells
5122  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5123  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5124  * @cfg {String} sortable Specifies that the table should be sortable
5125  * @cfg {String} summary Specifies a summary of the content of a table
5126  * @cfg {Number} width Specifies the width of a table
5127  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5128  * 
5129  * @cfg {boolean} striped Should the rows be alternative striped
5130  * @cfg {boolean} bordered Add borders to the table
5131  * @cfg {boolean} hover Add hover highlighting
5132  * @cfg {boolean} condensed Format condensed
5133  * @cfg {boolean} responsive Format condensed
5134  * @cfg {Boolean} loadMask (true|false) default false
5135  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5136  * @cfg {Boolean} thead (true|false) generate thead, default true
5137  * @cfg {Boolean} RowSelection (true|false) default false
5138  * @cfg {Boolean} CellSelection (true|false) default false
5139  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5140  
5141  * 
5142  * @constructor
5143  * Create a new Table
5144  * @param {Object} config The config object
5145  */
5146
5147 Roo.bootstrap.Table = function(config){
5148     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5149     
5150     if (this.sm) {
5151         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5152         this.sm = this.selModel;
5153         this.sm.xmodule = this.xmodule || false;
5154     }
5155     if (this.cm && typeof(this.cm.config) == 'undefined') {
5156         this.colModel = new Roo.grid.ColumnModel(this.cm);
5157         this.cm = this.colModel;
5158         this.cm.xmodule = this.xmodule || false;
5159     }
5160     if (this.store) {
5161         this.store= Roo.factory(this.store, Roo.data);
5162         this.ds = this.store;
5163         this.ds.xmodule = this.xmodule || false;
5164          
5165     }
5166     if (this.footer && this.store) {
5167         this.footer.dataSource = this.ds;
5168         this.footer = Roo.factory(this.footer);
5169     }
5170     
5171     /** @private */
5172     this.addEvents({
5173         /**
5174          * @event cellclick
5175          * Fires when a cell is clicked
5176          * @param {Roo.bootstrap.Table} this
5177          * @param {Roo.Element} el
5178          * @param {Number} rowIndex
5179          * @param {Number} columnIndex
5180          * @param {Roo.EventObject} e
5181          */
5182         "cellclick" : true,
5183         /**
5184          * @event celldblclick
5185          * Fires when a cell is double clicked
5186          * @param {Roo.bootstrap.Table} this
5187          * @param {Roo.Element} el
5188          * @param {Number} rowIndex
5189          * @param {Number} columnIndex
5190          * @param {Roo.EventObject} e
5191          */
5192         "celldblclick" : true,
5193         /**
5194          * @event rowclick
5195          * Fires when a row is clicked
5196          * @param {Roo.bootstrap.Table} this
5197          * @param {Roo.Element} el
5198          * @param {Number} rowIndex
5199          * @param {Roo.EventObject} e
5200          */
5201         "rowclick" : true,
5202         /**
5203          * @event rowdblclick
5204          * Fires when a row is double clicked
5205          * @param {Roo.bootstrap.Table} this
5206          * @param {Roo.Element} el
5207          * @param {Number} rowIndex
5208          * @param {Roo.EventObject} e
5209          */
5210         "rowdblclick" : true,
5211         /**
5212          * @event mouseover
5213          * Fires when a mouseover occur
5214          * @param {Roo.bootstrap.Table} this
5215          * @param {Roo.Element} el
5216          * @param {Number} rowIndex
5217          * @param {Number} columnIndex
5218          * @param {Roo.EventObject} e
5219          */
5220         "mouseover" : true,
5221         /**
5222          * @event mouseout
5223          * Fires when a mouseout occur
5224          * @param {Roo.bootstrap.Table} this
5225          * @param {Roo.Element} el
5226          * @param {Number} rowIndex
5227          * @param {Number} columnIndex
5228          * @param {Roo.EventObject} e
5229          */
5230         "mouseout" : true,
5231         /**
5232          * @event rowclass
5233          * Fires when a row is rendered, so you can change add a style to it.
5234          * @param {Roo.bootstrap.Table} this
5235          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5236          */
5237         'rowclass' : true,
5238           /**
5239          * @event rowsrendered
5240          * Fires when all the  rows have been rendered
5241          * @param {Roo.bootstrap.Table} this
5242          */
5243         'rowsrendered' : true
5244         
5245     });
5246 };
5247
5248 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5249     
5250     cls: false,
5251     align: false,
5252     bgcolor: false,
5253     border: false,
5254     cellpadding: false,
5255     cellspacing: false,
5256     frame: false,
5257     rules: false,
5258     sortable: false,
5259     summary: false,
5260     width: false,
5261     striped : false,
5262     bordered: false,
5263     hover:  false,
5264     condensed : false,
5265     responsive : false,
5266     sm : false,
5267     cm : false,
5268     store : false,
5269     loadMask : false,
5270     tfoot : true,
5271     thead : true,
5272     RowSelection : false,
5273     CellSelection : false,
5274     layout : false,
5275     
5276     // Roo.Element - the tbody
5277     mainBody: false, 
5278     
5279     getAutoCreate : function(){
5280         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5281         
5282         cfg = {
5283             tag: 'table',
5284             cls : 'table',
5285             cn : []
5286         }
5287             
5288         if (this.striped) {
5289             cfg.cls += ' table-striped';
5290         }
5291         
5292         if (this.hover) {
5293             cfg.cls += ' table-hover';
5294         }
5295         if (this.bordered) {
5296             cfg.cls += ' table-bordered';
5297         }
5298         if (this.condensed) {
5299             cfg.cls += ' table-condensed';
5300         }
5301         if (this.responsive) {
5302             cfg.cls += ' table-responsive';
5303         }
5304         
5305         if (this.cls) {
5306             cfg.cls+=  ' ' +this.cls;
5307         }
5308         
5309         // this lot should be simplifed...
5310         
5311         if (this.align) {
5312             cfg.align=this.align;
5313         }
5314         if (this.bgcolor) {
5315             cfg.bgcolor=this.bgcolor;
5316         }
5317         if (this.border) {
5318             cfg.border=this.border;
5319         }
5320         if (this.cellpadding) {
5321             cfg.cellpadding=this.cellpadding;
5322         }
5323         if (this.cellspacing) {
5324             cfg.cellspacing=this.cellspacing;
5325         }
5326         if (this.frame) {
5327             cfg.frame=this.frame;
5328         }
5329         if (this.rules) {
5330             cfg.rules=this.rules;
5331         }
5332         if (this.sortable) {
5333             cfg.sortable=this.sortable;
5334         }
5335         if (this.summary) {
5336             cfg.summary=this.summary;
5337         }
5338         if (this.width) {
5339             cfg.width=this.width;
5340         }
5341         if (this.layout) {
5342             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5343         }
5344         
5345         if(this.store || this.cm){
5346             if(this.thead){
5347                 cfg.cn.push(this.renderHeader());
5348             }
5349             
5350             cfg.cn.push(this.renderBody());
5351             
5352             if(this.tfoot){
5353                 cfg.cn.push(this.renderFooter());
5354             }
5355             
5356             cfg.cls+=  ' TableGrid';
5357         }
5358         
5359         return { cn : [ cfg ] };
5360     },
5361     
5362     initEvents : function()
5363     {   
5364         if(!this.store || !this.cm){
5365             return;
5366         }
5367         
5368         //Roo.log('initEvents with ds!!!!');
5369         
5370         this.mainBody = this.el.select('tbody', true).first();
5371         
5372         
5373         var _this = this;
5374         
5375         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5376             e.on('click', _this.sort, _this);
5377         });
5378         
5379         this.el.on("click", this.onClick, this);
5380         this.el.on("dblclick", this.onDblClick, this);
5381         
5382         // why is this done????? = it breaks dialogs??
5383         //this.parent().el.setStyle('position', 'relative');
5384         
5385         
5386         if (this.footer) {
5387             this.footer.parentId = this.id;
5388             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5389         }
5390         
5391         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5392         
5393         this.store.on('load', this.onLoad, this);
5394         this.store.on('beforeload', this.onBeforeLoad, this);
5395         this.store.on('update', this.onUpdate, this);
5396         this.store.on('add', this.onAdd, this);
5397         
5398     },
5399     
5400     onMouseover : function(e, el)
5401     {
5402         var cell = Roo.get(el);
5403         
5404         if(!cell){
5405             return;
5406         }
5407         
5408         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5409             cell = cell.findParent('td', false, true);
5410         }
5411         
5412         var row = cell.findParent('tr', false, true);
5413         var cellIndex = cell.dom.cellIndex;
5414         var rowIndex = row.dom.rowIndex - 1; // start from 0
5415         
5416         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5417         
5418     },
5419     
5420     onMouseout : function(e, el)
5421     {
5422         var cell = Roo.get(el);
5423         
5424         if(!cell){
5425             return;
5426         }
5427         
5428         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5429             cell = cell.findParent('td', false, true);
5430         }
5431         
5432         var row = cell.findParent('tr', false, true);
5433         var cellIndex = cell.dom.cellIndex;
5434         var rowIndex = row.dom.rowIndex - 1; // start from 0
5435         
5436         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5437         
5438     },
5439     
5440     onClick : function(e, el)
5441     {
5442         var cell = Roo.get(el);
5443         
5444         if(!cell || (!this.CellSelection && !this.RowSelection)){
5445             return;
5446         }
5447         
5448         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5449             cell = cell.findParent('td', false, true);
5450         }
5451         
5452         if(!cell || typeof(cell) == 'undefined'){
5453             return;
5454         }
5455         
5456         var row = cell.findParent('tr', false, true);
5457         
5458         if(!row || typeof(row) == 'undefined'){
5459             return;
5460         }
5461         
5462         var cellIndex = cell.dom.cellIndex;
5463         var rowIndex = this.getRowIndex(row);
5464         
5465         if(this.CellSelection){
5466             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5467         }
5468         
5469         if(this.RowSelection){
5470             this.fireEvent('rowclick', this, row, rowIndex, e);
5471         }
5472         
5473         
5474     },
5475     
5476     onDblClick : function(e,el)
5477     {
5478         var cell = Roo.get(el);
5479         
5480         if(!cell || (!this.CellSelection && !this.RowSelection)){
5481             return;
5482         }
5483         
5484         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5485             cell = cell.findParent('td', false, true);
5486         }
5487         
5488         if(!cell || typeof(cell) == 'undefined'){
5489             return;
5490         }
5491         
5492         var row = cell.findParent('tr', false, true);
5493         
5494         if(!row || typeof(row) == 'undefined'){
5495             return;
5496         }
5497         
5498         var cellIndex = cell.dom.cellIndex;
5499         var rowIndex = this.getRowIndex(row);
5500         
5501         if(this.CellSelection){
5502             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5503         }
5504         
5505         if(this.RowSelection){
5506             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5507         }
5508     },
5509     
5510     sort : function(e,el)
5511     {
5512         var col = Roo.get(el);
5513         
5514         if(!col.hasClass('sortable')){
5515             return;
5516         }
5517         
5518         var sort = col.attr('sort');
5519         var dir = 'ASC';
5520         
5521         if(col.hasClass('glyphicon-arrow-up')){
5522             dir = 'DESC';
5523         }
5524         
5525         this.store.sortInfo = {field : sort, direction : dir};
5526         
5527         if (this.footer) {
5528             Roo.log("calling footer first");
5529             this.footer.onClick('first');
5530         } else {
5531         
5532             this.store.load({ params : { start : 0 } });
5533         }
5534     },
5535     
5536     renderHeader : function()
5537     {
5538         var header = {
5539             tag: 'thead',
5540             cn : []
5541         };
5542         
5543         var cm = this.cm;
5544         
5545         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5546             
5547             var config = cm.config[i];
5548                     
5549             var c = {
5550                 tag: 'th',
5551                 style : '',
5552                 html: cm.getColumnHeader(i)
5553             };
5554             
5555             if(typeof(config.tooltip) != 'undefined'){
5556                 c.tooltip = config.tooltip;
5557             }
5558             
5559             if(typeof(config.colspan) != 'undefined'){
5560                 c.colspan = config.colspan;
5561             }
5562             
5563             if(typeof(config.hidden) != 'undefined' && config.hidden){
5564                 c.style += ' display:none;';
5565             }
5566             
5567             if(typeof(config.dataIndex) != 'undefined'){
5568                 c.sort = config.dataIndex;
5569             }
5570             
5571             if(typeof(config.sortable) != 'undefined' && config.sortable){
5572                 c.cls = 'sortable';
5573             }
5574             
5575             if(typeof(config.align) != 'undefined' && config.align.length){
5576                 c.style += ' text-align:' + config.align + ';';
5577             }
5578             
5579             if(typeof(config.width) != 'undefined'){
5580                 c.style += ' width:' + config.width + 'px;';
5581             }
5582             
5583             if(typeof(config.cls) != 'undefined'){
5584                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5585             }
5586             
5587             header.cn.push(c)
5588         }
5589         
5590         return header;
5591     },
5592     
5593     renderBody : function()
5594     {
5595         var body = {
5596             tag: 'tbody',
5597             cn : [
5598                 {
5599                     tag: 'tr',
5600                     cn : [
5601                         {
5602                             tag : 'td',
5603                             colspan :  this.cm.getColumnCount()
5604                         }
5605                     ]
5606                 }
5607             ]
5608         };
5609         
5610         return body;
5611     },
5612     
5613     renderFooter : function()
5614     {
5615         var footer = {
5616             tag: 'tfoot',
5617             cn : [
5618                 {
5619                     tag: 'tr',
5620                     cn : [
5621                         {
5622                             tag : 'td',
5623                             colspan :  this.cm.getColumnCount()
5624                         }
5625                     ]
5626                 }
5627             ]
5628         };
5629         
5630         return footer;
5631     },
5632     
5633     
5634     
5635     onLoad : function()
5636     {
5637         Roo.log('ds onload');
5638         this.clear();
5639         
5640         var _this = this;
5641         var cm = this.cm;
5642         var ds = this.store;
5643         
5644         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5645             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5646             
5647             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5648                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5649             }
5650             
5651             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5652                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5653             }
5654         });
5655         
5656         var tbody =  this.mainBody;
5657               
5658         if(ds.getCount() > 0){
5659             ds.data.each(function(d,rowIndex){
5660                 var row =  this.renderRow(cm, ds, rowIndex);
5661                 
5662                 tbody.createChild(row);
5663                 
5664                 var _this = this;
5665                 
5666                 if(row.cellObjects.length){
5667                     Roo.each(row.cellObjects, function(r){
5668                         _this.renderCellObject(r);
5669                     })
5670                 }
5671                 
5672             }, this);
5673         }
5674         
5675         Roo.each(this.el.select('tbody td', true).elements, function(e){
5676             e.on('mouseover', _this.onMouseover, _this);
5677         });
5678         
5679         Roo.each(this.el.select('tbody td', true).elements, function(e){
5680             e.on('mouseout', _this.onMouseout, _this);
5681         });
5682         this.fireEvent('rowsrendered', this);
5683         //if(this.loadMask){
5684         //    this.maskEl.hide();
5685         //}
5686     },
5687     
5688     
5689     onUpdate : function(ds,record)
5690     {
5691         this.refreshRow(record);
5692     },
5693     
5694     onRemove : function(ds, record, index, isUpdate){
5695         if(isUpdate !== true){
5696             this.fireEvent("beforerowremoved", this, index, record);
5697         }
5698         var bt = this.mainBody.dom;
5699         
5700         var rows = this.el.select('tbody > tr', true).elements;
5701         
5702         if(typeof(rows[index]) != 'undefined'){
5703             bt.removeChild(rows[index].dom);
5704         }
5705         
5706 //        if(bt.rows[index]){
5707 //            bt.removeChild(bt.rows[index]);
5708 //        }
5709         
5710         if(isUpdate !== true){
5711             //this.stripeRows(index);
5712             //this.syncRowHeights(index, index);
5713             //this.layout();
5714             this.fireEvent("rowremoved", this, index, record);
5715         }
5716     },
5717     
5718     onAdd : function(ds, records, rowIndex)
5719     {
5720         //Roo.log('on Add called');
5721         // - note this does not handle multiple adding very well..
5722         var bt = this.mainBody.dom;
5723         for (var i =0 ; i < records.length;i++) {
5724             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5725             //Roo.log(records[i]);
5726             //Roo.log(this.store.getAt(rowIndex+i));
5727             this.insertRow(this.store, rowIndex + i, false);
5728             return;
5729         }
5730         
5731     },
5732     
5733     
5734     refreshRow : function(record){
5735         var ds = this.store, index;
5736         if(typeof record == 'number'){
5737             index = record;
5738             record = ds.getAt(index);
5739         }else{
5740             index = ds.indexOf(record);
5741         }
5742         this.insertRow(ds, index, true);
5743         this.onRemove(ds, record, index+1, true);
5744         //this.syncRowHeights(index, index);
5745         //this.layout();
5746         this.fireEvent("rowupdated", this, index, record);
5747     },
5748     
5749     insertRow : function(dm, rowIndex, isUpdate){
5750         
5751         if(!isUpdate){
5752             this.fireEvent("beforerowsinserted", this, rowIndex);
5753         }
5754             //var s = this.getScrollState();
5755         var row = this.renderRow(this.cm, this.store, rowIndex);
5756         // insert before rowIndex..
5757         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5758         
5759         var _this = this;
5760                 
5761         if(row.cellObjects.length){
5762             Roo.each(row.cellObjects, function(r){
5763                 _this.renderCellObject(r);
5764             })
5765         }
5766             
5767         if(!isUpdate){
5768             this.fireEvent("rowsinserted", this, rowIndex);
5769             //this.syncRowHeights(firstRow, lastRow);
5770             //this.stripeRows(firstRow);
5771             //this.layout();
5772         }
5773         
5774     },
5775     
5776     
5777     getRowDom : function(rowIndex)
5778     {
5779         var rows = this.el.select('tbody > tr', true).elements;
5780         
5781         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
5782         
5783     },
5784     // returns the object tree for a tr..
5785   
5786     
5787     renderRow : function(cm, ds, rowIndex) 
5788     {
5789         
5790         var d = ds.getAt(rowIndex);
5791         
5792         var row = {
5793             tag : 'tr',
5794             cn : []
5795         };
5796             
5797         var cellObjects = [];
5798         
5799         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5800             var config = cm.config[i];
5801             
5802             var renderer = cm.getRenderer(i);
5803             var value = '';
5804             var id = false;
5805             
5806             if(typeof(renderer) !== 'undefined'){
5807                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5808             }
5809             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5810             // and are rendered into the cells after the row is rendered - using the id for the element.
5811             
5812             if(typeof(value) === 'object'){
5813                 id = Roo.id();
5814                 cellObjects.push({
5815                     container : id,
5816                     cfg : value 
5817                 })
5818             }
5819             
5820             var rowcfg = {
5821                 record: d,
5822                 rowIndex : rowIndex,
5823                 colIndex : i,
5824                 rowClass : ''
5825             }
5826
5827             this.fireEvent('rowclass', this, rowcfg);
5828             
5829             var td = {
5830                 tag: 'td',
5831                 cls : rowcfg.rowClass,
5832                 style: '',
5833                 html: (typeof(value) === 'object') ? '' : value
5834             };
5835             
5836             if (id) {
5837                 td.id = id;
5838             }
5839             
5840             if(typeof(config.colspan) != 'undefined'){
5841                 td.colspan = config.colspan;
5842             }
5843             
5844             if(typeof(config.hidden) != 'undefined' && config.hidden){
5845                 td.style += ' display:none;';
5846             }
5847             
5848             if(typeof(config.align) != 'undefined' && config.align.length){
5849                 td.style += ' text-align:' + config.align + ';';
5850             }
5851             
5852             if(typeof(config.width) != 'undefined'){
5853                 td.style += ' width:' +  config.width + 'px;';
5854             }
5855             
5856             if(typeof(config.cursor) != 'undefined'){
5857                 td.style += ' cursor:' +  config.cursor + ';';
5858             }
5859             
5860             if(typeof(config.cls) != 'undefined'){
5861                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
5862             }
5863              
5864             row.cn.push(td);
5865            
5866         }
5867         
5868         row.cellObjects = cellObjects;
5869         
5870         return row;
5871           
5872     },
5873     
5874     
5875     
5876     onBeforeLoad : function()
5877     {
5878         //Roo.log('ds onBeforeLoad');
5879         
5880         //this.clear();
5881         
5882         //if(this.loadMask){
5883         //    this.maskEl.show();
5884         //}
5885     },
5886      /**
5887      * Remove all rows
5888      */
5889     clear : function()
5890     {
5891         this.el.select('tbody', true).first().dom.innerHTML = '';
5892     },
5893     /**
5894      * Show or hide a row.
5895      * @param {Number} rowIndex to show or hide
5896      * @param {Boolean} state hide
5897      */
5898     setRowVisibility : function(rowIndex, state)
5899     {
5900         var bt = this.mainBody.dom;
5901         
5902         var rows = this.el.select('tbody > tr', true).elements;
5903         
5904         if(typeof(rows[rowIndex]) == 'undefined'){
5905             return;
5906         }
5907         rows[rowIndex].dom.style.display = state ? '' : 'none';
5908     },
5909     
5910     
5911     getSelectionModel : function(){
5912         if(!this.selModel){
5913             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5914         }
5915         return this.selModel;
5916     },
5917     /*
5918      * Render the Roo.bootstrap object from renderder
5919      */
5920     renderCellObject : function(r)
5921     {
5922         var _this = this;
5923         
5924         var t = r.cfg.render(r.container);
5925         
5926         if(r.cfg.cn){
5927             Roo.each(r.cfg.cn, function(c){
5928                 var child = {
5929                     container: t.getChildContainer(),
5930                     cfg: c
5931                 }
5932                 _this.renderCellObject(child);
5933             })
5934         }
5935     },
5936     
5937     getRowIndex : function(row)
5938     {
5939         var rowIndex = -1;
5940         
5941         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
5942             if(el != row){
5943                 return;
5944             }
5945             
5946             rowIndex = index;
5947         });
5948         
5949         return rowIndex;
5950     }
5951    
5952 });
5953
5954  
5955
5956  /*
5957  * - LGPL
5958  *
5959  * table cell
5960  * 
5961  */
5962
5963 /**
5964  * @class Roo.bootstrap.TableCell
5965  * @extends Roo.bootstrap.Component
5966  * Bootstrap TableCell class
5967  * @cfg {String} html cell contain text
5968  * @cfg {String} cls cell class
5969  * @cfg {String} tag cell tag (td|th) default td
5970  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5971  * @cfg {String} align Aligns the content in a cell
5972  * @cfg {String} axis Categorizes cells
5973  * @cfg {String} bgcolor Specifies the background color of a cell
5974  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5975  * @cfg {Number} colspan Specifies the number of columns a cell should span
5976  * @cfg {String} headers Specifies one or more header cells a cell is related to
5977  * @cfg {Number} height Sets the height of a cell
5978  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5979  * @cfg {Number} rowspan Sets the number of rows a cell should span
5980  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5981  * @cfg {String} valign Vertical aligns the content in a cell
5982  * @cfg {Number} width Specifies the width of a cell
5983  * 
5984  * @constructor
5985  * Create a new TableCell
5986  * @param {Object} config The config object
5987  */
5988
5989 Roo.bootstrap.TableCell = function(config){
5990     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5991 };
5992
5993 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5994     
5995     html: false,
5996     cls: false,
5997     tag: false,
5998     abbr: false,
5999     align: false,
6000     axis: false,
6001     bgcolor: false,
6002     charoff: false,
6003     colspan: false,
6004     headers: false,
6005     height: false,
6006     nowrap: false,
6007     rowspan: false,
6008     scope: false,
6009     valign: false,
6010     width: false,
6011     
6012     
6013     getAutoCreate : function(){
6014         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6015         
6016         cfg = {
6017             tag: 'td'
6018         }
6019         
6020         if(this.tag){
6021             cfg.tag = this.tag;
6022         }
6023         
6024         if (this.html) {
6025             cfg.html=this.html
6026         }
6027         if (this.cls) {
6028             cfg.cls=this.cls
6029         }
6030         if (this.abbr) {
6031             cfg.abbr=this.abbr
6032         }
6033         if (this.align) {
6034             cfg.align=this.align
6035         }
6036         if (this.axis) {
6037             cfg.axis=this.axis
6038         }
6039         if (this.bgcolor) {
6040             cfg.bgcolor=this.bgcolor
6041         }
6042         if (this.charoff) {
6043             cfg.charoff=this.charoff
6044         }
6045         if (this.colspan) {
6046             cfg.colspan=this.colspan
6047         }
6048         if (this.headers) {
6049             cfg.headers=this.headers
6050         }
6051         if (this.height) {
6052             cfg.height=this.height
6053         }
6054         if (this.nowrap) {
6055             cfg.nowrap=this.nowrap
6056         }
6057         if (this.rowspan) {
6058             cfg.rowspan=this.rowspan
6059         }
6060         if (this.scope) {
6061             cfg.scope=this.scope
6062         }
6063         if (this.valign) {
6064             cfg.valign=this.valign
6065         }
6066         if (this.width) {
6067             cfg.width=this.width
6068         }
6069         
6070         
6071         return cfg;
6072     }
6073    
6074 });
6075
6076  
6077
6078  /*
6079  * - LGPL
6080  *
6081  * table row
6082  * 
6083  */
6084
6085 /**
6086  * @class Roo.bootstrap.TableRow
6087  * @extends Roo.bootstrap.Component
6088  * Bootstrap TableRow class
6089  * @cfg {String} cls row class
6090  * @cfg {String} align Aligns the content in a table row
6091  * @cfg {String} bgcolor Specifies a background color for a table row
6092  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6093  * @cfg {String} valign Vertical aligns the content in a table row
6094  * 
6095  * @constructor
6096  * Create a new TableRow
6097  * @param {Object} config The config object
6098  */
6099
6100 Roo.bootstrap.TableRow = function(config){
6101     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6102 };
6103
6104 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6105     
6106     cls: false,
6107     align: false,
6108     bgcolor: false,
6109     charoff: false,
6110     valign: false,
6111     
6112     getAutoCreate : function(){
6113         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6114         
6115         cfg = {
6116             tag: 'tr'
6117         }
6118             
6119         if(this.cls){
6120             cfg.cls = this.cls;
6121         }
6122         if(this.align){
6123             cfg.align = this.align;
6124         }
6125         if(this.bgcolor){
6126             cfg.bgcolor = this.bgcolor;
6127         }
6128         if(this.charoff){
6129             cfg.charoff = this.charoff;
6130         }
6131         if(this.valign){
6132             cfg.valign = this.valign;
6133         }
6134         
6135         return cfg;
6136     }
6137    
6138 });
6139
6140  
6141
6142  /*
6143  * - LGPL
6144  *
6145  * table body
6146  * 
6147  */
6148
6149 /**
6150  * @class Roo.bootstrap.TableBody
6151  * @extends Roo.bootstrap.Component
6152  * Bootstrap TableBody class
6153  * @cfg {String} cls element class
6154  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6155  * @cfg {String} align Aligns the content inside the element
6156  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6157  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6158  * 
6159  * @constructor
6160  * Create a new TableBody
6161  * @param {Object} config The config object
6162  */
6163
6164 Roo.bootstrap.TableBody = function(config){
6165     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6166 };
6167
6168 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6169     
6170     cls: false,
6171     tag: false,
6172     align: false,
6173     charoff: false,
6174     valign: false,
6175     
6176     getAutoCreate : function(){
6177         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6178         
6179         cfg = {
6180             tag: 'tbody'
6181         }
6182             
6183         if (this.cls) {
6184             cfg.cls=this.cls
6185         }
6186         if(this.tag){
6187             cfg.tag = this.tag;
6188         }
6189         
6190         if(this.align){
6191             cfg.align = this.align;
6192         }
6193         if(this.charoff){
6194             cfg.charoff = this.charoff;
6195         }
6196         if(this.valign){
6197             cfg.valign = this.valign;
6198         }
6199         
6200         return cfg;
6201     }
6202     
6203     
6204 //    initEvents : function()
6205 //    {
6206 //        
6207 //        if(!this.store){
6208 //            return;
6209 //        }
6210 //        
6211 //        this.store = Roo.factory(this.store, Roo.data);
6212 //        this.store.on('load', this.onLoad, this);
6213 //        
6214 //        this.store.load();
6215 //        
6216 //    },
6217 //    
6218 //    onLoad: function () 
6219 //    {   
6220 //        this.fireEvent('load', this);
6221 //    }
6222 //    
6223 //   
6224 });
6225
6226  
6227
6228  /*
6229  * Based on:
6230  * Ext JS Library 1.1.1
6231  * Copyright(c) 2006-2007, Ext JS, LLC.
6232  *
6233  * Originally Released Under LGPL - original licence link has changed is not relivant.
6234  *
6235  * Fork - LGPL
6236  * <script type="text/javascript">
6237  */
6238
6239 // as we use this in bootstrap.
6240 Roo.namespace('Roo.form');
6241  /**
6242  * @class Roo.form.Action
6243  * Internal Class used to handle form actions
6244  * @constructor
6245  * @param {Roo.form.BasicForm} el The form element or its id
6246  * @param {Object} config Configuration options
6247  */
6248
6249  
6250  
6251 // define the action interface
6252 Roo.form.Action = function(form, options){
6253     this.form = form;
6254     this.options = options || {};
6255 };
6256 /**
6257  * Client Validation Failed
6258  * @const 
6259  */
6260 Roo.form.Action.CLIENT_INVALID = 'client';
6261 /**
6262  * Server Validation Failed
6263  * @const 
6264  */
6265 Roo.form.Action.SERVER_INVALID = 'server';
6266  /**
6267  * Connect to Server Failed
6268  * @const 
6269  */
6270 Roo.form.Action.CONNECT_FAILURE = 'connect';
6271 /**
6272  * Reading Data from Server Failed
6273  * @const 
6274  */
6275 Roo.form.Action.LOAD_FAILURE = 'load';
6276
6277 Roo.form.Action.prototype = {
6278     type : 'default',
6279     failureType : undefined,
6280     response : undefined,
6281     result : undefined,
6282
6283     // interface method
6284     run : function(options){
6285
6286     },
6287
6288     // interface method
6289     success : function(response){
6290
6291     },
6292
6293     // interface method
6294     handleResponse : function(response){
6295
6296     },
6297
6298     // default connection failure
6299     failure : function(response){
6300         
6301         this.response = response;
6302         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6303         this.form.afterAction(this, false);
6304     },
6305
6306     processResponse : function(response){
6307         this.response = response;
6308         if(!response.responseText){
6309             return true;
6310         }
6311         this.result = this.handleResponse(response);
6312         return this.result;
6313     },
6314
6315     // utility functions used internally
6316     getUrl : function(appendParams){
6317         var url = this.options.url || this.form.url || this.form.el.dom.action;
6318         if(appendParams){
6319             var p = this.getParams();
6320             if(p){
6321                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6322             }
6323         }
6324         return url;
6325     },
6326
6327     getMethod : function(){
6328         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6329     },
6330
6331     getParams : function(){
6332         var bp = this.form.baseParams;
6333         var p = this.options.params;
6334         if(p){
6335             if(typeof p == "object"){
6336                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6337             }else if(typeof p == 'string' && bp){
6338                 p += '&' + Roo.urlEncode(bp);
6339             }
6340         }else if(bp){
6341             p = Roo.urlEncode(bp);
6342         }
6343         return p;
6344     },
6345
6346     createCallback : function(){
6347         return {
6348             success: this.success,
6349             failure: this.failure,
6350             scope: this,
6351             timeout: (this.form.timeout*1000),
6352             upload: this.form.fileUpload ? this.success : undefined
6353         };
6354     }
6355 };
6356
6357 Roo.form.Action.Submit = function(form, options){
6358     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6359 };
6360
6361 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6362     type : 'submit',
6363
6364     haveProgress : false,
6365     uploadComplete : false,
6366     
6367     // uploadProgress indicator.
6368     uploadProgress : function()
6369     {
6370         if (!this.form.progressUrl) {
6371             return;
6372         }
6373         
6374         if (!this.haveProgress) {
6375             Roo.MessageBox.progress("Uploading", "Uploading");
6376         }
6377         if (this.uploadComplete) {
6378            Roo.MessageBox.hide();
6379            return;
6380         }
6381         
6382         this.haveProgress = true;
6383    
6384         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6385         
6386         var c = new Roo.data.Connection();
6387         c.request({
6388             url : this.form.progressUrl,
6389             params: {
6390                 id : uid
6391             },
6392             method: 'GET',
6393             success : function(req){
6394                //console.log(data);
6395                 var rdata = false;
6396                 var edata;
6397                 try  {
6398                    rdata = Roo.decode(req.responseText)
6399                 } catch (e) {
6400                     Roo.log("Invalid data from server..");
6401                     Roo.log(edata);
6402                     return;
6403                 }
6404                 if (!rdata || !rdata.success) {
6405                     Roo.log(rdata);
6406                     Roo.MessageBox.alert(Roo.encode(rdata));
6407                     return;
6408                 }
6409                 var data = rdata.data;
6410                 
6411                 if (this.uploadComplete) {
6412                    Roo.MessageBox.hide();
6413                    return;
6414                 }
6415                    
6416                 if (data){
6417                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6418                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6419                     );
6420                 }
6421                 this.uploadProgress.defer(2000,this);
6422             },
6423        
6424             failure: function(data) {
6425                 Roo.log('progress url failed ');
6426                 Roo.log(data);
6427             },
6428             scope : this
6429         });
6430            
6431     },
6432     
6433     
6434     run : function()
6435     {
6436         // run get Values on the form, so it syncs any secondary forms.
6437         this.form.getValues();
6438         
6439         var o = this.options;
6440         var method = this.getMethod();
6441         var isPost = method == 'POST';
6442         if(o.clientValidation === false || this.form.isValid()){
6443             
6444             if (this.form.progressUrl) {
6445                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6446                     (new Date() * 1) + '' + Math.random());
6447                     
6448             } 
6449             
6450             
6451             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6452                 form:this.form.el.dom,
6453                 url:this.getUrl(!isPost),
6454                 method: method,
6455                 params:isPost ? this.getParams() : null,
6456                 isUpload: this.form.fileUpload
6457             }));
6458             
6459             this.uploadProgress();
6460
6461         }else if (o.clientValidation !== false){ // client validation failed
6462             this.failureType = Roo.form.Action.CLIENT_INVALID;
6463             this.form.afterAction(this, false);
6464         }
6465     },
6466
6467     success : function(response)
6468     {
6469         this.uploadComplete= true;
6470         if (this.haveProgress) {
6471             Roo.MessageBox.hide();
6472         }
6473         
6474         
6475         var result = this.processResponse(response);
6476         if(result === true || result.success){
6477             this.form.afterAction(this, true);
6478             return;
6479         }
6480         if(result.errors){
6481             this.form.markInvalid(result.errors);
6482             this.failureType = Roo.form.Action.SERVER_INVALID;
6483         }
6484         this.form.afterAction(this, false);
6485     },
6486     failure : function(response)
6487     {
6488         this.uploadComplete= true;
6489         if (this.haveProgress) {
6490             Roo.MessageBox.hide();
6491         }
6492         
6493         this.response = response;
6494         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6495         this.form.afterAction(this, false);
6496     },
6497     
6498     handleResponse : function(response){
6499         if(this.form.errorReader){
6500             var rs = this.form.errorReader.read(response);
6501             var errors = [];
6502             if(rs.records){
6503                 for(var i = 0, len = rs.records.length; i < len; i++) {
6504                     var r = rs.records[i];
6505                     errors[i] = r.data;
6506                 }
6507             }
6508             if(errors.length < 1){
6509                 errors = null;
6510             }
6511             return {
6512                 success : rs.success,
6513                 errors : errors
6514             };
6515         }
6516         var ret = false;
6517         try {
6518             ret = Roo.decode(response.responseText);
6519         } catch (e) {
6520             ret = {
6521                 success: false,
6522                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6523                 errors : []
6524             };
6525         }
6526         return ret;
6527         
6528     }
6529 });
6530
6531
6532 Roo.form.Action.Load = function(form, options){
6533     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6534     this.reader = this.form.reader;
6535 };
6536
6537 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6538     type : 'load',
6539
6540     run : function(){
6541         
6542         Roo.Ajax.request(Roo.apply(
6543                 this.createCallback(), {
6544                     method:this.getMethod(),
6545                     url:this.getUrl(false),
6546                     params:this.getParams()
6547         }));
6548     },
6549
6550     success : function(response){
6551         
6552         var result = this.processResponse(response);
6553         if(result === true || !result.success || !result.data){
6554             this.failureType = Roo.form.Action.LOAD_FAILURE;
6555             this.form.afterAction(this, false);
6556             return;
6557         }
6558         this.form.clearInvalid();
6559         this.form.setValues(result.data);
6560         this.form.afterAction(this, true);
6561     },
6562
6563     handleResponse : function(response){
6564         if(this.form.reader){
6565             var rs = this.form.reader.read(response);
6566             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6567             return {
6568                 success : rs.success,
6569                 data : data
6570             };
6571         }
6572         return Roo.decode(response.responseText);
6573     }
6574 });
6575
6576 Roo.form.Action.ACTION_TYPES = {
6577     'load' : Roo.form.Action.Load,
6578     'submit' : Roo.form.Action.Submit
6579 };/*
6580  * - LGPL
6581  *
6582  * form
6583  * 
6584  */
6585
6586 /**
6587  * @class Roo.bootstrap.Form
6588  * @extends Roo.bootstrap.Component
6589  * Bootstrap Form class
6590  * @cfg {String} method  GET | POST (default POST)
6591  * @cfg {String} labelAlign top | left (default top)
6592  * @cfg {String} align left  | right - for navbars
6593  * @cfg {Boolean} loadMask load mask when submit (default true)
6594
6595  * 
6596  * @constructor
6597  * Create a new Form
6598  * @param {Object} config The config object
6599  */
6600
6601
6602 Roo.bootstrap.Form = function(config){
6603     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6604     this.addEvents({
6605         /**
6606          * @event clientvalidation
6607          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6608          * @param {Form} this
6609          * @param {Boolean} valid true if the form has passed client-side validation
6610          */
6611         clientvalidation: true,
6612         /**
6613          * @event beforeaction
6614          * Fires before any action is performed. Return false to cancel the action.
6615          * @param {Form} this
6616          * @param {Action} action The action to be performed
6617          */
6618         beforeaction: true,
6619         /**
6620          * @event actionfailed
6621          * Fires when an action fails.
6622          * @param {Form} this
6623          * @param {Action} action The action that failed
6624          */
6625         actionfailed : true,
6626         /**
6627          * @event actioncomplete
6628          * Fires when an action is completed.
6629          * @param {Form} this
6630          * @param {Action} action The action that completed
6631          */
6632         actioncomplete : true
6633     });
6634     
6635 };
6636
6637 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6638       
6639      /**
6640      * @cfg {String} method
6641      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6642      */
6643     method : 'POST',
6644     /**
6645      * @cfg {String} url
6646      * The URL to use for form actions if one isn't supplied in the action options.
6647      */
6648     /**
6649      * @cfg {Boolean} fileUpload
6650      * Set to true if this form is a file upload.
6651      */
6652      
6653     /**
6654      * @cfg {Object} baseParams
6655      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6656      */
6657       
6658     /**
6659      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6660      */
6661     timeout: 30,
6662     /**
6663      * @cfg {Sting} align (left|right) for navbar forms
6664      */
6665     align : 'left',
6666
6667     // private
6668     activeAction : null,
6669  
6670     /**
6671      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6672      * element by passing it or its id or mask the form itself by passing in true.
6673      * @type Mixed
6674      */
6675     waitMsgTarget : false,
6676     
6677     loadMask : true,
6678     
6679     getAutoCreate : function(){
6680         
6681         var cfg = {
6682             tag: 'form',
6683             method : this.method || 'POST',
6684             id : this.id || Roo.id(),
6685             cls : ''
6686         }
6687         if (this.parent().xtype.match(/^Nav/)) {
6688             cfg.cls = 'navbar-form navbar-' + this.align;
6689             
6690         }
6691         
6692         if (this.labelAlign == 'left' ) {
6693             cfg.cls += ' form-horizontal';
6694         }
6695         
6696         
6697         return cfg;
6698     },
6699     initEvents : function()
6700     {
6701         this.el.on('submit', this.onSubmit, this);
6702         // this was added as random key presses on the form where triggering form submit.
6703         this.el.on('keypress', function(e) {
6704             if (e.getCharCode() != 13) {
6705                 return true;
6706             }
6707             // we might need to allow it for textareas.. and some other items.
6708             // check e.getTarget().
6709             
6710             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6711                 return true;
6712             }
6713         
6714             Roo.log("keypress blocked");
6715             
6716             e.preventDefault();
6717             return false;
6718         });
6719         
6720     },
6721     // private
6722     onSubmit : function(e){
6723         e.stopEvent();
6724     },
6725     
6726      /**
6727      * Returns true if client-side validation on the form is successful.
6728      * @return Boolean
6729      */
6730     isValid : function(){
6731         var items = this.getItems();
6732         var valid = true;
6733         items.each(function(f){
6734            if(!f.validate()){
6735                valid = false;
6736                
6737            }
6738         });
6739         return valid;
6740     },
6741     /**
6742      * Returns true if any fields in this form have changed since their original load.
6743      * @return Boolean
6744      */
6745     isDirty : function(){
6746         var dirty = false;
6747         var items = this.getItems();
6748         items.each(function(f){
6749            if(f.isDirty()){
6750                dirty = true;
6751                return false;
6752            }
6753            return true;
6754         });
6755         return dirty;
6756     },
6757      /**
6758      * Performs a predefined action (submit or load) or custom actions you define on this form.
6759      * @param {String} actionName The name of the action type
6760      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6761      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6762      * accept other config options):
6763      * <pre>
6764 Property          Type             Description
6765 ----------------  ---------------  ----------------------------------------------------------------------------------
6766 url               String           The url for the action (defaults to the form's url)
6767 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6768 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6769 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6770                                    validate the form on the client (defaults to false)
6771      * </pre>
6772      * @return {BasicForm} this
6773      */
6774     doAction : function(action, options){
6775         if(typeof action == 'string'){
6776             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6777         }
6778         if(this.fireEvent('beforeaction', this, action) !== false){
6779             this.beforeAction(action);
6780             action.run.defer(100, action);
6781         }
6782         return this;
6783     },
6784     
6785     // private
6786     beforeAction : function(action){
6787         var o = action.options;
6788         
6789         if(this.loadMask){
6790             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6791         }
6792         // not really supported yet.. ??
6793         
6794         //if(this.waitMsgTarget === true){
6795         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6796         //}else if(this.waitMsgTarget){
6797         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6798         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6799         //}else {
6800         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6801        // }
6802          
6803     },
6804
6805     // private
6806     afterAction : function(action, success){
6807         this.activeAction = null;
6808         var o = action.options;
6809         
6810         //if(this.waitMsgTarget === true){
6811             this.el.unmask();
6812         //}else if(this.waitMsgTarget){
6813         //    this.waitMsgTarget.unmask();
6814         //}else{
6815         //    Roo.MessageBox.updateProgress(1);
6816         //    Roo.MessageBox.hide();
6817        // }
6818         // 
6819         if(success){
6820             if(o.reset){
6821                 this.reset();
6822             }
6823             Roo.callback(o.success, o.scope, [this, action]);
6824             this.fireEvent('actioncomplete', this, action);
6825             
6826         }else{
6827             
6828             // failure condition..
6829             // we have a scenario where updates need confirming.
6830             // eg. if a locking scenario exists..
6831             // we look for { errors : { needs_confirm : true }} in the response.
6832             if (
6833                 (typeof(action.result) != 'undefined')  &&
6834                 (typeof(action.result.errors) != 'undefined')  &&
6835                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6836            ){
6837                 var _t = this;
6838                 Roo.log("not supported yet");
6839                  /*
6840                 
6841                 Roo.MessageBox.confirm(
6842                     "Change requires confirmation",
6843                     action.result.errorMsg,
6844                     function(r) {
6845                         if (r != 'yes') {
6846                             return;
6847                         }
6848                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6849                     }
6850                     
6851                 );
6852                 */
6853                 
6854                 
6855                 return;
6856             }
6857             
6858             Roo.callback(o.failure, o.scope, [this, action]);
6859             // show an error message if no failed handler is set..
6860             if (!this.hasListener('actionfailed')) {
6861                 Roo.log("need to add dialog support");
6862                 /*
6863                 Roo.MessageBox.alert("Error",
6864                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6865                         action.result.errorMsg :
6866                         "Saving Failed, please check your entries or try again"
6867                 );
6868                 */
6869             }
6870             
6871             this.fireEvent('actionfailed', this, action);
6872         }
6873         
6874     },
6875     /**
6876      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6877      * @param {String} id The value to search for
6878      * @return Field
6879      */
6880     findField : function(id){
6881         var items = this.getItems();
6882         var field = items.get(id);
6883         if(!field){
6884              items.each(function(f){
6885                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6886                     field = f;
6887                     return false;
6888                 }
6889                 return true;
6890             });
6891         }
6892         return field || null;
6893     },
6894      /**
6895      * Mark fields in this form invalid in bulk.
6896      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6897      * @return {BasicForm} this
6898      */
6899     markInvalid : function(errors){
6900         if(errors instanceof Array){
6901             for(var i = 0, len = errors.length; i < len; i++){
6902                 var fieldError = errors[i];
6903                 var f = this.findField(fieldError.id);
6904                 if(f){
6905                     f.markInvalid(fieldError.msg);
6906                 }
6907             }
6908         }else{
6909             var field, id;
6910             for(id in errors){
6911                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6912                     field.markInvalid(errors[id]);
6913                 }
6914             }
6915         }
6916         //Roo.each(this.childForms || [], function (f) {
6917         //    f.markInvalid(errors);
6918         //});
6919         
6920         return this;
6921     },
6922
6923     /**
6924      * Set values for fields in this form in bulk.
6925      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6926      * @return {BasicForm} this
6927      */
6928     setValues : function(values){
6929         if(values instanceof Array){ // array of objects
6930             for(var i = 0, len = values.length; i < len; i++){
6931                 var v = values[i];
6932                 var f = this.findField(v.id);
6933                 if(f){
6934                     f.setValue(v.value);
6935                     if(this.trackResetOnLoad){
6936                         f.originalValue = f.getValue();
6937                     }
6938                 }
6939             }
6940         }else{ // object hash
6941             var field, id;
6942             for(id in values){
6943                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6944                     
6945                     if (field.setFromData && 
6946                         field.valueField && 
6947                         field.displayField &&
6948                         // combos' with local stores can 
6949                         // be queried via setValue()
6950                         // to set their value..
6951                         (field.store && !field.store.isLocal)
6952                         ) {
6953                         // it's a combo
6954                         var sd = { };
6955                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6956                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6957                         field.setFromData(sd);
6958                         
6959                     } else {
6960                         field.setValue(values[id]);
6961                     }
6962                     
6963                     
6964                     if(this.trackResetOnLoad){
6965                         field.originalValue = field.getValue();
6966                     }
6967                 }
6968             }
6969         }
6970          
6971         //Roo.each(this.childForms || [], function (f) {
6972         //    f.setValues(values);
6973         //});
6974                 
6975         return this;
6976     },
6977
6978     /**
6979      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6980      * they are returned as an array.
6981      * @param {Boolean} asString
6982      * @return {Object}
6983      */
6984     getValues : function(asString){
6985         //if (this.childForms) {
6986             // copy values from the child forms
6987         //    Roo.each(this.childForms, function (f) {
6988         //        this.setValues(f.getValues());
6989         //    }, this);
6990         //}
6991         
6992         
6993         
6994         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6995         if(asString === true){
6996             return fs;
6997         }
6998         return Roo.urlDecode(fs);
6999     },
7000     
7001     /**
7002      * Returns the fields in this form as an object with key/value pairs. 
7003      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7004      * @return {Object}
7005      */
7006     getFieldValues : function(with_hidden)
7007     {
7008         var items = this.getItems();
7009         var ret = {};
7010         items.each(function(f){
7011             if (!f.getName()) {
7012                 return;
7013             }
7014             var v = f.getValue();
7015             if (f.inputType =='radio') {
7016                 if (typeof(ret[f.getName()]) == 'undefined') {
7017                     ret[f.getName()] = ''; // empty..
7018                 }
7019                 
7020                 if (!f.el.dom.checked) {
7021                     return;
7022                     
7023                 }
7024                 v = f.el.dom.value;
7025                 
7026             }
7027             
7028             // not sure if this supported any more..
7029             if ((typeof(v) == 'object') && f.getRawValue) {
7030                 v = f.getRawValue() ; // dates..
7031             }
7032             // combo boxes where name != hiddenName...
7033             if (f.name != f.getName()) {
7034                 ret[f.name] = f.getRawValue();
7035             }
7036             ret[f.getName()] = v;
7037         });
7038         
7039         return ret;
7040     },
7041
7042     /**
7043      * Clears all invalid messages in this form.
7044      * @return {BasicForm} this
7045      */
7046     clearInvalid : function(){
7047         var items = this.getItems();
7048         
7049         items.each(function(f){
7050            f.clearInvalid();
7051         });
7052         
7053         
7054         
7055         return this;
7056     },
7057
7058     /**
7059      * Resets this form.
7060      * @return {BasicForm} this
7061      */
7062     reset : function(){
7063         var items = this.getItems();
7064         items.each(function(f){
7065             f.reset();
7066         });
7067         
7068         Roo.each(this.childForms || [], function (f) {
7069             f.reset();
7070         });
7071        
7072         
7073         return this;
7074     },
7075     getItems : function()
7076     {
7077         var r=new Roo.util.MixedCollection(false, function(o){
7078             return o.id || (o.id = Roo.id());
7079         });
7080         var iter = function(el) {
7081             if (el.inputEl) {
7082                 r.add(el);
7083             }
7084             if (!el.items) {
7085                 return;
7086             }
7087             Roo.each(el.items,function(e) {
7088                 iter(e);
7089             });
7090             
7091             
7092         };
7093         
7094         iter(this);
7095         return r;
7096         
7097         
7098         
7099         
7100     }
7101     
7102 });
7103
7104  
7105 /*
7106  * Based on:
7107  * Ext JS Library 1.1.1
7108  * Copyright(c) 2006-2007, Ext JS, LLC.
7109  *
7110  * Originally Released Under LGPL - original licence link has changed is not relivant.
7111  *
7112  * Fork - LGPL
7113  * <script type="text/javascript">
7114  */
7115 /**
7116  * @class Roo.form.VTypes
7117  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7118  * @singleton
7119  */
7120 Roo.form.VTypes = function(){
7121     // closure these in so they are only created once.
7122     var alpha = /^[a-zA-Z_]+$/;
7123     var alphanum = /^[a-zA-Z0-9_]+$/;
7124     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7125     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7126
7127     // All these messages and functions are configurable
7128     return {
7129         /**
7130          * The function used to validate email addresses
7131          * @param {String} value The email address
7132          */
7133         'email' : function(v){
7134             return email.test(v);
7135         },
7136         /**
7137          * The error text to display when the email validation function returns false
7138          * @type String
7139          */
7140         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7141         /**
7142          * The keystroke filter mask to be applied on email input
7143          * @type RegExp
7144          */
7145         'emailMask' : /[a-z0-9_\.\-@]/i,
7146
7147         /**
7148          * The function used to validate URLs
7149          * @param {String} value The URL
7150          */
7151         'url' : function(v){
7152             return url.test(v);
7153         },
7154         /**
7155          * The error text to display when the url validation function returns false
7156          * @type String
7157          */
7158         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7159         
7160         /**
7161          * The function used to validate alpha values
7162          * @param {String} value The value
7163          */
7164         'alpha' : function(v){
7165             return alpha.test(v);
7166         },
7167         /**
7168          * The error text to display when the alpha validation function returns false
7169          * @type String
7170          */
7171         'alphaText' : 'This field should only contain letters and _',
7172         /**
7173          * The keystroke filter mask to be applied on alpha input
7174          * @type RegExp
7175          */
7176         'alphaMask' : /[a-z_]/i,
7177
7178         /**
7179          * The function used to validate alphanumeric values
7180          * @param {String} value The value
7181          */
7182         'alphanum' : function(v){
7183             return alphanum.test(v);
7184         },
7185         /**
7186          * The error text to display when the alphanumeric validation function returns false
7187          * @type String
7188          */
7189         'alphanumText' : 'This field should only contain letters, numbers and _',
7190         /**
7191          * The keystroke filter mask to be applied on alphanumeric input
7192          * @type RegExp
7193          */
7194         'alphanumMask' : /[a-z0-9_]/i
7195     };
7196 }();/*
7197  * - LGPL
7198  *
7199  * Input
7200  * 
7201  */
7202
7203 /**
7204  * @class Roo.bootstrap.Input
7205  * @extends Roo.bootstrap.Component
7206  * Bootstrap Input class
7207  * @cfg {Boolean} disabled is it disabled
7208  * @cfg {String} fieldLabel - the label associated
7209  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7210  * @cfg {String} name name of the input
7211  * @cfg {string} fieldLabel - the label associated
7212  * @cfg {string}  inputType - input / file submit ...
7213  * @cfg {string} placeholder - placeholder to put in text.
7214  * @cfg {string}  before - input group add on before
7215  * @cfg {string} after - input group add on after
7216  * @cfg {string} size - (lg|sm) or leave empty..
7217  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7218  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7219  * @cfg {Number} md colspan out of 12 for computer-sized screens
7220  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7221  * @cfg {string} value default value of the input
7222  * @cfg {Number} labelWidth set the width of label (0-12)
7223  * @cfg {String} labelAlign (top|left)
7224  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7225  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7226
7227  * @cfg {String} align (left|center|right) Default left
7228  * 
7229  * 
7230  * 
7231  * @constructor
7232  * Create a new Input
7233  * @param {Object} config The config object
7234  */
7235
7236 Roo.bootstrap.Input = function(config){
7237     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7238    
7239         this.addEvents({
7240             /**
7241              * @event focus
7242              * Fires when this field receives input focus.
7243              * @param {Roo.form.Field} this
7244              */
7245             focus : true,
7246             /**
7247              * @event blur
7248              * Fires when this field loses input focus.
7249              * @param {Roo.form.Field} this
7250              */
7251             blur : true,
7252             /**
7253              * @event specialkey
7254              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7255              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7256              * @param {Roo.form.Field} this
7257              * @param {Roo.EventObject} e The event object
7258              */
7259             specialkey : true,
7260             /**
7261              * @event change
7262              * Fires just before the field blurs if the field value has changed.
7263              * @param {Roo.form.Field} this
7264              * @param {Mixed} newValue The new value
7265              * @param {Mixed} oldValue The original value
7266              */
7267             change : true,
7268             /**
7269              * @event invalid
7270              * Fires after the field has been marked as invalid.
7271              * @param {Roo.form.Field} this
7272              * @param {String} msg The validation message
7273              */
7274             invalid : true,
7275             /**
7276              * @event valid
7277              * Fires after the field has been validated with no errors.
7278              * @param {Roo.form.Field} this
7279              */
7280             valid : true,
7281              /**
7282              * @event keyup
7283              * Fires after the key up
7284              * @param {Roo.form.Field} this
7285              * @param {Roo.EventObject}  e The event Object
7286              */
7287             keyup : true
7288         });
7289 };
7290
7291 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7292      /**
7293      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7294       automatic validation (defaults to "keyup").
7295      */
7296     validationEvent : "keyup",
7297      /**
7298      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7299      */
7300     validateOnBlur : true,
7301     /**
7302      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7303      */
7304     validationDelay : 250,
7305      /**
7306      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7307      */
7308     focusClass : "x-form-focus",  // not needed???
7309     
7310        
7311     /**
7312      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7313      */
7314     invalidClass : "has-warning",
7315     
7316     /**
7317      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7318      */
7319     validClass : "has-success",
7320     
7321     /**
7322      * @cfg {Boolean} hasFeedback (true|false) default true
7323      */
7324     hasFeedback : true,
7325     
7326     /**
7327      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7328      */
7329     invalidFeedbackClass : "glyphicon-warning-sign",
7330     
7331     /**
7332      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7333      */
7334     validFeedbackClass : "glyphicon-ok",
7335     
7336     /**
7337      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7338      */
7339     selectOnFocus : false,
7340     
7341      /**
7342      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7343      */
7344     maskRe : null,
7345        /**
7346      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7347      */
7348     vtype : null,
7349     
7350       /**
7351      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7352      */
7353     disableKeyFilter : false,
7354     
7355        /**
7356      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7357      */
7358     disabled : false,
7359      /**
7360      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7361      */
7362     allowBlank : true,
7363     /**
7364      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7365      */
7366     blankText : "This field is required",
7367     
7368      /**
7369      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7370      */
7371     minLength : 0,
7372     /**
7373      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7374      */
7375     maxLength : Number.MAX_VALUE,
7376     /**
7377      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7378      */
7379     minLengthText : "The minimum length for this field is {0}",
7380     /**
7381      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7382      */
7383     maxLengthText : "The maximum length for this field is {0}",
7384   
7385     
7386     /**
7387      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7388      * If available, this function will be called only after the basic validators all return true, and will be passed the
7389      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7390      */
7391     validator : null,
7392     /**
7393      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7394      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7395      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7396      */
7397     regex : null,
7398     /**
7399      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7400      */
7401     regexText : "",
7402     
7403     autocomplete: false,
7404     
7405     
7406     fieldLabel : '',
7407     inputType : 'text',
7408     
7409     name : false,
7410     placeholder: false,
7411     before : false,
7412     after : false,
7413     size : false,
7414     hasFocus : false,
7415     preventMark: false,
7416     isFormField : true,
7417     value : '',
7418     labelWidth : 2,
7419     labelAlign : false,
7420     readOnly : false,
7421     align : false,
7422     formatedValue : false,
7423     
7424     parentLabelAlign : function()
7425     {
7426         var parent = this;
7427         while (parent.parent()) {
7428             parent = parent.parent();
7429             if (typeof(parent.labelAlign) !='undefined') {
7430                 return parent.labelAlign;
7431             }
7432         }
7433         return 'left';
7434         
7435     },
7436     
7437     getAutoCreate : function(){
7438         
7439         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7440         
7441         var id = Roo.id();
7442         
7443         var cfg = {};
7444         
7445         if(this.inputType != 'hidden'){
7446             cfg.cls = 'form-group' //input-group
7447         }
7448         
7449         var input =  {
7450             tag: 'input',
7451             id : id,
7452             type : this.inputType,
7453             value : this.value,
7454             cls : 'form-control',
7455             placeholder : this.placeholder || '',
7456             autocomplete : this.autocomplete || 'new-password'
7457         };
7458         
7459         
7460         if(this.align){
7461             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7462         }
7463         
7464         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7465             input.maxLength = this.maxLength;
7466         }
7467         
7468         if (this.disabled) {
7469             input.disabled=true;
7470         }
7471         
7472         if (this.readOnly) {
7473             input.readonly=true;
7474         }
7475         
7476         if (this.name) {
7477             input.name = this.name;
7478         }
7479         if (this.size) {
7480             input.cls += ' input-' + this.size;
7481         }
7482         var settings=this;
7483         ['xs','sm','md','lg'].map(function(size){
7484             if (settings[size]) {
7485                 cfg.cls += ' col-' + size + '-' + settings[size];
7486             }
7487         });
7488         
7489         var inputblock = input;
7490         
7491         var feedback = {
7492             tag: 'span',
7493             cls: 'glyphicon form-control-feedback'
7494         };
7495             
7496         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7497             
7498             inputblock = {
7499                 cls : 'has-feedback',
7500                 cn :  [
7501                     input,
7502                     feedback
7503                 ] 
7504             };  
7505         }
7506         
7507         if (this.before || this.after) {
7508             
7509             inputblock = {
7510                 cls : 'input-group',
7511                 cn :  [] 
7512             };
7513             
7514             if (this.before && typeof(this.before) == 'string') {
7515                 
7516                 inputblock.cn.push({
7517                     tag :'span',
7518                     cls : 'roo-input-before input-group-addon',
7519                     html : this.before
7520                 });
7521             }
7522             if (this.before && typeof(this.before) == 'object') {
7523                 this.before = Roo.factory(this.before);
7524                 Roo.log(this.before);
7525                 inputblock.cn.push({
7526                     tag :'span',
7527                     cls : 'roo-input-before input-group-' +
7528                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7529                 });
7530             }
7531             
7532             inputblock.cn.push(input);
7533             
7534             if (this.after && typeof(this.after) == 'string') {
7535                 inputblock.cn.push({
7536                     tag :'span',
7537                     cls : 'roo-input-after input-group-addon',
7538                     html : this.after
7539                 });
7540             }
7541             if (this.after && typeof(this.after) == 'object') {
7542                 this.after = Roo.factory(this.after);
7543                 Roo.log(this.after);
7544                 inputblock.cn.push({
7545                     tag :'span',
7546                     cls : 'roo-input-after input-group-' +
7547                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7548                 });
7549             }
7550             
7551             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7552                 inputblock.cls += ' has-feedback';
7553                 inputblock.cn.push(feedback);
7554             }
7555         };
7556         
7557         if (align ==='left' && this.fieldLabel.length) {
7558                 Roo.log("left and has label");
7559                 cfg.cn = [
7560                     
7561                     {
7562                         tag: 'label',
7563                         'for' :  id,
7564                         cls : 'control-label col-sm-' + this.labelWidth,
7565                         html : this.fieldLabel
7566                         
7567                     },
7568                     {
7569                         cls : "col-sm-" + (12 - this.labelWidth), 
7570                         cn: [
7571                             inputblock
7572                         ]
7573                     }
7574                     
7575                 ];
7576         } else if ( this.fieldLabel.length) {
7577                 Roo.log(" label");
7578                  cfg.cn = [
7579                    
7580                     {
7581                         tag: 'label',
7582                         //cls : 'input-group-addon',
7583                         html : this.fieldLabel
7584                         
7585                     },
7586                     
7587                     inputblock
7588                     
7589                 ];
7590
7591         } else {
7592             
7593                 Roo.log(" no label && no align");
7594                 cfg.cn = [
7595                     
7596                         inputblock
7597                     
7598                 ];
7599                 
7600                 
7601         };
7602         Roo.log('input-parentType: ' + this.parentType);
7603         
7604         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7605            cfg.cls += ' navbar-form';
7606            Roo.log(cfg);
7607         }
7608         
7609         return cfg;
7610         
7611     },
7612     /**
7613      * return the real input element.
7614      */
7615     inputEl: function ()
7616     {
7617         return this.el.select('input.form-control',true).first();
7618     },
7619     
7620     tooltipEl : function()
7621     {
7622         return this.inputEl();
7623     },
7624     
7625     setDisabled : function(v)
7626     {
7627         var i  = this.inputEl().dom;
7628         if (!v) {
7629             i.removeAttribute('disabled');
7630             return;
7631             
7632         }
7633         i.setAttribute('disabled','true');
7634     },
7635     initEvents : function()
7636     {
7637           
7638         this.inputEl().on("keydown" , this.fireKey,  this);
7639         this.inputEl().on("focus", this.onFocus,  this);
7640         this.inputEl().on("blur", this.onBlur,  this);
7641         
7642         this.inputEl().relayEvent('keyup', this);
7643
7644         // reference to original value for reset
7645         this.originalValue = this.getValue();
7646         //Roo.form.TextField.superclass.initEvents.call(this);
7647         if(this.validationEvent == 'keyup'){
7648             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7649             this.inputEl().on('keyup', this.filterValidation, this);
7650         }
7651         else if(this.validationEvent !== false){
7652             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7653         }
7654         
7655         if(this.selectOnFocus){
7656             this.on("focus", this.preFocus, this);
7657             
7658         }
7659         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7660             this.inputEl().on("keypress", this.filterKeys, this);
7661         }
7662        /* if(this.grow){
7663             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7664             this.el.on("click", this.autoSize,  this);
7665         }
7666         */
7667         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7668             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7669         }
7670         
7671         if (typeof(this.before) == 'object') {
7672             this.before.render(this.el.select('.roo-input-before',true).first());
7673         }
7674         if (typeof(this.after) == 'object') {
7675             this.after.render(this.el.select('.roo-input-after',true).first());
7676         }
7677         
7678         
7679     },
7680     filterValidation : function(e){
7681         if(!e.isNavKeyPress()){
7682             this.validationTask.delay(this.validationDelay);
7683         }
7684     },
7685      /**
7686      * Validates the field value
7687      * @return {Boolean} True if the value is valid, else false
7688      */
7689     validate : function(){
7690         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7691         if(this.disabled || this.validateValue(this.getRawValue())){
7692             this.markValid();
7693             return true;
7694         }
7695         
7696         this.markInvalid();
7697         return false;
7698     },
7699     
7700     
7701     /**
7702      * Validates a value according to the field's validation rules and marks the field as invalid
7703      * if the validation fails
7704      * @param {Mixed} value The value to validate
7705      * @return {Boolean} True if the value is valid, else false
7706      */
7707     validateValue : function(value){
7708         if(value.length < 1)  { // if it's blank
7709             if(this.allowBlank){
7710                 return true;
7711             }
7712             return false;
7713         }
7714         
7715         if(value.length < this.minLength){
7716             return false;
7717         }
7718         if(value.length > this.maxLength){
7719             return false;
7720         }
7721         if(this.vtype){
7722             var vt = Roo.form.VTypes;
7723             if(!vt[this.vtype](value, this)){
7724                 return false;
7725             }
7726         }
7727         if(typeof this.validator == "function"){
7728             var msg = this.validator(value);
7729             if(msg !== true){
7730                 return false;
7731             }
7732         }
7733         
7734         if(this.regex && !this.regex.test(value)){
7735             return false;
7736         }
7737         
7738         return true;
7739     },
7740
7741     
7742     
7743      // private
7744     fireKey : function(e){
7745         //Roo.log('field ' + e.getKey());
7746         if(e.isNavKeyPress()){
7747             this.fireEvent("specialkey", this, e);
7748         }
7749     },
7750     focus : function (selectText){
7751         if(this.rendered){
7752             this.inputEl().focus();
7753             if(selectText === true){
7754                 this.inputEl().dom.select();
7755             }
7756         }
7757         return this;
7758     } ,
7759     
7760     onFocus : function(){
7761         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7762            // this.el.addClass(this.focusClass);
7763         }
7764         if(!this.hasFocus){
7765             this.hasFocus = true;
7766             this.startValue = this.getValue();
7767             this.fireEvent("focus", this);
7768         }
7769     },
7770     
7771     beforeBlur : Roo.emptyFn,
7772
7773     
7774     // private
7775     onBlur : function(){
7776         this.beforeBlur();
7777         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7778             //this.el.removeClass(this.focusClass);
7779         }
7780         this.hasFocus = false;
7781         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7782             this.validate();
7783         }
7784         var v = this.getValue();
7785         if(String(v) !== String(this.startValue)){
7786             this.fireEvent('change', this, v, this.startValue);
7787         }
7788         this.fireEvent("blur", this);
7789     },
7790     
7791     /**
7792      * Resets the current field value to the originally loaded value and clears any validation messages
7793      */
7794     reset : function(){
7795         this.setValue(this.originalValue);
7796         this.validate();
7797     },
7798      /**
7799      * Returns the name of the field
7800      * @return {Mixed} name The name field
7801      */
7802     getName: function(){
7803         return this.name;
7804     },
7805      /**
7806      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7807      * @return {Mixed} value The field value
7808      */
7809     getValue : function(){
7810         
7811         var v = this.inputEl().getValue();
7812         
7813         return v;
7814     },
7815     /**
7816      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7817      * @return {Mixed} value The field value
7818      */
7819     getRawValue : function(){
7820         var v = this.inputEl().getValue();
7821         
7822         return v;
7823     },
7824     
7825     /**
7826      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7827      * @param {Mixed} value The value to set
7828      */
7829     setRawValue : function(v){
7830         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7831     },
7832     
7833     selectText : function(start, end){
7834         var v = this.getRawValue();
7835         if(v.length > 0){
7836             start = start === undefined ? 0 : start;
7837             end = end === undefined ? v.length : end;
7838             var d = this.inputEl().dom;
7839             if(d.setSelectionRange){
7840                 d.setSelectionRange(start, end);
7841             }else if(d.createTextRange){
7842                 var range = d.createTextRange();
7843                 range.moveStart("character", start);
7844                 range.moveEnd("character", v.length-end);
7845                 range.select();
7846             }
7847         }
7848     },
7849     
7850     /**
7851      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7852      * @param {Mixed} value The value to set
7853      */
7854     setValue : function(v){
7855         this.value = v;
7856         if(this.rendered){
7857             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7858             this.validate();
7859         }
7860     },
7861     
7862     /*
7863     processValue : function(value){
7864         if(this.stripCharsRe){
7865             var newValue = value.replace(this.stripCharsRe, '');
7866             if(newValue !== value){
7867                 this.setRawValue(newValue);
7868                 return newValue;
7869             }
7870         }
7871         return value;
7872     },
7873   */
7874     preFocus : function(){
7875         
7876         if(this.selectOnFocus){
7877             this.inputEl().dom.select();
7878         }
7879     },
7880     filterKeys : function(e){
7881         var k = e.getKey();
7882         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7883             return;
7884         }
7885         var c = e.getCharCode(), cc = String.fromCharCode(c);
7886         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7887             return;
7888         }
7889         if(!this.maskRe.test(cc)){
7890             e.stopEvent();
7891         }
7892     },
7893      /**
7894      * Clear any invalid styles/messages for this field
7895      */
7896     clearInvalid : function(){
7897         
7898         if(!this.el || this.preventMark){ // not rendered
7899             return;
7900         }
7901         this.el.removeClass(this.invalidClass);
7902         
7903         this.fireEvent('valid', this);
7904     },
7905     
7906      /**
7907      * Mark this field as valid
7908      */
7909     markValid : function(){
7910         if(!this.el  || this.preventMark){ // not rendered
7911             return;
7912         }
7913         
7914         this.el.removeClass([this.invalidClass, this.validClass]);
7915         
7916         if(this.disabled || this.allowBlank){
7917             return;
7918         }
7919         
7920         this.el.addClass(this.validClass);
7921         
7922         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
7923             
7924             var feedback = this.el.select('.form-control-feedback', true).first();
7925             
7926             if(feedback){
7927                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
7928                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
7929             }
7930             
7931         }
7932         
7933         this.fireEvent('valid', this);
7934     },
7935     
7936      /**
7937      * Mark this field as invalid
7938      * @param {String} msg The validation message
7939      */
7940     markInvalid : function(msg){
7941         if(!this.el  || this.preventMark){ // not rendered
7942             return;
7943         }
7944         
7945         this.el.removeClass([this.invalidClass, this.validClass]);
7946         
7947         if(this.disabled || this.allowBlank){
7948             return;
7949         }
7950         
7951         this.el.addClass(this.invalidClass);
7952         
7953         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7954             
7955             var feedback = this.el.select('.form-control-feedback', true).first();
7956             
7957             if(feedback){
7958                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
7959                 
7960                 if(this.getValue().length){
7961                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
7962                 }
7963                 
7964             }
7965             
7966         }
7967         
7968         this.fireEvent('invalid', this, msg);
7969     },
7970     // private
7971     SafariOnKeyDown : function(event)
7972     {
7973         // this is a workaround for a password hang bug on chrome/ webkit.
7974         
7975         var isSelectAll = false;
7976         
7977         if(this.inputEl().dom.selectionEnd > 0){
7978             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7979         }
7980         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7981             event.preventDefault();
7982             this.setValue('');
7983             return;
7984         }
7985         
7986         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
7987             
7988             event.preventDefault();
7989             // this is very hacky as keydown always get's upper case.
7990             //
7991             var cc = String.fromCharCode(event.getCharCode());
7992             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7993             
7994         }
7995     },
7996     adjustWidth : function(tag, w){
7997         tag = tag.toLowerCase();
7998         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7999             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8000                 if(tag == 'input'){
8001                     return w + 2;
8002                 }
8003                 if(tag == 'textarea'){
8004                     return w-2;
8005                 }
8006             }else if(Roo.isOpera){
8007                 if(tag == 'input'){
8008                     return w + 2;
8009                 }
8010                 if(tag == 'textarea'){
8011                     return w-2;
8012                 }
8013             }
8014         }
8015         return w;
8016     }
8017     
8018 });
8019
8020  
8021 /*
8022  * - LGPL
8023  *
8024  * Input
8025  * 
8026  */
8027
8028 /**
8029  * @class Roo.bootstrap.TextArea
8030  * @extends Roo.bootstrap.Input
8031  * Bootstrap TextArea class
8032  * @cfg {Number} cols Specifies the visible width of a text area
8033  * @cfg {Number} rows Specifies the visible number of lines in a text area
8034  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8035  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8036  * @cfg {string} html text
8037  * 
8038  * @constructor
8039  * Create a new TextArea
8040  * @param {Object} config The config object
8041  */
8042
8043 Roo.bootstrap.TextArea = function(config){
8044     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8045    
8046 };
8047
8048 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8049      
8050     cols : false,
8051     rows : 5,
8052     readOnly : false,
8053     warp : 'soft',
8054     resize : false,
8055     value: false,
8056     html: false,
8057     
8058     getAutoCreate : function(){
8059         
8060         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8061         
8062         var id = Roo.id();
8063         
8064         var cfg = {};
8065         
8066         var input =  {
8067             tag: 'textarea',
8068             id : id,
8069             warp : this.warp,
8070             rows : this.rows,
8071             value : this.value || '',
8072             html: this.html || '',
8073             cls : 'form-control',
8074             placeholder : this.placeholder || '' 
8075             
8076         };
8077         
8078         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8079             input.maxLength = this.maxLength;
8080         }
8081         
8082         if(this.resize){
8083             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8084         }
8085         
8086         if(this.cols){
8087             input.cols = this.cols;
8088         }
8089         
8090         if (this.readOnly) {
8091             input.readonly = true;
8092         }
8093         
8094         if (this.name) {
8095             input.name = this.name;
8096         }
8097         
8098         if (this.size) {
8099             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8100         }
8101         
8102         var settings=this;
8103         ['xs','sm','md','lg'].map(function(size){
8104             if (settings[size]) {
8105                 cfg.cls += ' col-' + size + '-' + settings[size];
8106             }
8107         });
8108         
8109         var inputblock = input;
8110         
8111         if(this.hasFeedback && !this.allowBlank){
8112             
8113             var feedback = {
8114                 tag: 'span',
8115                 cls: 'glyphicon form-control-feedback'
8116             };
8117
8118             inputblock = {
8119                 cls : 'has-feedback',
8120                 cn :  [
8121                     input,
8122                     feedback
8123                 ] 
8124             };  
8125         }
8126         
8127         
8128         if (this.before || this.after) {
8129             
8130             inputblock = {
8131                 cls : 'input-group',
8132                 cn :  [] 
8133             };
8134             if (this.before) {
8135                 inputblock.cn.push({
8136                     tag :'span',
8137                     cls : 'input-group-addon',
8138                     html : this.before
8139                 });
8140             }
8141             
8142             inputblock.cn.push(input);
8143             
8144             if(this.hasFeedback && !this.allowBlank){
8145                 inputblock.cls += ' has-feedback';
8146                 inputblock.cn.push(feedback);
8147             }
8148             
8149             if (this.after) {
8150                 inputblock.cn.push({
8151                     tag :'span',
8152                     cls : 'input-group-addon',
8153                     html : this.after
8154                 });
8155             }
8156             
8157         }
8158         
8159         if (align ==='left' && this.fieldLabel.length) {
8160                 Roo.log("left and has label");
8161                 cfg.cn = [
8162                     
8163                     {
8164                         tag: 'label',
8165                         'for' :  id,
8166                         cls : 'control-label col-sm-' + this.labelWidth,
8167                         html : this.fieldLabel
8168                         
8169                     },
8170                     {
8171                         cls : "col-sm-" + (12 - this.labelWidth), 
8172                         cn: [
8173                             inputblock
8174                         ]
8175                     }
8176                     
8177                 ];
8178         } else if ( this.fieldLabel.length) {
8179                 Roo.log(" label");
8180                  cfg.cn = [
8181                    
8182                     {
8183                         tag: 'label',
8184                         //cls : 'input-group-addon',
8185                         html : this.fieldLabel
8186                         
8187                     },
8188                     
8189                     inputblock
8190                     
8191                 ];
8192
8193         } else {
8194             
8195                    Roo.log(" no label && no align");
8196                 cfg.cn = [
8197                     
8198                         inputblock
8199                     
8200                 ];
8201                 
8202                 
8203         }
8204         
8205         if (this.disabled) {
8206             input.disabled=true;
8207         }
8208         
8209         return cfg;
8210         
8211     },
8212     /**
8213      * return the real textarea element.
8214      */
8215     inputEl: function ()
8216     {
8217         return this.el.select('textarea.form-control',true).first();
8218     }
8219 });
8220
8221  
8222 /*
8223  * - LGPL
8224  *
8225  * trigger field - base class for combo..
8226  * 
8227  */
8228  
8229 /**
8230  * @class Roo.bootstrap.TriggerField
8231  * @extends Roo.bootstrap.Input
8232  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8233  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8234  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8235  * for which you can provide a custom implementation.  For example:
8236  * <pre><code>
8237 var trigger = new Roo.bootstrap.TriggerField();
8238 trigger.onTriggerClick = myTriggerFn;
8239 trigger.applyTo('my-field');
8240 </code></pre>
8241  *
8242  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8243  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8244  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8245  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8246  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8247
8248  * @constructor
8249  * Create a new TriggerField.
8250  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8251  * to the base TextField)
8252  */
8253 Roo.bootstrap.TriggerField = function(config){
8254     this.mimicing = false;
8255     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8256 };
8257
8258 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8259     /**
8260      * @cfg {String} triggerClass A CSS class to apply to the trigger
8261      */
8262      /**
8263      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8264      */
8265     hideTrigger:false,
8266
8267     /** @cfg {Boolean} grow @hide */
8268     /** @cfg {Number} growMin @hide */
8269     /** @cfg {Number} growMax @hide */
8270
8271     /**
8272      * @hide 
8273      * @method
8274      */
8275     autoSize: Roo.emptyFn,
8276     // private
8277     monitorTab : true,
8278     // private
8279     deferHeight : true,
8280
8281     
8282     actionMode : 'wrap',
8283     
8284     caret : false,
8285     
8286     
8287     getAutoCreate : function(){
8288        
8289         var align = this.labelAlign || this.parentLabelAlign();
8290         
8291         var id = Roo.id();
8292         
8293         var cfg = {
8294             cls: 'form-group' //input-group
8295         };
8296         
8297         
8298         var input =  {
8299             tag: 'input',
8300             id : id,
8301             type : this.inputType,
8302             cls : 'form-control',
8303             autocomplete: 'new-password',
8304             placeholder : this.placeholder || '' 
8305             
8306         };
8307         if (this.name) {
8308             input.name = this.name;
8309         }
8310         if (this.size) {
8311             input.cls += ' input-' + this.size;
8312         }
8313         
8314         if (this.disabled) {
8315             input.disabled=true;
8316         }
8317         
8318         var inputblock = input;
8319         
8320         if(this.hasFeedback && !this.allowBlank){
8321             
8322             var feedback = {
8323                 tag: 'span',
8324                 cls: 'glyphicon form-control-feedback'
8325             };
8326
8327             inputblock = {
8328                 cls : 'has-feedback',
8329                 cn :  [
8330                     input,
8331                     feedback
8332                 ] 
8333             };  
8334         }
8335         
8336         if (this.before || this.after) {
8337             
8338             inputblock = {
8339                 cls : 'input-group',
8340                 cn :  [] 
8341             };
8342             if (this.before) {
8343                 inputblock.cn.push({
8344                     tag :'span',
8345                     cls : 'input-group-addon',
8346                     html : this.before
8347                 });
8348             }
8349             
8350             inputblock.cn.push(input);
8351             
8352             if(this.hasFeedback && !this.allowBlank){
8353                 inputblock.cls += ' has-feedback';
8354                 inputblock.cn.push(feedback);
8355             }
8356             
8357             if (this.after) {
8358                 inputblock.cn.push({
8359                     tag :'span',
8360                     cls : 'input-group-addon',
8361                     html : this.after
8362                 });
8363             }
8364             
8365         };
8366         
8367         var box = {
8368             tag: 'div',
8369             cn: [
8370                 {
8371                     tag: 'input',
8372                     type : 'hidden',
8373                     cls: 'form-hidden-field'
8374                 },
8375                 inputblock
8376             ]
8377             
8378         };
8379         
8380         if(this.multiple){
8381             Roo.log('multiple');
8382             
8383             box = {
8384                 tag: 'div',
8385                 cn: [
8386                     {
8387                         tag: 'input',
8388                         type : 'hidden',
8389                         cls: 'form-hidden-field'
8390                     },
8391                     {
8392                         tag: 'ul',
8393                         cls: 'select2-choices',
8394                         cn:[
8395                             {
8396                                 tag: 'li',
8397                                 cls: 'select2-search-field',
8398                                 cn: [
8399
8400                                     inputblock
8401                                 ]
8402                             }
8403                         ]
8404                     }
8405                 ]
8406             }
8407         };
8408         
8409         var combobox = {
8410             cls: 'select2-container input-group',
8411             cn: [
8412                 box
8413 //                {
8414 //                    tag: 'ul',
8415 //                    cls: 'typeahead typeahead-long dropdown-menu',
8416 //                    style: 'display:none'
8417 //                }
8418             ]
8419         };
8420         
8421         if(!this.multiple && this.showToggleBtn){
8422             
8423             var caret = {
8424                         tag: 'span',
8425                         cls: 'caret'
8426              };
8427             if (this.caret != false) {
8428                 caret = {
8429                      tag: 'i',
8430                      cls: 'fa fa-' + this.caret
8431                 };
8432                 
8433             }
8434             
8435             combobox.cn.push({
8436                 tag :'span',
8437                 cls : 'input-group-addon btn dropdown-toggle',
8438                 cn : [
8439                     caret,
8440                     {
8441                         tag: 'span',
8442                         cls: 'combobox-clear',
8443                         cn  : [
8444                             {
8445                                 tag : 'i',
8446                                 cls: 'icon-remove'
8447                             }
8448                         ]
8449                     }
8450                 ]
8451
8452             })
8453         }
8454         
8455         if(this.multiple){
8456             combobox.cls += ' select2-container-multi';
8457         }
8458         
8459         if (align ==='left' && this.fieldLabel.length) {
8460             
8461                 Roo.log("left and has label");
8462                 cfg.cn = [
8463                     
8464                     {
8465                         tag: 'label',
8466                         'for' :  id,
8467                         cls : 'control-label col-sm-' + this.labelWidth,
8468                         html : this.fieldLabel
8469                         
8470                     },
8471                     {
8472                         cls : "col-sm-" + (12 - this.labelWidth), 
8473                         cn: [
8474                             combobox
8475                         ]
8476                     }
8477                     
8478                 ];
8479         } else if ( this.fieldLabel.length) {
8480                 Roo.log(" label");
8481                  cfg.cn = [
8482                    
8483                     {
8484                         tag: 'label',
8485                         //cls : 'input-group-addon',
8486                         html : this.fieldLabel
8487                         
8488                     },
8489                     
8490                     combobox
8491                     
8492                 ];
8493
8494         } else {
8495             
8496                 Roo.log(" no label && no align");
8497                 cfg = combobox
8498                      
8499                 
8500         }
8501          
8502         var settings=this;
8503         ['xs','sm','md','lg'].map(function(size){
8504             if (settings[size]) {
8505                 cfg.cls += ' col-' + size + '-' + settings[size];
8506             }
8507         });
8508         
8509         return cfg;
8510         
8511     },
8512     
8513     
8514     
8515     // private
8516     onResize : function(w, h){
8517 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8518 //        if(typeof w == 'number'){
8519 //            var x = w - this.trigger.getWidth();
8520 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8521 //            this.trigger.setStyle('left', x+'px');
8522 //        }
8523     },
8524
8525     // private
8526     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8527
8528     // private
8529     getResizeEl : function(){
8530         return this.inputEl();
8531     },
8532
8533     // private
8534     getPositionEl : function(){
8535         return this.inputEl();
8536     },
8537
8538     // private
8539     alignErrorIcon : function(){
8540         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8541     },
8542
8543     // private
8544     initEvents : function(){
8545         
8546         this.createList();
8547         
8548         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8549         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8550         if(!this.multiple && this.showToggleBtn){
8551             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8552             if(this.hideTrigger){
8553                 this.trigger.setDisplayed(false);
8554             }
8555             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8556         }
8557         
8558         if(this.multiple){
8559             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8560         }
8561         
8562         //this.trigger.addClassOnOver('x-form-trigger-over');
8563         //this.trigger.addClassOnClick('x-form-trigger-click');
8564         
8565         //if(!this.width){
8566         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8567         //}
8568     },
8569     
8570     createList : function()
8571     {
8572         this.list = Roo.get(document.body).createChild({
8573             tag: 'ul',
8574             cls: 'typeahead typeahead-long dropdown-menu',
8575             style: 'display:none'
8576         });
8577         
8578         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8579         
8580     },
8581
8582     // private
8583     initTrigger : function(){
8584        
8585     },
8586
8587     // private
8588     onDestroy : function(){
8589         if(this.trigger){
8590             this.trigger.removeAllListeners();
8591           //  this.trigger.remove();
8592         }
8593         //if(this.wrap){
8594         //    this.wrap.remove();
8595         //}
8596         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8597     },
8598
8599     // private
8600     onFocus : function(){
8601         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8602         /*
8603         if(!this.mimicing){
8604             this.wrap.addClass('x-trigger-wrap-focus');
8605             this.mimicing = true;
8606             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8607             if(this.monitorTab){
8608                 this.el.on("keydown", this.checkTab, this);
8609             }
8610         }
8611         */
8612     },
8613
8614     // private
8615     checkTab : function(e){
8616         if(e.getKey() == e.TAB){
8617             this.triggerBlur();
8618         }
8619     },
8620
8621     // private
8622     onBlur : function(){
8623         // do nothing
8624     },
8625
8626     // private
8627     mimicBlur : function(e, t){
8628         /*
8629         if(!this.wrap.contains(t) && this.validateBlur()){
8630             this.triggerBlur();
8631         }
8632         */
8633     },
8634
8635     // private
8636     triggerBlur : function(){
8637         this.mimicing = false;
8638         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8639         if(this.monitorTab){
8640             this.el.un("keydown", this.checkTab, this);
8641         }
8642         //this.wrap.removeClass('x-trigger-wrap-focus');
8643         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8644     },
8645
8646     // private
8647     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8648     validateBlur : function(e, t){
8649         return true;
8650     },
8651
8652     // private
8653     onDisable : function(){
8654         this.inputEl().dom.disabled = true;
8655         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8656         //if(this.wrap){
8657         //    this.wrap.addClass('x-item-disabled');
8658         //}
8659     },
8660
8661     // private
8662     onEnable : function(){
8663         this.inputEl().dom.disabled = false;
8664         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8665         //if(this.wrap){
8666         //    this.el.removeClass('x-item-disabled');
8667         //}
8668     },
8669
8670     // private
8671     onShow : function(){
8672         var ae = this.getActionEl();
8673         
8674         if(ae){
8675             ae.dom.style.display = '';
8676             ae.dom.style.visibility = 'visible';
8677         }
8678     },
8679
8680     // private
8681     
8682     onHide : function(){
8683         var ae = this.getActionEl();
8684         ae.dom.style.display = 'none';
8685     },
8686
8687     /**
8688      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8689      * by an implementing function.
8690      * @method
8691      * @param {EventObject} e
8692      */
8693     onTriggerClick : Roo.emptyFn
8694 });
8695  /*
8696  * Based on:
8697  * Ext JS Library 1.1.1
8698  * Copyright(c) 2006-2007, Ext JS, LLC.
8699  *
8700  * Originally Released Under LGPL - original licence link has changed is not relivant.
8701  *
8702  * Fork - LGPL
8703  * <script type="text/javascript">
8704  */
8705
8706
8707 /**
8708  * @class Roo.data.SortTypes
8709  * @singleton
8710  * Defines the default sorting (casting?) comparison functions used when sorting data.
8711  */
8712 Roo.data.SortTypes = {
8713     /**
8714      * Default sort that does nothing
8715      * @param {Mixed} s The value being converted
8716      * @return {Mixed} The comparison value
8717      */
8718     none : function(s){
8719         return s;
8720     },
8721     
8722     /**
8723      * The regular expression used to strip tags
8724      * @type {RegExp}
8725      * @property
8726      */
8727     stripTagsRE : /<\/?[^>]+>/gi,
8728     
8729     /**
8730      * Strips all HTML tags to sort on text only
8731      * @param {Mixed} s The value being converted
8732      * @return {String} The comparison value
8733      */
8734     asText : function(s){
8735         return String(s).replace(this.stripTagsRE, "");
8736     },
8737     
8738     /**
8739      * Strips all HTML tags to sort on text only - Case insensitive
8740      * @param {Mixed} s The value being converted
8741      * @return {String} The comparison value
8742      */
8743     asUCText : function(s){
8744         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8745     },
8746     
8747     /**
8748      * Case insensitive string
8749      * @param {Mixed} s The value being converted
8750      * @return {String} The comparison value
8751      */
8752     asUCString : function(s) {
8753         return String(s).toUpperCase();
8754     },
8755     
8756     /**
8757      * Date sorting
8758      * @param {Mixed} s The value being converted
8759      * @return {Number} The comparison value
8760      */
8761     asDate : function(s) {
8762         if(!s){
8763             return 0;
8764         }
8765         if(s instanceof Date){
8766             return s.getTime();
8767         }
8768         return Date.parse(String(s));
8769     },
8770     
8771     /**
8772      * Float sorting
8773      * @param {Mixed} s The value being converted
8774      * @return {Float} The comparison value
8775      */
8776     asFloat : function(s) {
8777         var val = parseFloat(String(s).replace(/,/g, ""));
8778         if(isNaN(val)) val = 0;
8779         return val;
8780     },
8781     
8782     /**
8783      * Integer sorting
8784      * @param {Mixed} s The value being converted
8785      * @return {Number} The comparison value
8786      */
8787     asInt : function(s) {
8788         var val = parseInt(String(s).replace(/,/g, ""));
8789         if(isNaN(val)) val = 0;
8790         return val;
8791     }
8792 };/*
8793  * Based on:
8794  * Ext JS Library 1.1.1
8795  * Copyright(c) 2006-2007, Ext JS, LLC.
8796  *
8797  * Originally Released Under LGPL - original licence link has changed is not relivant.
8798  *
8799  * Fork - LGPL
8800  * <script type="text/javascript">
8801  */
8802
8803 /**
8804 * @class Roo.data.Record
8805  * Instances of this class encapsulate both record <em>definition</em> information, and record
8806  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8807  * to access Records cached in an {@link Roo.data.Store} object.<br>
8808  * <p>
8809  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8810  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8811  * objects.<br>
8812  * <p>
8813  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8814  * @constructor
8815  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8816  * {@link #create}. The parameters are the same.
8817  * @param {Array} data An associative Array of data values keyed by the field name.
8818  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8819  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8820  * not specified an integer id is generated.
8821  */
8822 Roo.data.Record = function(data, id){
8823     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8824     this.data = data;
8825 };
8826
8827 /**
8828  * Generate a constructor for a specific record layout.
8829  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8830  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8831  * Each field definition object may contain the following properties: <ul>
8832  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
8833  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8834  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8835  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8836  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8837  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8838  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8839  * this may be omitted.</p></li>
8840  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8841  * <ul><li>auto (Default, implies no conversion)</li>
8842  * <li>string</li>
8843  * <li>int</li>
8844  * <li>float</li>
8845  * <li>boolean</li>
8846  * <li>date</li></ul></p></li>
8847  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8848  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8849  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8850  * by the Reader into an object that will be stored in the Record. It is passed the
8851  * following parameters:<ul>
8852  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8853  * </ul></p></li>
8854  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8855  * </ul>
8856  * <br>usage:<br><pre><code>
8857 var TopicRecord = Roo.data.Record.create(
8858     {name: 'title', mapping: 'topic_title'},
8859     {name: 'author', mapping: 'username'},
8860     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8861     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8862     {name: 'lastPoster', mapping: 'user2'},
8863     {name: 'excerpt', mapping: 'post_text'}
8864 );
8865
8866 var myNewRecord = new TopicRecord({
8867     title: 'Do my job please',
8868     author: 'noobie',
8869     totalPosts: 1,
8870     lastPost: new Date(),
8871     lastPoster: 'Animal',
8872     excerpt: 'No way dude!'
8873 });
8874 myStore.add(myNewRecord);
8875 </code></pre>
8876  * @method create
8877  * @static
8878  */
8879 Roo.data.Record.create = function(o){
8880     var f = function(){
8881         f.superclass.constructor.apply(this, arguments);
8882     };
8883     Roo.extend(f, Roo.data.Record);
8884     var p = f.prototype;
8885     p.fields = new Roo.util.MixedCollection(false, function(field){
8886         return field.name;
8887     });
8888     for(var i = 0, len = o.length; i < len; i++){
8889         p.fields.add(new Roo.data.Field(o[i]));
8890     }
8891     f.getField = function(name){
8892         return p.fields.get(name);  
8893     };
8894     return f;
8895 };
8896
8897 Roo.data.Record.AUTO_ID = 1000;
8898 Roo.data.Record.EDIT = 'edit';
8899 Roo.data.Record.REJECT = 'reject';
8900 Roo.data.Record.COMMIT = 'commit';
8901
8902 Roo.data.Record.prototype = {
8903     /**
8904      * Readonly flag - true if this record has been modified.
8905      * @type Boolean
8906      */
8907     dirty : false,
8908     editing : false,
8909     error: null,
8910     modified: null,
8911
8912     // private
8913     join : function(store){
8914         this.store = store;
8915     },
8916
8917     /**
8918      * Set the named field to the specified value.
8919      * @param {String} name The name of the field to set.
8920      * @param {Object} value The value to set the field to.
8921      */
8922     set : function(name, value){
8923         if(this.data[name] == value){
8924             return;
8925         }
8926         this.dirty = true;
8927         if(!this.modified){
8928             this.modified = {};
8929         }
8930         if(typeof this.modified[name] == 'undefined'){
8931             this.modified[name] = this.data[name];
8932         }
8933         this.data[name] = value;
8934         if(!this.editing && this.store){
8935             this.store.afterEdit(this);
8936         }       
8937     },
8938
8939     /**
8940      * Get the value of the named field.
8941      * @param {String} name The name of the field to get the value of.
8942      * @return {Object} The value of the field.
8943      */
8944     get : function(name){
8945         return this.data[name]; 
8946     },
8947
8948     // private
8949     beginEdit : function(){
8950         this.editing = true;
8951         this.modified = {}; 
8952     },
8953
8954     // private
8955     cancelEdit : function(){
8956         this.editing = false;
8957         delete this.modified;
8958     },
8959
8960     // private
8961     endEdit : function(){
8962         this.editing = false;
8963         if(this.dirty && this.store){
8964             this.store.afterEdit(this);
8965         }
8966     },
8967
8968     /**
8969      * Usually called by the {@link Roo.data.Store} which owns the Record.
8970      * Rejects all changes made to the Record since either creation, or the last commit operation.
8971      * Modified fields are reverted to their original values.
8972      * <p>
8973      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8974      * of reject operations.
8975      */
8976     reject : function(){
8977         var m = this.modified;
8978         for(var n in m){
8979             if(typeof m[n] != "function"){
8980                 this.data[n] = m[n];
8981             }
8982         }
8983         this.dirty = false;
8984         delete this.modified;
8985         this.editing = false;
8986         if(this.store){
8987             this.store.afterReject(this);
8988         }
8989     },
8990
8991     /**
8992      * Usually called by the {@link Roo.data.Store} which owns the Record.
8993      * Commits all changes made to the Record since either creation, or the last commit operation.
8994      * <p>
8995      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8996      * of commit operations.
8997      */
8998     commit : function(){
8999         this.dirty = false;
9000         delete this.modified;
9001         this.editing = false;
9002         if(this.store){
9003             this.store.afterCommit(this);
9004         }
9005     },
9006
9007     // private
9008     hasError : function(){
9009         return this.error != null;
9010     },
9011
9012     // private
9013     clearError : function(){
9014         this.error = null;
9015     },
9016
9017     /**
9018      * Creates a copy of this record.
9019      * @param {String} id (optional) A new record id if you don't want to use this record's id
9020      * @return {Record}
9021      */
9022     copy : function(newId) {
9023         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9024     }
9025 };/*
9026  * Based on:
9027  * Ext JS Library 1.1.1
9028  * Copyright(c) 2006-2007, Ext JS, LLC.
9029  *
9030  * Originally Released Under LGPL - original licence link has changed is not relivant.
9031  *
9032  * Fork - LGPL
9033  * <script type="text/javascript">
9034  */
9035
9036
9037
9038 /**
9039  * @class Roo.data.Store
9040  * @extends Roo.util.Observable
9041  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9042  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9043  * <p>
9044  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
9045  * has no knowledge of the format of the data returned by the Proxy.<br>
9046  * <p>
9047  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9048  * instances from the data object. These records are cached and made available through accessor functions.
9049  * @constructor
9050  * Creates a new Store.
9051  * @param {Object} config A config object containing the objects needed for the Store to access data,
9052  * and read the data into Records.
9053  */
9054 Roo.data.Store = function(config){
9055     this.data = new Roo.util.MixedCollection(false);
9056     this.data.getKey = function(o){
9057         return o.id;
9058     };
9059     this.baseParams = {};
9060     // private
9061     this.paramNames = {
9062         "start" : "start",
9063         "limit" : "limit",
9064         "sort" : "sort",
9065         "dir" : "dir",
9066         "multisort" : "_multisort"
9067     };
9068
9069     if(config && config.data){
9070         this.inlineData = config.data;
9071         delete config.data;
9072     }
9073
9074     Roo.apply(this, config);
9075     
9076     if(this.reader){ // reader passed
9077         this.reader = Roo.factory(this.reader, Roo.data);
9078         this.reader.xmodule = this.xmodule || false;
9079         if(!this.recordType){
9080             this.recordType = this.reader.recordType;
9081         }
9082         if(this.reader.onMetaChange){
9083             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9084         }
9085     }
9086
9087     if(this.recordType){
9088         this.fields = this.recordType.prototype.fields;
9089     }
9090     this.modified = [];
9091
9092     this.addEvents({
9093         /**
9094          * @event datachanged
9095          * Fires when the data cache has changed, and a widget which is using this Store
9096          * as a Record cache should refresh its view.
9097          * @param {Store} this
9098          */
9099         datachanged : true,
9100         /**
9101          * @event metachange
9102          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9103          * @param {Store} this
9104          * @param {Object} meta The JSON metadata
9105          */
9106         metachange : true,
9107         /**
9108          * @event add
9109          * Fires when Records have been added to the Store
9110          * @param {Store} this
9111          * @param {Roo.data.Record[]} records The array of Records added
9112          * @param {Number} index The index at which the record(s) were added
9113          */
9114         add : true,
9115         /**
9116          * @event remove
9117          * Fires when a Record has been removed from the Store
9118          * @param {Store} this
9119          * @param {Roo.data.Record} record The Record that was removed
9120          * @param {Number} index The index at which the record was removed
9121          */
9122         remove : true,
9123         /**
9124          * @event update
9125          * Fires when a Record has been updated
9126          * @param {Store} this
9127          * @param {Roo.data.Record} record The Record that was updated
9128          * @param {String} operation The update operation being performed.  Value may be one of:
9129          * <pre><code>
9130  Roo.data.Record.EDIT
9131  Roo.data.Record.REJECT
9132  Roo.data.Record.COMMIT
9133          * </code></pre>
9134          */
9135         update : true,
9136         /**
9137          * @event clear
9138          * Fires when the data cache has been cleared.
9139          * @param {Store} this
9140          */
9141         clear : true,
9142         /**
9143          * @event beforeload
9144          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9145          * the load action will be canceled.
9146          * @param {Store} this
9147          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9148          */
9149         beforeload : true,
9150         /**
9151          * @event beforeloadadd
9152          * Fires after a new set of Records has been loaded.
9153          * @param {Store} this
9154          * @param {Roo.data.Record[]} records The Records that were loaded
9155          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9156          */
9157         beforeloadadd : true,
9158         /**
9159          * @event load
9160          * Fires after a new set of Records has been loaded, before they are added to the store.
9161          * @param {Store} this
9162          * @param {Roo.data.Record[]} records The Records that were loaded
9163          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9164          * @params {Object} return from reader
9165          */
9166         load : true,
9167         /**
9168          * @event loadexception
9169          * Fires if an exception occurs in the Proxy during loading.
9170          * Called with the signature of the Proxy's "loadexception" event.
9171          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9172          * 
9173          * @param {Proxy} 
9174          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9175          * @param {Object} load options 
9176          * @param {Object} jsonData from your request (normally this contains the Exception)
9177          */
9178         loadexception : true
9179     });
9180     
9181     if(this.proxy){
9182         this.proxy = Roo.factory(this.proxy, Roo.data);
9183         this.proxy.xmodule = this.xmodule || false;
9184         this.relayEvents(this.proxy,  ["loadexception"]);
9185     }
9186     this.sortToggle = {};
9187     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9188
9189     Roo.data.Store.superclass.constructor.call(this);
9190
9191     if(this.inlineData){
9192         this.loadData(this.inlineData);
9193         delete this.inlineData;
9194     }
9195 };
9196
9197 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9198      /**
9199     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9200     * without a remote query - used by combo/forms at present.
9201     */
9202     
9203     /**
9204     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9205     */
9206     /**
9207     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9208     */
9209     /**
9210     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9211     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9212     */
9213     /**
9214     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9215     * on any HTTP request
9216     */
9217     /**
9218     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9219     */
9220     /**
9221     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9222     */
9223     multiSort: false,
9224     /**
9225     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9226     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9227     */
9228     remoteSort : false,
9229
9230     /**
9231     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9232      * loaded or when a record is removed. (defaults to false).
9233     */
9234     pruneModifiedRecords : false,
9235
9236     // private
9237     lastOptions : null,
9238
9239     /**
9240      * Add Records to the Store and fires the add event.
9241      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9242      */
9243     add : function(records){
9244         records = [].concat(records);
9245         for(var i = 0, len = records.length; i < len; i++){
9246             records[i].join(this);
9247         }
9248         var index = this.data.length;
9249         this.data.addAll(records);
9250         this.fireEvent("add", this, records, index);
9251     },
9252
9253     /**
9254      * Remove a Record from the Store and fires the remove event.
9255      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9256      */
9257     remove : function(record){
9258         var index = this.data.indexOf(record);
9259         this.data.removeAt(index);
9260         if(this.pruneModifiedRecords){
9261             this.modified.remove(record);
9262         }
9263         this.fireEvent("remove", this, record, index);
9264     },
9265
9266     /**
9267      * Remove all Records from the Store and fires the clear event.
9268      */
9269     removeAll : function(){
9270         this.data.clear();
9271         if(this.pruneModifiedRecords){
9272             this.modified = [];
9273         }
9274         this.fireEvent("clear", this);
9275     },
9276
9277     /**
9278      * Inserts Records to the Store at the given index and fires the add event.
9279      * @param {Number} index The start index at which to insert the passed Records.
9280      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9281      */
9282     insert : function(index, records){
9283         records = [].concat(records);
9284         for(var i = 0, len = records.length; i < len; i++){
9285             this.data.insert(index, records[i]);
9286             records[i].join(this);
9287         }
9288         this.fireEvent("add", this, records, index);
9289     },
9290
9291     /**
9292      * Get the index within the cache of the passed Record.
9293      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9294      * @return {Number} The index of the passed Record. Returns -1 if not found.
9295      */
9296     indexOf : function(record){
9297         return this.data.indexOf(record);
9298     },
9299
9300     /**
9301      * Get the index within the cache of the Record with the passed id.
9302      * @param {String} id The id of the Record to find.
9303      * @return {Number} The index of the Record. Returns -1 if not found.
9304      */
9305     indexOfId : function(id){
9306         return this.data.indexOfKey(id);
9307     },
9308
9309     /**
9310      * Get the Record with the specified id.
9311      * @param {String} id The id of the Record to find.
9312      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9313      */
9314     getById : function(id){
9315         return this.data.key(id);
9316     },
9317
9318     /**
9319      * Get the Record at the specified index.
9320      * @param {Number} index The index of the Record to find.
9321      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9322      */
9323     getAt : function(index){
9324         return this.data.itemAt(index);
9325     },
9326
9327     /**
9328      * Returns a range of Records between specified indices.
9329      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9330      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9331      * @return {Roo.data.Record[]} An array of Records
9332      */
9333     getRange : function(start, end){
9334         return this.data.getRange(start, end);
9335     },
9336
9337     // private
9338     storeOptions : function(o){
9339         o = Roo.apply({}, o);
9340         delete o.callback;
9341         delete o.scope;
9342         this.lastOptions = o;
9343     },
9344
9345     /**
9346      * Loads the Record cache from the configured Proxy using the configured Reader.
9347      * <p>
9348      * If using remote paging, then the first load call must specify the <em>start</em>
9349      * and <em>limit</em> properties in the options.params property to establish the initial
9350      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9351      * <p>
9352      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9353      * and this call will return before the new data has been loaded. Perform any post-processing
9354      * in a callback function, or in a "load" event handler.</strong>
9355      * <p>
9356      * @param {Object} options An object containing properties which control loading options:<ul>
9357      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9358      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9359      * passed the following arguments:<ul>
9360      * <li>r : Roo.data.Record[]</li>
9361      * <li>options: Options object from the load call</li>
9362      * <li>success: Boolean success indicator</li></ul></li>
9363      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9364      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9365      * </ul>
9366      */
9367     load : function(options){
9368         options = options || {};
9369         if(this.fireEvent("beforeload", this, options) !== false){
9370             this.storeOptions(options);
9371             var p = Roo.apply(options.params || {}, this.baseParams);
9372             // if meta was not loaded from remote source.. try requesting it.
9373             if (!this.reader.metaFromRemote) {
9374                 p._requestMeta = 1;
9375             }
9376             if(this.sortInfo && this.remoteSort){
9377                 var pn = this.paramNames;
9378                 p[pn["sort"]] = this.sortInfo.field;
9379                 p[pn["dir"]] = this.sortInfo.direction;
9380             }
9381             if (this.multiSort) {
9382                 var pn = this.paramNames;
9383                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9384             }
9385             
9386             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9387         }
9388     },
9389
9390     /**
9391      * Reloads the Record cache from the configured Proxy using the configured Reader and
9392      * the options from the last load operation performed.
9393      * @param {Object} options (optional) An object containing properties which may override the options
9394      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9395      * the most recently used options are reused).
9396      */
9397     reload : function(options){
9398         this.load(Roo.applyIf(options||{}, this.lastOptions));
9399     },
9400
9401     // private
9402     // Called as a callback by the Reader during a load operation.
9403     loadRecords : function(o, options, success){
9404         if(!o || success === false){
9405             if(success !== false){
9406                 this.fireEvent("load", this, [], options, o);
9407             }
9408             if(options.callback){
9409                 options.callback.call(options.scope || this, [], options, false);
9410             }
9411             return;
9412         }
9413         // if data returned failure - throw an exception.
9414         if (o.success === false) {
9415             // show a message if no listener is registered.
9416             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9417                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9418             }
9419             // loadmask wil be hooked into this..
9420             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9421             return;
9422         }
9423         var r = o.records, t = o.totalRecords || r.length;
9424         
9425         this.fireEvent("beforeloadadd", this, r, options, o);
9426         
9427         if(!options || options.add !== true){
9428             if(this.pruneModifiedRecords){
9429                 this.modified = [];
9430             }
9431             for(var i = 0, len = r.length; i < len; i++){
9432                 r[i].join(this);
9433             }
9434             if(this.snapshot){
9435                 this.data = this.snapshot;
9436                 delete this.snapshot;
9437             }
9438             this.data.clear();
9439             this.data.addAll(r);
9440             this.totalLength = t;
9441             this.applySort();
9442             this.fireEvent("datachanged", this);
9443         }else{
9444             this.totalLength = Math.max(t, this.data.length+r.length);
9445             this.add(r);
9446         }
9447         this.fireEvent("load", this, r, options, o);
9448         if(options.callback){
9449             options.callback.call(options.scope || this, r, options, true);
9450         }
9451     },
9452
9453
9454     /**
9455      * Loads data from a passed data block. A Reader which understands the format of the data
9456      * must have been configured in the constructor.
9457      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9458      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9459      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9460      */
9461     loadData : function(o, append){
9462         var r = this.reader.readRecords(o);
9463         this.loadRecords(r, {add: append}, true);
9464     },
9465
9466     /**
9467      * Gets the number of cached records.
9468      * <p>
9469      * <em>If using paging, this may not be the total size of the dataset. If the data object
9470      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9471      * the data set size</em>
9472      */
9473     getCount : function(){
9474         return this.data.length || 0;
9475     },
9476
9477     /**
9478      * Gets the total number of records in the dataset as returned by the server.
9479      * <p>
9480      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9481      * the dataset size</em>
9482      */
9483     getTotalCount : function(){
9484         return this.totalLength || 0;
9485     },
9486
9487     /**
9488      * Returns the sort state of the Store as an object with two properties:
9489      * <pre><code>
9490  field {String} The name of the field by which the Records are sorted
9491  direction {String} The sort order, "ASC" or "DESC"
9492      * </code></pre>
9493      */
9494     getSortState : function(){
9495         return this.sortInfo;
9496     },
9497
9498     // private
9499     applySort : function(){
9500         if(this.sortInfo && !this.remoteSort){
9501             var s = this.sortInfo, f = s.field;
9502             var st = this.fields.get(f).sortType;
9503             var fn = function(r1, r2){
9504                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9505                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9506             };
9507             this.data.sort(s.direction, fn);
9508             if(this.snapshot && this.snapshot != this.data){
9509                 this.snapshot.sort(s.direction, fn);
9510             }
9511         }
9512     },
9513
9514     /**
9515      * Sets the default sort column and order to be used by the next load operation.
9516      * @param {String} fieldName The name of the field to sort by.
9517      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9518      */
9519     setDefaultSort : function(field, dir){
9520         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9521     },
9522
9523     /**
9524      * Sort the Records.
9525      * If remote sorting is used, the sort is performed on the server, and the cache is
9526      * reloaded. If local sorting is used, the cache is sorted internally.
9527      * @param {String} fieldName The name of the field to sort by.
9528      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9529      */
9530     sort : function(fieldName, dir){
9531         var f = this.fields.get(fieldName);
9532         if(!dir){
9533             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9534             
9535             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9536                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9537             }else{
9538                 dir = f.sortDir;
9539             }
9540         }
9541         this.sortToggle[f.name] = dir;
9542         this.sortInfo = {field: f.name, direction: dir};
9543         if(!this.remoteSort){
9544             this.applySort();
9545             this.fireEvent("datachanged", this);
9546         }else{
9547             this.load(this.lastOptions);
9548         }
9549     },
9550
9551     /**
9552      * Calls the specified function for each of the Records in the cache.
9553      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9554      * Returning <em>false</em> aborts and exits the iteration.
9555      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9556      */
9557     each : function(fn, scope){
9558         this.data.each(fn, scope);
9559     },
9560
9561     /**
9562      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9563      * (e.g., during paging).
9564      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9565      */
9566     getModifiedRecords : function(){
9567         return this.modified;
9568     },
9569
9570     // private
9571     createFilterFn : function(property, value, anyMatch){
9572         if(!value.exec){ // not a regex
9573             value = String(value);
9574             if(value.length == 0){
9575                 return false;
9576             }
9577             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9578         }
9579         return function(r){
9580             return value.test(r.data[property]);
9581         };
9582     },
9583
9584     /**
9585      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9586      * @param {String} property A field on your records
9587      * @param {Number} start The record index to start at (defaults to 0)
9588      * @param {Number} end The last record index to include (defaults to length - 1)
9589      * @return {Number} The sum
9590      */
9591     sum : function(property, start, end){
9592         var rs = this.data.items, v = 0;
9593         start = start || 0;
9594         end = (end || end === 0) ? end : rs.length-1;
9595
9596         for(var i = start; i <= end; i++){
9597             v += (rs[i].data[property] || 0);
9598         }
9599         return v;
9600     },
9601
9602     /**
9603      * Filter the records by a specified property.
9604      * @param {String} field A field on your records
9605      * @param {String/RegExp} value Either a string that the field
9606      * should start with or a RegExp to test against the field
9607      * @param {Boolean} anyMatch True to match any part not just the beginning
9608      */
9609     filter : function(property, value, anyMatch){
9610         var fn = this.createFilterFn(property, value, anyMatch);
9611         return fn ? this.filterBy(fn) : this.clearFilter();
9612     },
9613
9614     /**
9615      * Filter by a function. The specified function will be called with each
9616      * record in this data source. If the function returns true the record is included,
9617      * otherwise it is filtered.
9618      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9619      * @param {Object} scope (optional) The scope of the function (defaults to this)
9620      */
9621     filterBy : function(fn, scope){
9622         this.snapshot = this.snapshot || this.data;
9623         this.data = this.queryBy(fn, scope||this);
9624         this.fireEvent("datachanged", this);
9625     },
9626
9627     /**
9628      * Query the records by a specified property.
9629      * @param {String} field A field on your records
9630      * @param {String/RegExp} value Either a string that the field
9631      * should start with or a RegExp to test against the field
9632      * @param {Boolean} anyMatch True to match any part not just the beginning
9633      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9634      */
9635     query : function(property, value, anyMatch){
9636         var fn = this.createFilterFn(property, value, anyMatch);
9637         return fn ? this.queryBy(fn) : this.data.clone();
9638     },
9639
9640     /**
9641      * Query by a function. The specified function will be called with each
9642      * record in this data source. If the function returns true the record is included
9643      * in the results.
9644      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9645      * @param {Object} scope (optional) The scope of the function (defaults to this)
9646       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9647      **/
9648     queryBy : function(fn, scope){
9649         var data = this.snapshot || this.data;
9650         return data.filterBy(fn, scope||this);
9651     },
9652
9653     /**
9654      * Collects unique values for a particular dataIndex from this store.
9655      * @param {String} dataIndex The property to collect
9656      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9657      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9658      * @return {Array} An array of the unique values
9659      **/
9660     collect : function(dataIndex, allowNull, bypassFilter){
9661         var d = (bypassFilter === true && this.snapshot) ?
9662                 this.snapshot.items : this.data.items;
9663         var v, sv, r = [], l = {};
9664         for(var i = 0, len = d.length; i < len; i++){
9665             v = d[i].data[dataIndex];
9666             sv = String(v);
9667             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9668                 l[sv] = true;
9669                 r[r.length] = v;
9670             }
9671         }
9672         return r;
9673     },
9674
9675     /**
9676      * Revert to a view of the Record cache with no filtering applied.
9677      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9678      */
9679     clearFilter : function(suppressEvent){
9680         if(this.snapshot && this.snapshot != this.data){
9681             this.data = this.snapshot;
9682             delete this.snapshot;
9683             if(suppressEvent !== true){
9684                 this.fireEvent("datachanged", this);
9685             }
9686         }
9687     },
9688
9689     // private
9690     afterEdit : function(record){
9691         if(this.modified.indexOf(record) == -1){
9692             this.modified.push(record);
9693         }
9694         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9695     },
9696     
9697     // private
9698     afterReject : function(record){
9699         this.modified.remove(record);
9700         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9701     },
9702
9703     // private
9704     afterCommit : function(record){
9705         this.modified.remove(record);
9706         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9707     },
9708
9709     /**
9710      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9711      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9712      */
9713     commitChanges : function(){
9714         var m = this.modified.slice(0);
9715         this.modified = [];
9716         for(var i = 0, len = m.length; i < len; i++){
9717             m[i].commit();
9718         }
9719     },
9720
9721     /**
9722      * Cancel outstanding changes on all changed records.
9723      */
9724     rejectChanges : function(){
9725         var m = this.modified.slice(0);
9726         this.modified = [];
9727         for(var i = 0, len = m.length; i < len; i++){
9728             m[i].reject();
9729         }
9730     },
9731
9732     onMetaChange : function(meta, rtype, o){
9733         this.recordType = rtype;
9734         this.fields = rtype.prototype.fields;
9735         delete this.snapshot;
9736         this.sortInfo = meta.sortInfo || this.sortInfo;
9737         this.modified = [];
9738         this.fireEvent('metachange', this, this.reader.meta);
9739     },
9740     
9741     moveIndex : function(data, type)
9742     {
9743         var index = this.indexOf(data);
9744         
9745         var newIndex = index + type;
9746         
9747         this.remove(data);
9748         
9749         this.insert(newIndex, data);
9750         
9751     }
9752 });/*
9753  * Based on:
9754  * Ext JS Library 1.1.1
9755  * Copyright(c) 2006-2007, Ext JS, LLC.
9756  *
9757  * Originally Released Under LGPL - original licence link has changed is not relivant.
9758  *
9759  * Fork - LGPL
9760  * <script type="text/javascript">
9761  */
9762
9763 /**
9764  * @class Roo.data.SimpleStore
9765  * @extends Roo.data.Store
9766  * Small helper class to make creating Stores from Array data easier.
9767  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9768  * @cfg {Array} fields An array of field definition objects, or field name strings.
9769  * @cfg {Array} data The multi-dimensional array of data
9770  * @constructor
9771  * @param {Object} config
9772  */
9773 Roo.data.SimpleStore = function(config){
9774     Roo.data.SimpleStore.superclass.constructor.call(this, {
9775         isLocal : true,
9776         reader: new Roo.data.ArrayReader({
9777                 id: config.id
9778             },
9779             Roo.data.Record.create(config.fields)
9780         ),
9781         proxy : new Roo.data.MemoryProxy(config.data)
9782     });
9783     this.load();
9784 };
9785 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9786  * Based on:
9787  * Ext JS Library 1.1.1
9788  * Copyright(c) 2006-2007, Ext JS, LLC.
9789  *
9790  * Originally Released Under LGPL - original licence link has changed is not relivant.
9791  *
9792  * Fork - LGPL
9793  * <script type="text/javascript">
9794  */
9795
9796 /**
9797 /**
9798  * @extends Roo.data.Store
9799  * @class Roo.data.JsonStore
9800  * Small helper class to make creating Stores for JSON data easier. <br/>
9801 <pre><code>
9802 var store = new Roo.data.JsonStore({
9803     url: 'get-images.php',
9804     root: 'images',
9805     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9806 });
9807 </code></pre>
9808  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9809  * JsonReader and HttpProxy (unless inline data is provided).</b>
9810  * @cfg {Array} fields An array of field definition objects, or field name strings.
9811  * @constructor
9812  * @param {Object} config
9813  */
9814 Roo.data.JsonStore = function(c){
9815     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9816         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9817         reader: new Roo.data.JsonReader(c, c.fields)
9818     }));
9819 };
9820 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9821  * Based on:
9822  * Ext JS Library 1.1.1
9823  * Copyright(c) 2006-2007, Ext JS, LLC.
9824  *
9825  * Originally Released Under LGPL - original licence link has changed is not relivant.
9826  *
9827  * Fork - LGPL
9828  * <script type="text/javascript">
9829  */
9830
9831  
9832 Roo.data.Field = function(config){
9833     if(typeof config == "string"){
9834         config = {name: config};
9835     }
9836     Roo.apply(this, config);
9837     
9838     if(!this.type){
9839         this.type = "auto";
9840     }
9841     
9842     var st = Roo.data.SortTypes;
9843     // named sortTypes are supported, here we look them up
9844     if(typeof this.sortType == "string"){
9845         this.sortType = st[this.sortType];
9846     }
9847     
9848     // set default sortType for strings and dates
9849     if(!this.sortType){
9850         switch(this.type){
9851             case "string":
9852                 this.sortType = st.asUCString;
9853                 break;
9854             case "date":
9855                 this.sortType = st.asDate;
9856                 break;
9857             default:
9858                 this.sortType = st.none;
9859         }
9860     }
9861
9862     // define once
9863     var stripRe = /[\$,%]/g;
9864
9865     // prebuilt conversion function for this field, instead of
9866     // switching every time we're reading a value
9867     if(!this.convert){
9868         var cv, dateFormat = this.dateFormat;
9869         switch(this.type){
9870             case "":
9871             case "auto":
9872             case undefined:
9873                 cv = function(v){ return v; };
9874                 break;
9875             case "string":
9876                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9877                 break;
9878             case "int":
9879                 cv = function(v){
9880                     return v !== undefined && v !== null && v !== '' ?
9881                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9882                     };
9883                 break;
9884             case "float":
9885                 cv = function(v){
9886                     return v !== undefined && v !== null && v !== '' ?
9887                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9888                     };
9889                 break;
9890             case "bool":
9891             case "boolean":
9892                 cv = function(v){ return v === true || v === "true" || v == 1; };
9893                 break;
9894             case "date":
9895                 cv = function(v){
9896                     if(!v){
9897                         return '';
9898                     }
9899                     if(v instanceof Date){
9900                         return v;
9901                     }
9902                     if(dateFormat){
9903                         if(dateFormat == "timestamp"){
9904                             return new Date(v*1000);
9905                         }
9906                         return Date.parseDate(v, dateFormat);
9907                     }
9908                     var parsed = Date.parse(v);
9909                     return parsed ? new Date(parsed) : null;
9910                 };
9911              break;
9912             
9913         }
9914         this.convert = cv;
9915     }
9916 };
9917
9918 Roo.data.Field.prototype = {
9919     dateFormat: null,
9920     defaultValue: "",
9921     mapping: null,
9922     sortType : null,
9923     sortDir : "ASC"
9924 };/*
9925  * Based on:
9926  * Ext JS Library 1.1.1
9927  * Copyright(c) 2006-2007, Ext JS, LLC.
9928  *
9929  * Originally Released Under LGPL - original licence link has changed is not relivant.
9930  *
9931  * Fork - LGPL
9932  * <script type="text/javascript">
9933  */
9934  
9935 // Base class for reading structured data from a data source.  This class is intended to be
9936 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9937
9938 /**
9939  * @class Roo.data.DataReader
9940  * Base class for reading structured data from a data source.  This class is intended to be
9941  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9942  */
9943
9944 Roo.data.DataReader = function(meta, recordType){
9945     
9946     this.meta = meta;
9947     
9948     this.recordType = recordType instanceof Array ? 
9949         Roo.data.Record.create(recordType) : recordType;
9950 };
9951
9952 Roo.data.DataReader.prototype = {
9953      /**
9954      * Create an empty record
9955      * @param {Object} data (optional) - overlay some values
9956      * @return {Roo.data.Record} record created.
9957      */
9958     newRow :  function(d) {
9959         var da =  {};
9960         this.recordType.prototype.fields.each(function(c) {
9961             switch( c.type) {
9962                 case 'int' : da[c.name] = 0; break;
9963                 case 'date' : da[c.name] = new Date(); break;
9964                 case 'float' : da[c.name] = 0.0; break;
9965                 case 'boolean' : da[c.name] = false; break;
9966                 default : da[c.name] = ""; break;
9967             }
9968             
9969         });
9970         return new this.recordType(Roo.apply(da, d));
9971     }
9972     
9973 };/*
9974  * Based on:
9975  * Ext JS Library 1.1.1
9976  * Copyright(c) 2006-2007, Ext JS, LLC.
9977  *
9978  * Originally Released Under LGPL - original licence link has changed is not relivant.
9979  *
9980  * Fork - LGPL
9981  * <script type="text/javascript">
9982  */
9983
9984 /**
9985  * @class Roo.data.DataProxy
9986  * @extends Roo.data.Observable
9987  * This class is an abstract base class for implementations which provide retrieval of
9988  * unformatted data objects.<br>
9989  * <p>
9990  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9991  * (of the appropriate type which knows how to parse the data object) to provide a block of
9992  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9993  * <p>
9994  * Custom implementations must implement the load method as described in
9995  * {@link Roo.data.HttpProxy#load}.
9996  */
9997 Roo.data.DataProxy = function(){
9998     this.addEvents({
9999         /**
10000          * @event beforeload
10001          * Fires before a network request is made to retrieve a data object.
10002          * @param {Object} This DataProxy object.
10003          * @param {Object} params The params parameter to the load function.
10004          */
10005         beforeload : true,
10006         /**
10007          * @event load
10008          * Fires before the load method's callback is called.
10009          * @param {Object} This DataProxy object.
10010          * @param {Object} o The data object.
10011          * @param {Object} arg The callback argument object passed to the load function.
10012          */
10013         load : true,
10014         /**
10015          * @event loadexception
10016          * Fires if an Exception occurs during data retrieval.
10017          * @param {Object} This DataProxy object.
10018          * @param {Object} o The data object.
10019          * @param {Object} arg The callback argument object passed to the load function.
10020          * @param {Object} e The Exception.
10021          */
10022         loadexception : true
10023     });
10024     Roo.data.DataProxy.superclass.constructor.call(this);
10025 };
10026
10027 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10028
10029     /**
10030      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10031      */
10032 /*
10033  * Based on:
10034  * Ext JS Library 1.1.1
10035  * Copyright(c) 2006-2007, Ext JS, LLC.
10036  *
10037  * Originally Released Under LGPL - original licence link has changed is not relivant.
10038  *
10039  * Fork - LGPL
10040  * <script type="text/javascript">
10041  */
10042 /**
10043  * @class Roo.data.MemoryProxy
10044  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10045  * to the Reader when its load method is called.
10046  * @constructor
10047  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10048  */
10049 Roo.data.MemoryProxy = function(data){
10050     if (data.data) {
10051         data = data.data;
10052     }
10053     Roo.data.MemoryProxy.superclass.constructor.call(this);
10054     this.data = data;
10055 };
10056
10057 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10058     /**
10059      * Load data from the requested source (in this case an in-memory
10060      * data object passed to the constructor), read the data object into
10061      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10062      * process that block using the passed callback.
10063      * @param {Object} params This parameter is not used by the MemoryProxy class.
10064      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10065      * object into a block of Roo.data.Records.
10066      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10067      * The function must be passed <ul>
10068      * <li>The Record block object</li>
10069      * <li>The "arg" argument from the load function</li>
10070      * <li>A boolean success indicator</li>
10071      * </ul>
10072      * @param {Object} scope The scope in which to call the callback
10073      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10074      */
10075     load : function(params, reader, callback, scope, arg){
10076         params = params || {};
10077         var result;
10078         try {
10079             result = reader.readRecords(this.data);
10080         }catch(e){
10081             this.fireEvent("loadexception", this, arg, null, e);
10082             callback.call(scope, null, arg, false);
10083             return;
10084         }
10085         callback.call(scope, result, arg, true);
10086     },
10087     
10088     // private
10089     update : function(params, records){
10090         
10091     }
10092 });/*
10093  * Based on:
10094  * Ext JS Library 1.1.1
10095  * Copyright(c) 2006-2007, Ext JS, LLC.
10096  *
10097  * Originally Released Under LGPL - original licence link has changed is not relivant.
10098  *
10099  * Fork - LGPL
10100  * <script type="text/javascript">
10101  */
10102 /**
10103  * @class Roo.data.HttpProxy
10104  * @extends Roo.data.DataProxy
10105  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10106  * configured to reference a certain URL.<br><br>
10107  * <p>
10108  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10109  * from which the running page was served.<br><br>
10110  * <p>
10111  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10112  * <p>
10113  * Be aware that to enable the browser to parse an XML document, the server must set
10114  * the Content-Type header in the HTTP response to "text/xml".
10115  * @constructor
10116  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10117  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10118  * will be used to make the request.
10119  */
10120 Roo.data.HttpProxy = function(conn){
10121     Roo.data.HttpProxy.superclass.constructor.call(this);
10122     // is conn a conn config or a real conn?
10123     this.conn = conn;
10124     this.useAjax = !conn || !conn.events;
10125   
10126 };
10127
10128 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10129     // thse are take from connection...
10130     
10131     /**
10132      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10133      */
10134     /**
10135      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10136      * extra parameters to each request made by this object. (defaults to undefined)
10137      */
10138     /**
10139      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10140      *  to each request made by this object. (defaults to undefined)
10141      */
10142     /**
10143      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
10144      */
10145     /**
10146      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10147      */
10148      /**
10149      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10150      * @type Boolean
10151      */
10152   
10153
10154     /**
10155      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10156      * @type Boolean
10157      */
10158     /**
10159      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10160      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10161      * a finer-grained basis than the DataProxy events.
10162      */
10163     getConnection : function(){
10164         return this.useAjax ? Roo.Ajax : this.conn;
10165     },
10166
10167     /**
10168      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10169      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10170      * process that block using the passed callback.
10171      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10172      * for the request to the remote server.
10173      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10174      * object into a block of Roo.data.Records.
10175      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10176      * The function must be passed <ul>
10177      * <li>The Record block object</li>
10178      * <li>The "arg" argument from the load function</li>
10179      * <li>A boolean success indicator</li>
10180      * </ul>
10181      * @param {Object} scope The scope in which to call the callback
10182      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10183      */
10184     load : function(params, reader, callback, scope, arg){
10185         if(this.fireEvent("beforeload", this, params) !== false){
10186             var  o = {
10187                 params : params || {},
10188                 request: {
10189                     callback : callback,
10190                     scope : scope,
10191                     arg : arg
10192                 },
10193                 reader: reader,
10194                 callback : this.loadResponse,
10195                 scope: this
10196             };
10197             if(this.useAjax){
10198                 Roo.applyIf(o, this.conn);
10199                 if(this.activeRequest){
10200                     Roo.Ajax.abort(this.activeRequest);
10201                 }
10202                 this.activeRequest = Roo.Ajax.request(o);
10203             }else{
10204                 this.conn.request(o);
10205             }
10206         }else{
10207             callback.call(scope||this, null, arg, false);
10208         }
10209     },
10210
10211     // private
10212     loadResponse : function(o, success, response){
10213         delete this.activeRequest;
10214         if(!success){
10215             this.fireEvent("loadexception", this, o, response);
10216             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10217             return;
10218         }
10219         var result;
10220         try {
10221             result = o.reader.read(response);
10222         }catch(e){
10223             this.fireEvent("loadexception", this, o, response, e);
10224             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10225             return;
10226         }
10227         
10228         this.fireEvent("load", this, o, o.request.arg);
10229         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10230     },
10231
10232     // private
10233     update : function(dataSet){
10234
10235     },
10236
10237     // private
10238     updateResponse : function(dataSet){
10239
10240     }
10241 });/*
10242  * Based on:
10243  * Ext JS Library 1.1.1
10244  * Copyright(c) 2006-2007, Ext JS, LLC.
10245  *
10246  * Originally Released Under LGPL - original licence link has changed is not relivant.
10247  *
10248  * Fork - LGPL
10249  * <script type="text/javascript">
10250  */
10251
10252 /**
10253  * @class Roo.data.ScriptTagProxy
10254  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10255  * other than the originating domain of the running page.<br><br>
10256  * <p>
10257  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
10258  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10259  * <p>
10260  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10261  * source code that is used as the source inside a &lt;script> tag.<br><br>
10262  * <p>
10263  * In order for the browser to process the returned data, the server must wrap the data object
10264  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10265  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10266  * depending on whether the callback name was passed:
10267  * <p>
10268  * <pre><code>
10269 boolean scriptTag = false;
10270 String cb = request.getParameter("callback");
10271 if (cb != null) {
10272     scriptTag = true;
10273     response.setContentType("text/javascript");
10274 } else {
10275     response.setContentType("application/x-json");
10276 }
10277 Writer out = response.getWriter();
10278 if (scriptTag) {
10279     out.write(cb + "(");
10280 }
10281 out.print(dataBlock.toJsonString());
10282 if (scriptTag) {
10283     out.write(");");
10284 }
10285 </pre></code>
10286  *
10287  * @constructor
10288  * @param {Object} config A configuration object.
10289  */
10290 Roo.data.ScriptTagProxy = function(config){
10291     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10292     Roo.apply(this, config);
10293     this.head = document.getElementsByTagName("head")[0];
10294 };
10295
10296 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10297
10298 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10299     /**
10300      * @cfg {String} url The URL from which to request the data object.
10301      */
10302     /**
10303      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10304      */
10305     timeout : 30000,
10306     /**
10307      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10308      * the server the name of the callback function set up by the load call to process the returned data object.
10309      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10310      * javascript output which calls this named function passing the data object as its only parameter.
10311      */
10312     callbackParam : "callback",
10313     /**
10314      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10315      * name to the request.
10316      */
10317     nocache : true,
10318
10319     /**
10320      * Load data from the configured URL, read the data object into
10321      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10322      * process that block using the passed callback.
10323      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10324      * for the request to the remote server.
10325      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10326      * object into a block of Roo.data.Records.
10327      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10328      * The function must be passed <ul>
10329      * <li>The Record block object</li>
10330      * <li>The "arg" argument from the load function</li>
10331      * <li>A boolean success indicator</li>
10332      * </ul>
10333      * @param {Object} scope The scope in which to call the callback
10334      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10335      */
10336     load : function(params, reader, callback, scope, arg){
10337         if(this.fireEvent("beforeload", this, params) !== false){
10338
10339             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10340
10341             var url = this.url;
10342             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10343             if(this.nocache){
10344                 url += "&_dc=" + (new Date().getTime());
10345             }
10346             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10347             var trans = {
10348                 id : transId,
10349                 cb : "stcCallback"+transId,
10350                 scriptId : "stcScript"+transId,
10351                 params : params,
10352                 arg : arg,
10353                 url : url,
10354                 callback : callback,
10355                 scope : scope,
10356                 reader : reader
10357             };
10358             var conn = this;
10359
10360             window[trans.cb] = function(o){
10361                 conn.handleResponse(o, trans);
10362             };
10363
10364             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10365
10366             if(this.autoAbort !== false){
10367                 this.abort();
10368             }
10369
10370             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10371
10372             var script = document.createElement("script");
10373             script.setAttribute("src", url);
10374             script.setAttribute("type", "text/javascript");
10375             script.setAttribute("id", trans.scriptId);
10376             this.head.appendChild(script);
10377
10378             this.trans = trans;
10379         }else{
10380             callback.call(scope||this, null, arg, false);
10381         }
10382     },
10383
10384     // private
10385     isLoading : function(){
10386         return this.trans ? true : false;
10387     },
10388
10389     /**
10390      * Abort the current server request.
10391      */
10392     abort : function(){
10393         if(this.isLoading()){
10394             this.destroyTrans(this.trans);
10395         }
10396     },
10397
10398     // private
10399     destroyTrans : function(trans, isLoaded){
10400         this.head.removeChild(document.getElementById(trans.scriptId));
10401         clearTimeout(trans.timeoutId);
10402         if(isLoaded){
10403             window[trans.cb] = undefined;
10404             try{
10405                 delete window[trans.cb];
10406             }catch(e){}
10407         }else{
10408             // if hasn't been loaded, wait for load to remove it to prevent script error
10409             window[trans.cb] = function(){
10410                 window[trans.cb] = undefined;
10411                 try{
10412                     delete window[trans.cb];
10413                 }catch(e){}
10414             };
10415         }
10416     },
10417
10418     // private
10419     handleResponse : function(o, trans){
10420         this.trans = false;
10421         this.destroyTrans(trans, true);
10422         var result;
10423         try {
10424             result = trans.reader.readRecords(o);
10425         }catch(e){
10426             this.fireEvent("loadexception", this, o, trans.arg, e);
10427             trans.callback.call(trans.scope||window, null, trans.arg, false);
10428             return;
10429         }
10430         this.fireEvent("load", this, o, trans.arg);
10431         trans.callback.call(trans.scope||window, result, trans.arg, true);
10432     },
10433
10434     // private
10435     handleFailure : function(trans){
10436         this.trans = false;
10437         this.destroyTrans(trans, false);
10438         this.fireEvent("loadexception", this, null, trans.arg);
10439         trans.callback.call(trans.scope||window, null, trans.arg, false);
10440     }
10441 });/*
10442  * Based on:
10443  * Ext JS Library 1.1.1
10444  * Copyright(c) 2006-2007, Ext JS, LLC.
10445  *
10446  * Originally Released Under LGPL - original licence link has changed is not relivant.
10447  *
10448  * Fork - LGPL
10449  * <script type="text/javascript">
10450  */
10451
10452 /**
10453  * @class Roo.data.JsonReader
10454  * @extends Roo.data.DataReader
10455  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10456  * based on mappings in a provided Roo.data.Record constructor.
10457  * 
10458  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10459  * in the reply previously. 
10460  * 
10461  * <p>
10462  * Example code:
10463  * <pre><code>
10464 var RecordDef = Roo.data.Record.create([
10465     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10466     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10467 ]);
10468 var myReader = new Roo.data.JsonReader({
10469     totalProperty: "results",    // The property which contains the total dataset size (optional)
10470     root: "rows",                // The property which contains an Array of row objects
10471     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10472 }, RecordDef);
10473 </code></pre>
10474  * <p>
10475  * This would consume a JSON file like this:
10476  * <pre><code>
10477 { 'results': 2, 'rows': [
10478     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10479     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10480 }
10481 </code></pre>
10482  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10483  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10484  * paged from the remote server.
10485  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10486  * @cfg {String} root name of the property which contains the Array of row objects.
10487  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10488  * @constructor
10489  * Create a new JsonReader
10490  * @param {Object} meta Metadata configuration options
10491  * @param {Object} recordType Either an Array of field definition objects,
10492  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10493  */
10494 Roo.data.JsonReader = function(meta, recordType){
10495     
10496     meta = meta || {};
10497     // set some defaults:
10498     Roo.applyIf(meta, {
10499         totalProperty: 'total',
10500         successProperty : 'success',
10501         root : 'data',
10502         id : 'id'
10503     });
10504     
10505     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10506 };
10507 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10508     
10509     /**
10510      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10511      * Used by Store query builder to append _requestMeta to params.
10512      * 
10513      */
10514     metaFromRemote : false,
10515     /**
10516      * This method is only used by a DataProxy which has retrieved data from a remote server.
10517      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10518      * @return {Object} data A data block which is used by an Roo.data.Store object as
10519      * a cache of Roo.data.Records.
10520      */
10521     read : function(response){
10522         var json = response.responseText;
10523        
10524         var o = /* eval:var:o */ eval("("+json+")");
10525         if(!o) {
10526             throw {message: "JsonReader.read: Json object not found"};
10527         }
10528         
10529         if(o.metaData){
10530             
10531             delete this.ef;
10532             this.metaFromRemote = true;
10533             this.meta = o.metaData;
10534             this.recordType = Roo.data.Record.create(o.metaData.fields);
10535             this.onMetaChange(this.meta, this.recordType, o);
10536         }
10537         return this.readRecords(o);
10538     },
10539
10540     // private function a store will implement
10541     onMetaChange : function(meta, recordType, o){
10542
10543     },
10544
10545     /**
10546          * @ignore
10547          */
10548     simpleAccess: function(obj, subsc) {
10549         return obj[subsc];
10550     },
10551
10552         /**
10553          * @ignore
10554          */
10555     getJsonAccessor: function(){
10556         var re = /[\[\.]/;
10557         return function(expr) {
10558             try {
10559                 return(re.test(expr))
10560                     ? new Function("obj", "return obj." + expr)
10561                     : function(obj){
10562                         return obj[expr];
10563                     };
10564             } catch(e){}
10565             return Roo.emptyFn;
10566         };
10567     }(),
10568
10569     /**
10570      * Create a data block containing Roo.data.Records from an XML document.
10571      * @param {Object} o An object which contains an Array of row objects in the property specified
10572      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10573      * which contains the total size of the dataset.
10574      * @return {Object} data A data block which is used by an Roo.data.Store object as
10575      * a cache of Roo.data.Records.
10576      */
10577     readRecords : function(o){
10578         /**
10579          * After any data loads, the raw JSON data is available for further custom processing.
10580          * @type Object
10581          */
10582         this.o = o;
10583         var s = this.meta, Record = this.recordType,
10584             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10585
10586 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10587         if (!this.ef) {
10588             if(s.totalProperty) {
10589                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10590                 }
10591                 if(s.successProperty) {
10592                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10593                 }
10594                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10595                 if (s.id) {
10596                         var g = this.getJsonAccessor(s.id);
10597                         this.getId = function(rec) {
10598                                 var r = g(rec);  
10599                                 return (r === undefined || r === "") ? null : r;
10600                         };
10601                 } else {
10602                         this.getId = function(){return null;};
10603                 }
10604             this.ef = [];
10605             for(var jj = 0; jj < fl; jj++){
10606                 f = fi[jj];
10607                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10608                 this.ef[jj] = this.getJsonAccessor(map);
10609             }
10610         }
10611
10612         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10613         if(s.totalProperty){
10614             var vt = parseInt(this.getTotal(o), 10);
10615             if(!isNaN(vt)){
10616                 totalRecords = vt;
10617             }
10618         }
10619         if(s.successProperty){
10620             var vs = this.getSuccess(o);
10621             if(vs === false || vs === 'false'){
10622                 success = false;
10623             }
10624         }
10625         var records = [];
10626         for(var i = 0; i < c; i++){
10627                 var n = root[i];
10628             var values = {};
10629             var id = this.getId(n);
10630             for(var j = 0; j < fl; j++){
10631                 f = fi[j];
10632             var v = this.ef[j](n);
10633             if (!f.convert) {
10634                 Roo.log('missing convert for ' + f.name);
10635                 Roo.log(f);
10636                 continue;
10637             }
10638             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10639             }
10640             var record = new Record(values, id);
10641             record.json = n;
10642             records[i] = record;
10643         }
10644         return {
10645             raw : o,
10646             success : success,
10647             records : records,
10648             totalRecords : totalRecords
10649         };
10650     }
10651 });/*
10652  * Based on:
10653  * Ext JS Library 1.1.1
10654  * Copyright(c) 2006-2007, Ext JS, LLC.
10655  *
10656  * Originally Released Under LGPL - original licence link has changed is not relivant.
10657  *
10658  * Fork - LGPL
10659  * <script type="text/javascript">
10660  */
10661
10662 /**
10663  * @class Roo.data.ArrayReader
10664  * @extends Roo.data.DataReader
10665  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10666  * Each element of that Array represents a row of data fields. The
10667  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10668  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10669  * <p>
10670  * Example code:.
10671  * <pre><code>
10672 var RecordDef = Roo.data.Record.create([
10673     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10674     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10675 ]);
10676 var myReader = new Roo.data.ArrayReader({
10677     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10678 }, RecordDef);
10679 </code></pre>
10680  * <p>
10681  * This would consume an Array like this:
10682  * <pre><code>
10683 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10684   </code></pre>
10685  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10686  * @constructor
10687  * Create a new JsonReader
10688  * @param {Object} meta Metadata configuration options.
10689  * @param {Object} recordType Either an Array of field definition objects
10690  * as specified to {@link Roo.data.Record#create},
10691  * or an {@link Roo.data.Record} object
10692  * created using {@link Roo.data.Record#create}.
10693  */
10694 Roo.data.ArrayReader = function(meta, recordType){
10695     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10696 };
10697
10698 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10699     /**
10700      * Create a data block containing Roo.data.Records from an XML document.
10701      * @param {Object} o An Array of row objects which represents the dataset.
10702      * @return {Object} data A data block which is used by an Roo.data.Store object as
10703      * a cache of Roo.data.Records.
10704      */
10705     readRecords : function(o){
10706         var sid = this.meta ? this.meta.id : null;
10707         var recordType = this.recordType, fields = recordType.prototype.fields;
10708         var records = [];
10709         var root = o;
10710             for(var i = 0; i < root.length; i++){
10711                     var n = root[i];
10712                 var values = {};
10713                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10714                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10715                 var f = fields.items[j];
10716                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10717                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10718                 v = f.convert(v);
10719                 values[f.name] = v;
10720             }
10721                 var record = new recordType(values, id);
10722                 record.json = n;
10723                 records[records.length] = record;
10724             }
10725             return {
10726                 records : records,
10727                 totalRecords : records.length
10728             };
10729     }
10730 });/*
10731  * - LGPL
10732  * * 
10733  */
10734
10735 /**
10736  * @class Roo.bootstrap.ComboBox
10737  * @extends Roo.bootstrap.TriggerField
10738  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10739  * @cfg {Boolean} append (true|false) default false
10740  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10741  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10742  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10743  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10744  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10745  * @constructor
10746  * Create a new ComboBox.
10747  * @param {Object} config Configuration options
10748  */
10749 Roo.bootstrap.ComboBox = function(config){
10750     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10751     this.addEvents({
10752         /**
10753          * @event expand
10754          * Fires when the dropdown list is expanded
10755              * @param {Roo.bootstrap.ComboBox} combo This combo box
10756              */
10757         'expand' : true,
10758         /**
10759          * @event collapse
10760          * Fires when the dropdown list is collapsed
10761              * @param {Roo.bootstrap.ComboBox} combo This combo box
10762              */
10763         'collapse' : true,
10764         /**
10765          * @event beforeselect
10766          * Fires before a list item is selected. Return false to cancel the selection.
10767              * @param {Roo.bootstrap.ComboBox} combo This combo box
10768              * @param {Roo.data.Record} record The data record returned from the underlying store
10769              * @param {Number} index The index of the selected item in the dropdown list
10770              */
10771         'beforeselect' : true,
10772         /**
10773          * @event select
10774          * Fires when a list item is selected
10775              * @param {Roo.bootstrap.ComboBox} combo This combo box
10776              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10777              * @param {Number} index The index of the selected item in the dropdown list
10778              */
10779         'select' : true,
10780         /**
10781          * @event beforequery
10782          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10783          * The event object passed has these properties:
10784              * @param {Roo.bootstrap.ComboBox} combo This combo box
10785              * @param {String} query The query
10786              * @param {Boolean} forceAll true to force "all" query
10787              * @param {Boolean} cancel true to cancel the query
10788              * @param {Object} e The query event object
10789              */
10790         'beforequery': true,
10791          /**
10792          * @event add
10793          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10794              * @param {Roo.bootstrap.ComboBox} combo This combo box
10795              */
10796         'add' : true,
10797         /**
10798          * @event edit
10799          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10800              * @param {Roo.bootstrap.ComboBox} combo This combo box
10801              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10802              */
10803         'edit' : true,
10804         /**
10805          * @event remove
10806          * Fires when the remove value from the combobox array
10807              * @param {Roo.bootstrap.ComboBox} combo This combo box
10808              */
10809         'remove' : true,
10810         /**
10811          * @event specialfilter
10812          * Fires when specialfilter
10813             * @param {Roo.bootstrap.ComboBox} combo This combo box
10814             */
10815         'specialfilter' : true
10816         
10817     });
10818     
10819     this.item = [];
10820     this.tickItems = [];
10821     
10822     this.selectedIndex = -1;
10823     if(this.mode == 'local'){
10824         if(config.queryDelay === undefined){
10825             this.queryDelay = 10;
10826         }
10827         if(config.minChars === undefined){
10828             this.minChars = 0;
10829         }
10830     }
10831 };
10832
10833 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10834      
10835     /**
10836      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10837      * rendering into an Roo.Editor, defaults to false)
10838      */
10839     /**
10840      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10841      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10842      */
10843     /**
10844      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10845      */
10846     /**
10847      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10848      * the dropdown list (defaults to undefined, with no header element)
10849      */
10850
10851      /**
10852      * @cfg {String/Roo.Template} tpl The template to use to render the output
10853      */
10854      
10855      /**
10856      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10857      */
10858     listWidth: undefined,
10859     /**
10860      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10861      * mode = 'remote' or 'text' if mode = 'local')
10862      */
10863     displayField: undefined,
10864     
10865     /**
10866      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10867      * mode = 'remote' or 'value' if mode = 'local'). 
10868      * Note: use of a valueField requires the user make a selection
10869      * in order for a value to be mapped.
10870      */
10871     valueField: undefined,
10872     
10873     
10874     /**
10875      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10876      * field's data value (defaults to the underlying DOM element's name)
10877      */
10878     hiddenName: undefined,
10879     /**
10880      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10881      */
10882     listClass: '',
10883     /**
10884      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10885      */
10886     selectedClass: 'active',
10887     
10888     /**
10889      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10890      */
10891     shadow:'sides',
10892     /**
10893      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10894      * anchor positions (defaults to 'tl-bl')
10895      */
10896     listAlign: 'tl-bl?',
10897     /**
10898      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10899      */
10900     maxHeight: 300,
10901     /**
10902      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10903      * query specified by the allQuery config option (defaults to 'query')
10904      */
10905     triggerAction: 'query',
10906     /**
10907      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10908      * (defaults to 4, does not apply if editable = false)
10909      */
10910     minChars : 4,
10911     /**
10912      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10913      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10914      */
10915     typeAhead: false,
10916     /**
10917      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10918      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10919      */
10920     queryDelay: 500,
10921     /**
10922      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10923      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10924      */
10925     pageSize: 0,
10926     /**
10927      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10928      * when editable = true (defaults to false)
10929      */
10930     selectOnFocus:false,
10931     /**
10932      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10933      */
10934     queryParam: 'query',
10935     /**
10936      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10937      * when mode = 'remote' (defaults to 'Loading...')
10938      */
10939     loadingText: 'Loading...',
10940     /**
10941      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10942      */
10943     resizable: false,
10944     /**
10945      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10946      */
10947     handleHeight : 8,
10948     /**
10949      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10950      * traditional select (defaults to true)
10951      */
10952     editable: true,
10953     /**
10954      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10955      */
10956     allQuery: '',
10957     /**
10958      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10959      */
10960     mode: 'remote',
10961     /**
10962      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10963      * listWidth has a higher value)
10964      */
10965     minListWidth : 70,
10966     /**
10967      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10968      * allow the user to set arbitrary text into the field (defaults to false)
10969      */
10970     forceSelection:false,
10971     /**
10972      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10973      * if typeAhead = true (defaults to 250)
10974      */
10975     typeAheadDelay : 250,
10976     /**
10977      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10978      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10979      */
10980     valueNotFoundText : undefined,
10981     /**
10982      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10983      */
10984     blockFocus : false,
10985     
10986     /**
10987      * @cfg {Boolean} disableClear Disable showing of clear button.
10988      */
10989     disableClear : false,
10990     /**
10991      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10992      */
10993     alwaysQuery : false,
10994     
10995     /**
10996      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10997      */
10998     multiple : false,
10999     
11000     /**
11001      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11002      */
11003     invalidClass : "has-warning",
11004     
11005     /**
11006      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11007      */
11008     validClass : "has-success",
11009     
11010     /**
11011      * @cfg {Boolean} specialFilter (true|false) special filter default false
11012      */
11013     specialFilter : false,
11014     
11015     //private
11016     addicon : false,
11017     editicon: false,
11018     
11019     page: 0,
11020     hasQuery: false,
11021     append: false,
11022     loadNext: false,
11023     autoFocus : true,
11024     tickable : false,
11025     btnPosition : 'right',
11026     triggerList : true,
11027     showToggleBtn : true,
11028     // element that contains real text value.. (when hidden is used..)
11029     
11030     getAutoCreate : function()
11031     {
11032         var cfg = false;
11033         
11034         /*
11035          *  Normal ComboBox
11036          */
11037         if(!this.tickable){
11038             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11039             return cfg;
11040         }
11041         
11042         /*
11043          *  ComboBox with tickable selections
11044          */
11045              
11046         var align = this.labelAlign || this.parentLabelAlign();
11047         
11048         cfg = {
11049             cls : 'form-group roo-combobox-tickable' //input-group
11050         };
11051         
11052         var buttons = {
11053             tag : 'div',
11054             cls : 'tickable-buttons',
11055             cn : [
11056                 {
11057                     tag : 'button',
11058                     type : 'button',
11059                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11060                     html : 'Edit'
11061                 },
11062                 {
11063                     tag : 'button',
11064                     type : 'button',
11065                     name : 'ok',
11066                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11067                     html : 'Done'
11068                 },
11069                 {
11070                     tag : 'button',
11071                     type : 'button',
11072                     name : 'cancel',
11073                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11074                     html : 'Cancel'
11075                 }
11076             ]
11077         };
11078         
11079         if(this.editable){
11080             buttons.cn.unshift({
11081                 tag: 'input',
11082                 cls: 'select2-search-field-input'
11083             });
11084         }
11085         
11086         var _this = this;
11087         
11088         Roo.each(buttons.cn, function(c){
11089             if (_this.size) {
11090                 c.cls += ' btn-' + _this.size;
11091             }
11092
11093             if (_this.disabled) {
11094                 c.disabled = true;
11095             }
11096         });
11097         
11098         var box = {
11099             tag: 'div',
11100             cn: [
11101                 {
11102                     tag: 'input',
11103                     type : 'hidden',
11104                     cls: 'form-hidden-field'
11105                 },
11106                 {
11107                     tag: 'ul',
11108                     cls: 'select2-choices',
11109                     cn:[
11110                         {
11111                             tag: 'li',
11112                             cls: 'select2-search-field',
11113                             cn: [
11114
11115                                 buttons
11116                             ]
11117                         }
11118                     ]
11119                 }
11120             ]
11121         }
11122         
11123         var combobox = {
11124             cls: 'select2-container input-group select2-container-multi',
11125             cn: [
11126                 box
11127 //                {
11128 //                    tag: 'ul',
11129 //                    cls: 'typeahead typeahead-long dropdown-menu',
11130 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11131 //                }
11132             ]
11133         };
11134         
11135         if(this.hasFeedback && !this.allowBlank){
11136             
11137             var feedback = {
11138                 tag: 'span',
11139                 cls: 'glyphicon form-control-feedback'
11140             };
11141
11142             combobox.cn.push(feedback);
11143         }
11144         
11145         if (align ==='left' && this.fieldLabel.length) {
11146             
11147                 Roo.log("left and has label");
11148                 cfg.cn = [
11149                     
11150                     {
11151                         tag: 'label',
11152                         'for' :  id,
11153                         cls : 'control-label col-sm-' + this.labelWidth,
11154                         html : this.fieldLabel
11155                         
11156                     },
11157                     {
11158                         cls : "col-sm-" + (12 - this.labelWidth), 
11159                         cn: [
11160                             combobox
11161                         ]
11162                     }
11163                     
11164                 ];
11165         } else if ( this.fieldLabel.length) {
11166                 Roo.log(" label");
11167                  cfg.cn = [
11168                    
11169                     {
11170                         tag: 'label',
11171                         //cls : 'input-group-addon',
11172                         html : this.fieldLabel
11173                         
11174                     },
11175                     
11176                     combobox
11177                     
11178                 ];
11179
11180         } else {
11181             
11182                 Roo.log(" no label && no align");
11183                 cfg = combobox
11184                      
11185                 
11186         }
11187          
11188         var settings=this;
11189         ['xs','sm','md','lg'].map(function(size){
11190             if (settings[size]) {
11191                 cfg.cls += ' col-' + size + '-' + settings[size];
11192             }
11193         });
11194         
11195         return cfg;
11196         
11197     },
11198     
11199     // private
11200     initEvents: function()
11201     {
11202         
11203         if (!this.store) {
11204             throw "can not find store for combo";
11205         }
11206         this.store = Roo.factory(this.store, Roo.data);
11207         
11208         if(this.tickable){
11209             this.initTickableEvents();
11210             return;
11211         }
11212         
11213         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11214         
11215         if(this.hiddenName){
11216             
11217             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11218             
11219             this.hiddenField.dom.value =
11220                 this.hiddenValue !== undefined ? this.hiddenValue :
11221                 this.value !== undefined ? this.value : '';
11222
11223             // prevent input submission
11224             this.el.dom.removeAttribute('name');
11225             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11226              
11227              
11228         }
11229         //if(Roo.isGecko){
11230         //    this.el.dom.setAttribute('autocomplete', 'off');
11231         //}
11232         
11233         var cls = 'x-combo-list';
11234         
11235         //this.list = new Roo.Layer({
11236         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11237         //});
11238         
11239         var _this = this;
11240         
11241         (function(){
11242             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11243             _this.list.setWidth(lw);
11244         }).defer(100);
11245         
11246         this.list.on('mouseover', this.onViewOver, this);
11247         this.list.on('mousemove', this.onViewMove, this);
11248         
11249         this.list.on('scroll', this.onViewScroll, this);
11250         
11251         /*
11252         this.list.swallowEvent('mousewheel');
11253         this.assetHeight = 0;
11254
11255         if(this.title){
11256             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11257             this.assetHeight += this.header.getHeight();
11258         }
11259
11260         this.innerList = this.list.createChild({cls:cls+'-inner'});
11261         this.innerList.on('mouseover', this.onViewOver, this);
11262         this.innerList.on('mousemove', this.onViewMove, this);
11263         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11264         
11265         if(this.allowBlank && !this.pageSize && !this.disableClear){
11266             this.footer = this.list.createChild({cls:cls+'-ft'});
11267             this.pageTb = new Roo.Toolbar(this.footer);
11268            
11269         }
11270         if(this.pageSize){
11271             this.footer = this.list.createChild({cls:cls+'-ft'});
11272             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11273                     {pageSize: this.pageSize});
11274             
11275         }
11276         
11277         if (this.pageTb && this.allowBlank && !this.disableClear) {
11278             var _this = this;
11279             this.pageTb.add(new Roo.Toolbar.Fill(), {
11280                 cls: 'x-btn-icon x-btn-clear',
11281                 text: '&#160;',
11282                 handler: function()
11283                 {
11284                     _this.collapse();
11285                     _this.clearValue();
11286                     _this.onSelect(false, -1);
11287                 }
11288             });
11289         }
11290         if (this.footer) {
11291             this.assetHeight += this.footer.getHeight();
11292         }
11293         */
11294             
11295         if(!this.tpl){
11296             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11297         }
11298
11299         this.view = new Roo.View(this.list, this.tpl, {
11300             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11301         });
11302         //this.view.wrapEl.setDisplayed(false);
11303         this.view.on('click', this.onViewClick, this);
11304         
11305         
11306         
11307         this.store.on('beforeload', this.onBeforeLoad, this);
11308         this.store.on('load', this.onLoad, this);
11309         this.store.on('loadexception', this.onLoadException, this);
11310         /*
11311         if(this.resizable){
11312             this.resizer = new Roo.Resizable(this.list,  {
11313                pinned:true, handles:'se'
11314             });
11315             this.resizer.on('resize', function(r, w, h){
11316                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11317                 this.listWidth = w;
11318                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11319                 this.restrictHeight();
11320             }, this);
11321             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11322         }
11323         */
11324         if(!this.editable){
11325             this.editable = true;
11326             this.setEditable(false);
11327         }
11328         
11329         /*
11330         
11331         if (typeof(this.events.add.listeners) != 'undefined') {
11332             
11333             this.addicon = this.wrap.createChild(
11334                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11335        
11336             this.addicon.on('click', function(e) {
11337                 this.fireEvent('add', this);
11338             }, this);
11339         }
11340         if (typeof(this.events.edit.listeners) != 'undefined') {
11341             
11342             this.editicon = this.wrap.createChild(
11343                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11344             if (this.addicon) {
11345                 this.editicon.setStyle('margin-left', '40px');
11346             }
11347             this.editicon.on('click', function(e) {
11348                 
11349                 // we fire even  if inothing is selected..
11350                 this.fireEvent('edit', this, this.lastData );
11351                 
11352             }, this);
11353         }
11354         */
11355         
11356         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11357             "up" : function(e){
11358                 this.inKeyMode = true;
11359                 this.selectPrev();
11360             },
11361
11362             "down" : function(e){
11363                 if(!this.isExpanded()){
11364                     this.onTriggerClick();
11365                 }else{
11366                     this.inKeyMode = true;
11367                     this.selectNext();
11368                 }
11369             },
11370
11371             "enter" : function(e){
11372 //                this.onViewClick();
11373                 //return true;
11374                 this.collapse();
11375                 
11376                 if(this.fireEvent("specialkey", this, e)){
11377                     this.onViewClick(false);
11378                 }
11379                 
11380                 return true;
11381             },
11382
11383             "esc" : function(e){
11384                 this.collapse();
11385             },
11386
11387             "tab" : function(e){
11388                 this.collapse();
11389                 
11390                 if(this.fireEvent("specialkey", this, e)){
11391                     this.onViewClick(false);
11392                 }
11393                 
11394                 return true;
11395             },
11396
11397             scope : this,
11398
11399             doRelay : function(foo, bar, hname){
11400                 if(hname == 'down' || this.scope.isExpanded()){
11401                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11402                 }
11403                 return true;
11404             },
11405
11406             forceKeyDown: true
11407         });
11408         
11409         
11410         this.queryDelay = Math.max(this.queryDelay || 10,
11411                 this.mode == 'local' ? 10 : 250);
11412         
11413         
11414         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11415         
11416         if(this.typeAhead){
11417             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11418         }
11419         if(this.editable !== false){
11420             this.inputEl().on("keyup", this.onKeyUp, this);
11421         }
11422         if(this.forceSelection){
11423             this.inputEl().on('blur', this.doForce, this);
11424         }
11425         
11426         if(this.multiple){
11427             this.choices = this.el.select('ul.select2-choices', true).first();
11428             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11429         }
11430     },
11431     
11432     initTickableEvents: function()
11433     {   
11434         this.createList();
11435         
11436         if(this.hiddenName){
11437             
11438             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11439             
11440             this.hiddenField.dom.value =
11441                 this.hiddenValue !== undefined ? this.hiddenValue :
11442                 this.value !== undefined ? this.value : '';
11443
11444             // prevent input submission
11445             this.el.dom.removeAttribute('name');
11446             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11447              
11448              
11449         }
11450         
11451 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11452         
11453         this.choices = this.el.select('ul.select2-choices', true).first();
11454         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11455         if(this.triggerList){
11456             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11457         }
11458          
11459         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11460         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11461         
11462         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11463         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11464         
11465         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11466         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11467         
11468         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11469         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11470         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11471         
11472         this.okBtn.hide();
11473         this.cancelBtn.hide();
11474         
11475         var _this = this;
11476         
11477         (function(){
11478             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11479             _this.list.setWidth(lw);
11480         }).defer(100);
11481         
11482         this.list.on('mouseover', this.onViewOver, this);
11483         this.list.on('mousemove', this.onViewMove, this);
11484         
11485         this.list.on('scroll', this.onViewScroll, this);
11486         
11487         if(!this.tpl){
11488             this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
11489         }
11490
11491         this.view = new Roo.View(this.list, this.tpl, {
11492             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11493         });
11494         
11495         //this.view.wrapEl.setDisplayed(false);
11496         this.view.on('click', this.onViewClick, this);
11497         
11498         
11499         
11500         this.store.on('beforeload', this.onBeforeLoad, this);
11501         this.store.on('load', this.onLoad, this);
11502         this.store.on('loadexception', this.onLoadException, this);
11503         
11504         if(this.editable){
11505             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11506                 "up" : function(e){
11507                     this.inKeyMode = true;
11508                     this.selectPrev();
11509                 },
11510
11511                 "down" : function(e){
11512                     this.inKeyMode = true;
11513                     this.selectNext();
11514                 },
11515
11516                 "enter" : function(e){
11517                     if(this.fireEvent("specialkey", this, e)){
11518                         this.onViewClick(false);
11519                     }
11520                     
11521                     return true;
11522                 },
11523
11524                 "esc" : function(e){
11525                     this.onTickableFooterButtonClick(e, false, false);
11526                 },
11527
11528                 "tab" : function(e){
11529                     this.fireEvent("specialkey", this, e);
11530                     
11531                     this.onTickableFooterButtonClick(e, false, false);
11532                     
11533                     return true;
11534                 },
11535
11536                 scope : this,
11537
11538                 doRelay : function(e, fn, key){
11539                     if(this.scope.isExpanded()){
11540                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11541                     }
11542                     return true;
11543                 },
11544
11545                 forceKeyDown: true
11546             });
11547         }
11548         
11549         this.queryDelay = Math.max(this.queryDelay || 10,
11550                 this.mode == 'local' ? 10 : 250);
11551         
11552         
11553         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11554         
11555         if(this.typeAhead){
11556             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11557         }
11558         
11559         if(this.editable !== false){
11560             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11561         }
11562         
11563     },
11564
11565     onDestroy : function(){
11566         if(this.view){
11567             this.view.setStore(null);
11568             this.view.el.removeAllListeners();
11569             this.view.el.remove();
11570             this.view.purgeListeners();
11571         }
11572         if(this.list){
11573             this.list.dom.innerHTML  = '';
11574         }
11575         
11576         if(this.store){
11577             this.store.un('beforeload', this.onBeforeLoad, this);
11578             this.store.un('load', this.onLoad, this);
11579             this.store.un('loadexception', this.onLoadException, this);
11580         }
11581         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11582     },
11583
11584     // private
11585     fireKey : function(e){
11586         if(e.isNavKeyPress() && !this.list.isVisible()){
11587             this.fireEvent("specialkey", this, e);
11588         }
11589     },
11590
11591     // private
11592     onResize: function(w, h){
11593 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11594 //        
11595 //        if(typeof w != 'number'){
11596 //            // we do not handle it!?!?
11597 //            return;
11598 //        }
11599 //        var tw = this.trigger.getWidth();
11600 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11601 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11602 //        var x = w - tw;
11603 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11604 //            
11605 //        //this.trigger.setStyle('left', x+'px');
11606 //        
11607 //        if(this.list && this.listWidth === undefined){
11608 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11609 //            this.list.setWidth(lw);
11610 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11611 //        }
11612         
11613     
11614         
11615     },
11616
11617     /**
11618      * Allow or prevent the user from directly editing the field text.  If false is passed,
11619      * the user will only be able to select from the items defined in the dropdown list.  This method
11620      * is the runtime equivalent of setting the 'editable' config option at config time.
11621      * @param {Boolean} value True to allow the user to directly edit the field text
11622      */
11623     setEditable : function(value){
11624         if(value == this.editable){
11625             return;
11626         }
11627         this.editable = value;
11628         if(!value){
11629             this.inputEl().dom.setAttribute('readOnly', true);
11630             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11631             this.inputEl().addClass('x-combo-noedit');
11632         }else{
11633             this.inputEl().dom.setAttribute('readOnly', false);
11634             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11635             this.inputEl().removeClass('x-combo-noedit');
11636         }
11637     },
11638
11639     // private
11640     
11641     onBeforeLoad : function(combo,opts){
11642         if(!this.hasFocus){
11643             return;
11644         }
11645          if (!opts.add) {
11646             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11647          }
11648         this.restrictHeight();
11649         this.selectedIndex = -1;
11650     },
11651
11652     // private
11653     onLoad : function(){
11654         
11655         this.hasQuery = false;
11656         
11657         if(!this.hasFocus){
11658             return;
11659         }
11660         
11661         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11662             this.loading.hide();
11663         }
11664              
11665         if(this.store.getCount() > 0){
11666             this.expand();
11667             this.restrictHeight();
11668             if(this.lastQuery == this.allQuery){
11669                 if(this.editable && !this.tickable){
11670                     this.inputEl().dom.select();
11671                 }
11672                 
11673                 if(
11674                     !this.selectByValue(this.value, true) &&
11675                     this.autoFocus && 
11676                     (
11677                         !this.store.lastOptions ||
11678                         typeof(this.store.lastOptions.add) == 'undefined' || 
11679                         this.store.lastOptions.add != true
11680                     )
11681                 ){
11682                     this.select(0, true);
11683                 }
11684             }else{
11685                 if(this.autoFocus){
11686                     this.selectNext();
11687                 }
11688                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11689                     this.taTask.delay(this.typeAheadDelay);
11690                 }
11691             }
11692         }else{
11693             this.onEmptyResults();
11694         }
11695         
11696         //this.el.focus();
11697     },
11698     // private
11699     onLoadException : function()
11700     {
11701         this.hasQuery = false;
11702         
11703         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11704             this.loading.hide();
11705         }
11706         
11707         if(this.tickable && this.editable){
11708             return;
11709         }
11710         
11711         this.collapse();
11712         
11713         Roo.log(this.store.reader.jsonData);
11714         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11715             // fixme
11716             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11717         }
11718         
11719         
11720     },
11721     // private
11722     onTypeAhead : function(){
11723         if(this.store.getCount() > 0){
11724             var r = this.store.getAt(0);
11725             var newValue = r.data[this.displayField];
11726             var len = newValue.length;
11727             var selStart = this.getRawValue().length;
11728             
11729             if(selStart != len){
11730                 this.setRawValue(newValue);
11731                 this.selectText(selStart, newValue.length);
11732             }
11733         }
11734     },
11735
11736     // private
11737     onSelect : function(record, index){
11738         
11739         if(this.fireEvent('beforeselect', this, record, index) !== false){
11740         
11741             this.setFromData(index > -1 ? record.data : false);
11742             
11743             this.collapse();
11744             this.fireEvent('select', this, record, index);
11745         }
11746     },
11747
11748     /**
11749      * Returns the currently selected field value or empty string if no value is set.
11750      * @return {String} value The selected value
11751      */
11752     getValue : function(){
11753         
11754         if(this.multiple){
11755             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11756         }
11757         
11758         if(this.valueField){
11759             return typeof this.value != 'undefined' ? this.value : '';
11760         }else{
11761             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11762         }
11763     },
11764
11765     /**
11766      * Clears any text/value currently set in the field
11767      */
11768     clearValue : function(){
11769         if(this.hiddenField){
11770             this.hiddenField.dom.value = '';
11771         }
11772         this.value = '';
11773         this.setRawValue('');
11774         this.lastSelectionText = '';
11775         this.lastData = false;
11776         
11777     },
11778
11779     /**
11780      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11781      * will be displayed in the field.  If the value does not match the data value of an existing item,
11782      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11783      * Otherwise the field will be blank (although the value will still be set).
11784      * @param {String} value The value to match
11785      */
11786     setValue : function(v){
11787         if(this.multiple){
11788             this.syncValue();
11789             return;
11790         }
11791         
11792         var text = v;
11793         if(this.valueField){
11794             var r = this.findRecord(this.valueField, v);
11795             if(r){
11796                 text = r.data[this.displayField];
11797             }else if(this.valueNotFoundText !== undefined){
11798                 text = this.valueNotFoundText;
11799             }
11800         }
11801         this.lastSelectionText = text;
11802         if(this.hiddenField){
11803             this.hiddenField.dom.value = v;
11804         }
11805         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11806         this.value = v;
11807     },
11808     /**
11809      * @property {Object} the last set data for the element
11810      */
11811     
11812     lastData : false,
11813     /**
11814      * Sets the value of the field based on a object which is related to the record format for the store.
11815      * @param {Object} value the value to set as. or false on reset?
11816      */
11817     setFromData : function(o){
11818         
11819         if(this.multiple){
11820             this.addItem(o);
11821             return;
11822         }
11823             
11824         var dv = ''; // display value
11825         var vv = ''; // value value..
11826         this.lastData = o;
11827         if (this.displayField) {
11828             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11829         } else {
11830             // this is an error condition!!!
11831             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11832         }
11833         
11834         if(this.valueField){
11835             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11836         }
11837         
11838         if(this.hiddenField){
11839             this.hiddenField.dom.value = vv;
11840             
11841             this.lastSelectionText = dv;
11842             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11843             this.value = vv;
11844             return;
11845         }
11846         // no hidden field.. - we store the value in 'value', but still display
11847         // display field!!!!
11848         this.lastSelectionText = dv;
11849         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11850         this.value = vv;
11851         
11852         
11853     },
11854     // private
11855     reset : function(){
11856         // overridden so that last data is reset..
11857         
11858         if(this.multiple){
11859             this.clearItem();
11860             return;
11861         }
11862         
11863         this.setValue(this.originalValue);
11864         this.clearInvalid();
11865         this.lastData = false;
11866         if (this.view) {
11867             this.view.clearSelections();
11868         }
11869     },
11870     // private
11871     findRecord : function(prop, value){
11872         var record;
11873         if(this.store.getCount() > 0){
11874             this.store.each(function(r){
11875                 if(r.data[prop] == value){
11876                     record = r;
11877                     return false;
11878                 }
11879                 return true;
11880             });
11881         }
11882         return record;
11883     },
11884     
11885     getName: function()
11886     {
11887         // returns hidden if it's set..
11888         if (!this.rendered) {return ''};
11889         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11890         
11891     },
11892     // private
11893     onViewMove : function(e, t){
11894         this.inKeyMode = false;
11895     },
11896
11897     // private
11898     onViewOver : function(e, t){
11899         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11900             return;
11901         }
11902         var item = this.view.findItemFromChild(t);
11903         
11904         if(item){
11905             var index = this.view.indexOf(item);
11906             this.select(index, false);
11907         }
11908     },
11909
11910     // private
11911     onViewClick : function(view, doFocus, el, e)
11912     {
11913         var index = this.view.getSelectedIndexes()[0];
11914         
11915         var r = this.store.getAt(index);
11916         
11917         if(this.tickable){
11918             
11919             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
11920                 return;
11921             }
11922             
11923             var rm = false;
11924             var _this = this;
11925             
11926             Roo.each(this.tickItems, function(v,k){
11927                 
11928                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11929                     _this.tickItems.splice(k, 1);
11930                     
11931                     if(typeof(e) == 'undefined' && view == false){
11932                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
11933                     }
11934                     
11935                     rm = true;
11936                     return;
11937                 }
11938             });
11939             
11940             if(rm){
11941                 return;
11942             }
11943             
11944             this.tickItems.push(r.data);
11945             
11946             if(typeof(e) == 'undefined' && view == false){
11947                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
11948             }
11949                     
11950             return;
11951         }
11952         
11953         if(r){
11954             this.onSelect(r, index);
11955         }
11956         if(doFocus !== false && !this.blockFocus){
11957             this.inputEl().focus();
11958         }
11959     },
11960
11961     // private
11962     restrictHeight : function(){
11963         //this.innerList.dom.style.height = '';
11964         //var inner = this.innerList.dom;
11965         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11966         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11967         //this.list.beginUpdate();
11968         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11969         this.list.alignTo(this.inputEl(), this.listAlign);
11970         this.list.alignTo(this.inputEl(), this.listAlign);
11971         //this.list.endUpdate();
11972     },
11973
11974     // private
11975     onEmptyResults : function(){
11976         
11977         if(this.tickable && this.editable){
11978             this.restrictHeight();
11979             return;
11980         }
11981         
11982         this.collapse();
11983     },
11984
11985     /**
11986      * Returns true if the dropdown list is expanded, else false.
11987      */
11988     isExpanded : function(){
11989         return this.list.isVisible();
11990     },
11991
11992     /**
11993      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11994      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11995      * @param {String} value The data value of the item to select
11996      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11997      * selected item if it is not currently in view (defaults to true)
11998      * @return {Boolean} True if the value matched an item in the list, else false
11999      */
12000     selectByValue : function(v, scrollIntoView){
12001         if(v !== undefined && v !== null){
12002             var r = this.findRecord(this.valueField || this.displayField, v);
12003             if(r){
12004                 this.select(this.store.indexOf(r), scrollIntoView);
12005                 return true;
12006             }
12007         }
12008         return false;
12009     },
12010
12011     /**
12012      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12013      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12014      * @param {Number} index The zero-based index of the list item to select
12015      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12016      * selected item if it is not currently in view (defaults to true)
12017      */
12018     select : function(index, scrollIntoView){
12019         this.selectedIndex = index;
12020         this.view.select(index);
12021         if(scrollIntoView !== false){
12022             var el = this.view.getNode(index);
12023             /*
12024              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12025              */
12026             if(el){
12027                 this.list.scrollChildIntoView(el, false);
12028             }
12029         }
12030     },
12031
12032     // private
12033     selectNext : function(){
12034         var ct = this.store.getCount();
12035         if(ct > 0){
12036             if(this.selectedIndex == -1){
12037                 this.select(0);
12038             }else if(this.selectedIndex < ct-1){
12039                 this.select(this.selectedIndex+1);
12040             }
12041         }
12042     },
12043
12044     // private
12045     selectPrev : function(){
12046         var ct = this.store.getCount();
12047         if(ct > 0){
12048             if(this.selectedIndex == -1){
12049                 this.select(0);
12050             }else if(this.selectedIndex != 0){
12051                 this.select(this.selectedIndex-1);
12052             }
12053         }
12054     },
12055
12056     // private
12057     onKeyUp : function(e){
12058         if(this.editable !== false && !e.isSpecialKey()){
12059             this.lastKey = e.getKey();
12060             this.dqTask.delay(this.queryDelay);
12061         }
12062     },
12063
12064     // private
12065     validateBlur : function(){
12066         return !this.list || !this.list.isVisible();   
12067     },
12068
12069     // private
12070     initQuery : function(){
12071         
12072         var v = this.getRawValue();
12073         
12074         if(this.tickable && this.editable){
12075             v = this.tickableInputEl().getValue();
12076         }
12077         
12078         this.doQuery(v);
12079     },
12080
12081     // private
12082     doForce : function(){
12083         if(this.inputEl().dom.value.length > 0){
12084             this.inputEl().dom.value =
12085                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12086              
12087         }
12088     },
12089
12090     /**
12091      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12092      * query allowing the query action to be canceled if needed.
12093      * @param {String} query The SQL query to execute
12094      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12095      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12096      * saved in the current store (defaults to false)
12097      */
12098     doQuery : function(q, forceAll){
12099         
12100         if(q === undefined || q === null){
12101             q = '';
12102         }
12103         var qe = {
12104             query: q,
12105             forceAll: forceAll,
12106             combo: this,
12107             cancel:false
12108         };
12109         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12110             return false;
12111         }
12112         q = qe.query;
12113         
12114         forceAll = qe.forceAll;
12115         if(forceAll === true || (q.length >= this.minChars)){
12116             
12117             this.hasQuery = true;
12118             
12119             if(this.lastQuery != q || this.alwaysQuery){
12120                 this.lastQuery = q;
12121                 if(this.mode == 'local'){
12122                     this.selectedIndex = -1;
12123                     if(forceAll){
12124                         this.store.clearFilter();
12125                     }else{
12126                         
12127                         if(this.specialFilter){
12128                             this.fireEvent('specialfilter', this);
12129                             this.onLoad();
12130                             return;
12131                         }
12132                         
12133                         this.store.filter(this.displayField, q);
12134                     }
12135                     
12136                     this.store.fireEvent("datachanged", this.store);
12137                     
12138                     this.onLoad();
12139                     
12140                     
12141                 }else{
12142                     
12143                     this.store.baseParams[this.queryParam] = q;
12144                     
12145                     var options = {params : this.getParams(q)};
12146                     
12147                     if(this.loadNext){
12148                         options.add = true;
12149                         options.params.start = this.page * this.pageSize;
12150                     }
12151                     
12152                     this.store.load(options);
12153                     
12154                     /*
12155                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12156                      *  we should expand the list on onLoad
12157                      *  so command out it
12158                      */
12159 //                    this.expand();
12160                 }
12161             }else{
12162                 this.selectedIndex = -1;
12163                 this.onLoad();   
12164             }
12165         }
12166         
12167         this.loadNext = false;
12168     },
12169     
12170     // private
12171     getParams : function(q){
12172         var p = {};
12173         //p[this.queryParam] = q;
12174         
12175         if(this.pageSize){
12176             p.start = 0;
12177             p.limit = this.pageSize;
12178         }
12179         return p;
12180     },
12181
12182     /**
12183      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12184      */
12185     collapse : function(){
12186         if(!this.isExpanded()){
12187             return;
12188         }
12189         
12190         this.list.hide();
12191         
12192         if(this.tickable){
12193             this.hasFocus = false;
12194             this.okBtn.hide();
12195             this.cancelBtn.hide();
12196             this.trigger.show();
12197             
12198             if(this.editable){
12199                 this.tickableInputEl().dom.value = '';
12200                 this.tickableInputEl().blur();
12201             }
12202             
12203         }
12204         
12205         Roo.get(document).un('mousedown', this.collapseIf, this);
12206         Roo.get(document).un('mousewheel', this.collapseIf, this);
12207         if (!this.editable) {
12208             Roo.get(document).un('keydown', this.listKeyPress, this);
12209         }
12210         this.fireEvent('collapse', this);
12211     },
12212
12213     // private
12214     collapseIf : function(e){
12215         var in_combo  = e.within(this.el);
12216         var in_list =  e.within(this.list);
12217         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12218         
12219         if (in_combo || in_list || is_list) {
12220             //e.stopPropagation();
12221             return;
12222         }
12223         
12224         if(this.tickable){
12225             this.onTickableFooterButtonClick(e, false, false);
12226         }
12227
12228         this.collapse();
12229         
12230     },
12231
12232     /**
12233      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12234      */
12235     expand : function(){
12236        
12237         if(this.isExpanded() || !this.hasFocus){
12238             return;
12239         }
12240         
12241         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12242         this.list.setWidth(lw);
12243         
12244         
12245          Roo.log('expand');
12246         
12247         this.list.show();
12248         
12249         this.restrictHeight();
12250         
12251         if(this.tickable){
12252             
12253             this.tickItems = Roo.apply([], this.item);
12254             
12255             this.okBtn.show();
12256             this.cancelBtn.show();
12257             this.trigger.hide();
12258             
12259             if(this.editable){
12260                 this.tickableInputEl().focus();
12261             }
12262             
12263         }
12264         
12265         Roo.get(document).on('mousedown', this.collapseIf, this);
12266         Roo.get(document).on('mousewheel', this.collapseIf, this);
12267         if (!this.editable) {
12268             Roo.get(document).on('keydown', this.listKeyPress, this);
12269         }
12270         
12271         this.fireEvent('expand', this);
12272     },
12273
12274     // private
12275     // Implements the default empty TriggerField.onTriggerClick function
12276     onTriggerClick : function(e)
12277     {
12278         Roo.log('trigger click');
12279         
12280         if(this.disabled || !this.triggerList){
12281             return;
12282         }
12283         
12284         this.page = 0;
12285         this.loadNext = false;
12286         
12287         if(this.isExpanded()){
12288             this.collapse();
12289             if (!this.blockFocus) {
12290                 this.inputEl().focus();
12291             }
12292             
12293         }else {
12294             this.hasFocus = true;
12295             if(this.triggerAction == 'all') {
12296                 this.doQuery(this.allQuery, true);
12297             } else {
12298                 this.doQuery(this.getRawValue());
12299             }
12300             if (!this.blockFocus) {
12301                 this.inputEl().focus();
12302             }
12303         }
12304     },
12305     
12306     onTickableTriggerClick : function(e)
12307     {
12308         if(this.disabled){
12309             return;
12310         }
12311         
12312         this.page = 0;
12313         this.loadNext = false;
12314         this.hasFocus = true;
12315         
12316         if(this.triggerAction == 'all') {
12317             this.doQuery(this.allQuery, true);
12318         } else {
12319             this.doQuery(this.getRawValue());
12320         }
12321     },
12322     
12323     onSearchFieldClick : function(e)
12324     {
12325         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12326             this.onTickableFooterButtonClick(e, false, false);
12327             return;
12328         }
12329         
12330         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12331             return;
12332         }
12333         
12334         this.page = 0;
12335         this.loadNext = false;
12336         this.hasFocus = true;
12337         
12338         if(this.triggerAction == 'all') {
12339             this.doQuery(this.allQuery, true);
12340         } else {
12341             this.doQuery(this.getRawValue());
12342         }
12343     },
12344     
12345     listKeyPress : function(e)
12346     {
12347         //Roo.log('listkeypress');
12348         // scroll to first matching element based on key pres..
12349         if (e.isSpecialKey()) {
12350             return false;
12351         }
12352         var k = String.fromCharCode(e.getKey()).toUpperCase();
12353         //Roo.log(k);
12354         var match  = false;
12355         var csel = this.view.getSelectedNodes();
12356         var cselitem = false;
12357         if (csel.length) {
12358             var ix = this.view.indexOf(csel[0]);
12359             cselitem  = this.store.getAt(ix);
12360             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12361                 cselitem = false;
12362             }
12363             
12364         }
12365         
12366         this.store.each(function(v) { 
12367             if (cselitem) {
12368                 // start at existing selection.
12369                 if (cselitem.id == v.id) {
12370                     cselitem = false;
12371                 }
12372                 return true;
12373             }
12374                 
12375             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12376                 match = this.store.indexOf(v);
12377                 return false;
12378             }
12379             return true;
12380         }, this);
12381         
12382         if (match === false) {
12383             return true; // no more action?
12384         }
12385         // scroll to?
12386         this.view.select(match);
12387         var sn = Roo.get(this.view.getSelectedNodes()[0])
12388         sn.scrollIntoView(sn.dom.parentNode, false);
12389     },
12390     
12391     onViewScroll : function(e, t){
12392         
12393         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
12394             return;
12395         }
12396         
12397         this.hasQuery = true;
12398         
12399         this.loading = this.list.select('.loading', true).first();
12400         
12401         if(this.loading === null){
12402             this.list.createChild({
12403                 tag: 'div',
12404                 cls: 'loading select2-more-results select2-active',
12405                 html: 'Loading more results...'
12406             })
12407             
12408             this.loading = this.list.select('.loading', true).first();
12409             
12410             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12411             
12412             this.loading.hide();
12413         }
12414         
12415         this.loading.show();
12416         
12417         var _combo = this;
12418         
12419         this.page++;
12420         this.loadNext = true;
12421         
12422         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12423         
12424         return;
12425     },
12426     
12427     addItem : function(o)
12428     {   
12429         var dv = ''; // display value
12430         
12431         if (this.displayField) {
12432             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12433         } else {
12434             // this is an error condition!!!
12435             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12436         }
12437         
12438         if(!dv.length){
12439             return;
12440         }
12441         
12442         var choice = this.choices.createChild({
12443             tag: 'li',
12444             cls: 'select2-search-choice',
12445             cn: [
12446                 {
12447                     tag: 'div',
12448                     html: dv
12449                 },
12450                 {
12451                     tag: 'a',
12452                     href: '#',
12453                     cls: 'select2-search-choice-close',
12454                     tabindex: '-1'
12455                 }
12456             ]
12457             
12458         }, this.searchField);
12459         
12460         var close = choice.select('a.select2-search-choice-close', true).first()
12461         
12462         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12463         
12464         this.item.push(o);
12465         
12466         this.lastData = o;
12467         
12468         this.syncValue();
12469         
12470         this.inputEl().dom.value = '';
12471         
12472         this.validate();
12473     },
12474     
12475     onRemoveItem : function(e, _self, o)
12476     {
12477         e.preventDefault();
12478         
12479         this.lastItem = Roo.apply([], this.item);
12480         
12481         var index = this.item.indexOf(o.data) * 1;
12482         
12483         if( index < 0){
12484             Roo.log('not this item?!');
12485             return;
12486         }
12487         
12488         this.item.splice(index, 1);
12489         o.item.remove();
12490         
12491         this.syncValue();
12492         
12493         this.fireEvent('remove', this, e);
12494         
12495         this.validate();
12496         
12497     },
12498     
12499     syncValue : function()
12500     {
12501         if(!this.item.length){
12502             this.clearValue();
12503             return;
12504         }
12505             
12506         var value = [];
12507         var _this = this;
12508         Roo.each(this.item, function(i){
12509             if(_this.valueField){
12510                 value.push(i[_this.valueField]);
12511                 return;
12512             }
12513
12514             value.push(i);
12515         });
12516
12517         this.value = value.join(',');
12518
12519         if(this.hiddenField){
12520             this.hiddenField.dom.value = this.value;
12521         }
12522         
12523         this.store.fireEvent("datachanged", this.store);
12524     },
12525     
12526     clearItem : function()
12527     {
12528         if(!this.multiple){
12529             return;
12530         }
12531         
12532         this.item = [];
12533         
12534         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12535            c.remove();
12536         });
12537         
12538         this.syncValue();
12539         
12540         this.validate();
12541     },
12542     
12543     inputEl: function ()
12544     {
12545         if(this.tickable){
12546             return this.searchField;
12547         }
12548         return this.el.select('input.form-control',true).first();
12549     },
12550     
12551     
12552     onTickableFooterButtonClick : function(e, btn, el)
12553     {
12554         e.preventDefault();
12555         
12556         this.lastItem = Roo.apply([], this.item);
12557         
12558         if(btn && btn.name == 'cancel'){
12559             this.tickItems = Roo.apply([], this.item);
12560             this.collapse();
12561             return;
12562         }
12563         
12564         this.clearItem();
12565         
12566         var _this = this;
12567         
12568         Roo.each(this.tickItems, function(o){
12569             _this.addItem(o);
12570         });
12571         
12572         this.collapse();
12573         
12574     },
12575     
12576     validate : function()
12577     {
12578         var v = this.getRawValue();
12579         
12580         if(this.multiple){
12581             v = this.getValue();
12582         }
12583         
12584         if(this.disabled || this.allowBlank || v.length){
12585             this.markValid();
12586             return true;
12587         }
12588         
12589         this.markInvalid();
12590         return false;
12591     },
12592     
12593     tickableInputEl : function()
12594     {
12595         if(!this.tickable || !this.editable){
12596             return this.inputEl();
12597         }
12598         
12599         return this.inputEl().select('.select2-search-field-input', true).first();
12600     }
12601     
12602     
12603
12604     /** 
12605     * @cfg {Boolean} grow 
12606     * @hide 
12607     */
12608     /** 
12609     * @cfg {Number} growMin 
12610     * @hide 
12611     */
12612     /** 
12613     * @cfg {Number} growMax 
12614     * @hide 
12615     */
12616     /**
12617      * @hide
12618      * @method autoSize
12619      */
12620 });
12621 /*
12622  * Based on:
12623  * Ext JS Library 1.1.1
12624  * Copyright(c) 2006-2007, Ext JS, LLC.
12625  *
12626  * Originally Released Under LGPL - original licence link has changed is not relivant.
12627  *
12628  * Fork - LGPL
12629  * <script type="text/javascript">
12630  */
12631
12632 /**
12633  * @class Roo.View
12634  * @extends Roo.util.Observable
12635  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12636  * This class also supports single and multi selection modes. <br>
12637  * Create a data model bound view:
12638  <pre><code>
12639  var store = new Roo.data.Store(...);
12640
12641  var view = new Roo.View({
12642     el : "my-element",
12643     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12644  
12645     singleSelect: true,
12646     selectedClass: "ydataview-selected",
12647     store: store
12648  });
12649
12650  // listen for node click?
12651  view.on("click", function(vw, index, node, e){
12652  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12653  });
12654
12655  // load XML data
12656  dataModel.load("foobar.xml");
12657  </code></pre>
12658  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12659  * <br><br>
12660  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12661  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12662  * 
12663  * Note: old style constructor is still suported (container, template, config)
12664  * 
12665  * @constructor
12666  * Create a new View
12667  * @param {Object} config The config object
12668  * 
12669  */
12670 Roo.View = function(config, depreciated_tpl, depreciated_config){
12671     
12672     this.parent = false;
12673     
12674     if (typeof(depreciated_tpl) == 'undefined') {
12675         // new way.. - universal constructor.
12676         Roo.apply(this, config);
12677         this.el  = Roo.get(this.el);
12678     } else {
12679         // old format..
12680         this.el  = Roo.get(config);
12681         this.tpl = depreciated_tpl;
12682         Roo.apply(this, depreciated_config);
12683     }
12684     this.wrapEl  = this.el.wrap().wrap();
12685     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12686     
12687     
12688     if(typeof(this.tpl) == "string"){
12689         this.tpl = new Roo.Template(this.tpl);
12690     } else {
12691         // support xtype ctors..
12692         this.tpl = new Roo.factory(this.tpl, Roo);
12693     }
12694     
12695     
12696     this.tpl.compile();
12697     
12698     /** @private */
12699     this.addEvents({
12700         /**
12701          * @event beforeclick
12702          * Fires before a click is processed. Returns false to cancel the default action.
12703          * @param {Roo.View} this
12704          * @param {Number} index The index of the target node
12705          * @param {HTMLElement} node The target node
12706          * @param {Roo.EventObject} e The raw event object
12707          */
12708             "beforeclick" : true,
12709         /**
12710          * @event click
12711          * Fires when a template node is clicked.
12712          * @param {Roo.View} this
12713          * @param {Number} index The index of the target node
12714          * @param {HTMLElement} node The target node
12715          * @param {Roo.EventObject} e The raw event object
12716          */
12717             "click" : true,
12718         /**
12719          * @event dblclick
12720          * Fires when a template node is double clicked.
12721          * @param {Roo.View} this
12722          * @param {Number} index The index of the target node
12723          * @param {HTMLElement} node The target node
12724          * @param {Roo.EventObject} e The raw event object
12725          */
12726             "dblclick" : true,
12727         /**
12728          * @event contextmenu
12729          * Fires when a template node is right clicked.
12730          * @param {Roo.View} this
12731          * @param {Number} index The index of the target node
12732          * @param {HTMLElement} node The target node
12733          * @param {Roo.EventObject} e The raw event object
12734          */
12735             "contextmenu" : true,
12736         /**
12737          * @event selectionchange
12738          * Fires when the selected nodes change.
12739          * @param {Roo.View} this
12740          * @param {Array} selections Array of the selected nodes
12741          */
12742             "selectionchange" : true,
12743     
12744         /**
12745          * @event beforeselect
12746          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12747          * @param {Roo.View} this
12748          * @param {HTMLElement} node The node to be selected
12749          * @param {Array} selections Array of currently selected nodes
12750          */
12751             "beforeselect" : true,
12752         /**
12753          * @event preparedata
12754          * Fires on every row to render, to allow you to change the data.
12755          * @param {Roo.View} this
12756          * @param {Object} data to be rendered (change this)
12757          */
12758           "preparedata" : true
12759           
12760           
12761         });
12762
12763
12764
12765     this.el.on({
12766         "click": this.onClick,
12767         "dblclick": this.onDblClick,
12768         "contextmenu": this.onContextMenu,
12769         scope:this
12770     });
12771
12772     this.selections = [];
12773     this.nodes = [];
12774     this.cmp = new Roo.CompositeElementLite([]);
12775     if(this.store){
12776         this.store = Roo.factory(this.store, Roo.data);
12777         this.setStore(this.store, true);
12778     }
12779     
12780     if ( this.footer && this.footer.xtype) {
12781            
12782          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12783         
12784         this.footer.dataSource = this.store
12785         this.footer.container = fctr;
12786         this.footer = Roo.factory(this.footer, Roo);
12787         fctr.insertFirst(this.el);
12788         
12789         // this is a bit insane - as the paging toolbar seems to detach the el..
12790 //        dom.parentNode.parentNode.parentNode
12791          // they get detached?
12792     }
12793     
12794     
12795     Roo.View.superclass.constructor.call(this);
12796     
12797     
12798 };
12799
12800 Roo.extend(Roo.View, Roo.util.Observable, {
12801     
12802      /**
12803      * @cfg {Roo.data.Store} store Data store to load data from.
12804      */
12805     store : false,
12806     
12807     /**
12808      * @cfg {String|Roo.Element} el The container element.
12809      */
12810     el : '',
12811     
12812     /**
12813      * @cfg {String|Roo.Template} tpl The template used by this View 
12814      */
12815     tpl : false,
12816     /**
12817      * @cfg {String} dataName the named area of the template to use as the data area
12818      *                          Works with domtemplates roo-name="name"
12819      */
12820     dataName: false,
12821     /**
12822      * @cfg {String} selectedClass The css class to add to selected nodes
12823      */
12824     selectedClass : "x-view-selected",
12825      /**
12826      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12827      */
12828     emptyText : "",
12829     
12830     /**
12831      * @cfg {String} text to display on mask (default Loading)
12832      */
12833     mask : false,
12834     /**
12835      * @cfg {Boolean} multiSelect Allow multiple selection
12836      */
12837     multiSelect : false,
12838     /**
12839      * @cfg {Boolean} singleSelect Allow single selection
12840      */
12841     singleSelect:  false,
12842     
12843     /**
12844      * @cfg {Boolean} toggleSelect - selecting 
12845      */
12846     toggleSelect : false,
12847     
12848     /**
12849      * @cfg {Boolean} tickable - selecting 
12850      */
12851     tickable : false,
12852     
12853     /**
12854      * Returns the element this view is bound to.
12855      * @return {Roo.Element}
12856      */
12857     getEl : function(){
12858         return this.wrapEl;
12859     },
12860     
12861     
12862
12863     /**
12864      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12865      */
12866     refresh : function(){
12867         //Roo.log('refresh');
12868         var t = this.tpl;
12869         
12870         // if we are using something like 'domtemplate', then
12871         // the what gets used is:
12872         // t.applySubtemplate(NAME, data, wrapping data..)
12873         // the outer template then get' applied with
12874         //     the store 'extra data'
12875         // and the body get's added to the
12876         //      roo-name="data" node?
12877         //      <span class='roo-tpl-{name}'></span> ?????
12878         
12879         
12880         
12881         this.clearSelections();
12882         this.el.update("");
12883         var html = [];
12884         var records = this.store.getRange();
12885         if(records.length < 1) {
12886             
12887             // is this valid??  = should it render a template??
12888             
12889             this.el.update(this.emptyText);
12890             return;
12891         }
12892         var el = this.el;
12893         if (this.dataName) {
12894             this.el.update(t.apply(this.store.meta)); //????
12895             el = this.el.child('.roo-tpl-' + this.dataName);
12896         }
12897         
12898         for(var i = 0, len = records.length; i < len; i++){
12899             var data = this.prepareData(records[i].data, i, records[i]);
12900             this.fireEvent("preparedata", this, data, i, records[i]);
12901             
12902             var d = Roo.apply({}, data);
12903             
12904             if(this.tickable){
12905                 Roo.apply(d, {'roo-id' : Roo.id()});
12906                 
12907                 var _this = this;
12908             
12909                 Roo.each(this.parent.item, function(item){
12910                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12911                         return;
12912                     }
12913                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12914                 });
12915             }
12916             
12917             html[html.length] = Roo.util.Format.trim(
12918                 this.dataName ?
12919                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12920                     t.apply(d)
12921             );
12922         }
12923         
12924         
12925         
12926         el.update(html.join(""));
12927         this.nodes = el.dom.childNodes;
12928         this.updateIndexes(0);
12929     },
12930     
12931
12932     /**
12933      * Function to override to reformat the data that is sent to
12934      * the template for each node.
12935      * DEPRICATED - use the preparedata event handler.
12936      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12937      * a JSON object for an UpdateManager bound view).
12938      */
12939     prepareData : function(data, index, record)
12940     {
12941         this.fireEvent("preparedata", this, data, index, record);
12942         return data;
12943     },
12944
12945     onUpdate : function(ds, record){
12946         // Roo.log('on update');   
12947         this.clearSelections();
12948         var index = this.store.indexOf(record);
12949         var n = this.nodes[index];
12950         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12951         n.parentNode.removeChild(n);
12952         this.updateIndexes(index, index);
12953     },
12954
12955     
12956     
12957 // --------- FIXME     
12958     onAdd : function(ds, records, index)
12959     {
12960         //Roo.log(['on Add', ds, records, index] );        
12961         this.clearSelections();
12962         if(this.nodes.length == 0){
12963             this.refresh();
12964             return;
12965         }
12966         var n = this.nodes[index];
12967         for(var i = 0, len = records.length; i < len; i++){
12968             var d = this.prepareData(records[i].data, i, records[i]);
12969             if(n){
12970                 this.tpl.insertBefore(n, d);
12971             }else{
12972                 
12973                 this.tpl.append(this.el, d);
12974             }
12975         }
12976         this.updateIndexes(index);
12977     },
12978
12979     onRemove : function(ds, record, index){
12980        // Roo.log('onRemove');
12981         this.clearSelections();
12982         var el = this.dataName  ?
12983             this.el.child('.roo-tpl-' + this.dataName) :
12984             this.el; 
12985         
12986         el.dom.removeChild(this.nodes[index]);
12987         this.updateIndexes(index);
12988     },
12989
12990     /**
12991      * Refresh an individual node.
12992      * @param {Number} index
12993      */
12994     refreshNode : function(index){
12995         this.onUpdate(this.store, this.store.getAt(index));
12996     },
12997
12998     updateIndexes : function(startIndex, endIndex){
12999         var ns = this.nodes;
13000         startIndex = startIndex || 0;
13001         endIndex = endIndex || ns.length - 1;
13002         for(var i = startIndex; i <= endIndex; i++){
13003             ns[i].nodeIndex = i;
13004         }
13005     },
13006
13007     /**
13008      * Changes the data store this view uses and refresh the view.
13009      * @param {Store} store
13010      */
13011     setStore : function(store, initial){
13012         if(!initial && this.store){
13013             this.store.un("datachanged", this.refresh);
13014             this.store.un("add", this.onAdd);
13015             this.store.un("remove", this.onRemove);
13016             this.store.un("update", this.onUpdate);
13017             this.store.un("clear", this.refresh);
13018             this.store.un("beforeload", this.onBeforeLoad);
13019             this.store.un("load", this.onLoad);
13020             this.store.un("loadexception", this.onLoad);
13021         }
13022         if(store){
13023           
13024             store.on("datachanged", this.refresh, this);
13025             store.on("add", this.onAdd, this);
13026             store.on("remove", this.onRemove, this);
13027             store.on("update", this.onUpdate, this);
13028             store.on("clear", this.refresh, this);
13029             store.on("beforeload", this.onBeforeLoad, this);
13030             store.on("load", this.onLoad, this);
13031             store.on("loadexception", this.onLoad, this);
13032         }
13033         
13034         if(store){
13035             this.refresh();
13036         }
13037     },
13038     /**
13039      * onbeforeLoad - masks the loading area.
13040      *
13041      */
13042     onBeforeLoad : function(store,opts)
13043     {
13044          //Roo.log('onBeforeLoad');   
13045         if (!opts.add) {
13046             this.el.update("");
13047         }
13048         this.el.mask(this.mask ? this.mask : "Loading" ); 
13049     },
13050     onLoad : function ()
13051     {
13052         this.el.unmask();
13053     },
13054     
13055
13056     /**
13057      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13058      * @param {HTMLElement} node
13059      * @return {HTMLElement} The template node
13060      */
13061     findItemFromChild : function(node){
13062         var el = this.dataName  ?
13063             this.el.child('.roo-tpl-' + this.dataName,true) :
13064             this.el.dom; 
13065         
13066         if(!node || node.parentNode == el){
13067                     return node;
13068             }
13069             var p = node.parentNode;
13070             while(p && p != el){
13071             if(p.parentNode == el){
13072                 return p;
13073             }
13074             p = p.parentNode;
13075         }
13076             return null;
13077     },
13078
13079     /** @ignore */
13080     onClick : function(e){
13081         var item = this.findItemFromChild(e.getTarget());
13082         if(item){
13083             var index = this.indexOf(item);
13084             if(this.onItemClick(item, index, e) !== false){
13085                 this.fireEvent("click", this, index, item, e);
13086             }
13087         }else{
13088             this.clearSelections();
13089         }
13090     },
13091
13092     /** @ignore */
13093     onContextMenu : function(e){
13094         var item = this.findItemFromChild(e.getTarget());
13095         if(item){
13096             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
13097         }
13098     },
13099
13100     /** @ignore */
13101     onDblClick : function(e){
13102         var item = this.findItemFromChild(e.getTarget());
13103         if(item){
13104             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
13105         }
13106     },
13107
13108     onItemClick : function(item, index, e)
13109     {
13110         if(this.fireEvent("beforeclick", this, index, item, e) === false){
13111             return false;
13112         }
13113         if (this.toggleSelect) {
13114             var m = this.isSelected(item) ? 'unselect' : 'select';
13115             //Roo.log(m);
13116             var _t = this;
13117             _t[m](item, true, false);
13118             return true;
13119         }
13120         if(this.multiSelect || this.singleSelect){
13121             if(this.multiSelect && e.shiftKey && this.lastSelection){
13122                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
13123             }else{
13124                 this.select(item, this.multiSelect && e.ctrlKey);
13125                 this.lastSelection = item;
13126             }
13127             
13128             if(!this.tickable){
13129                 e.preventDefault();
13130             }
13131             
13132         }
13133         return true;
13134     },
13135
13136     /**
13137      * Get the number of selected nodes.
13138      * @return {Number}
13139      */
13140     getSelectionCount : function(){
13141         return this.selections.length;
13142     },
13143
13144     /**
13145      * Get the currently selected nodes.
13146      * @return {Array} An array of HTMLElements
13147      */
13148     getSelectedNodes : function(){
13149         return this.selections;
13150     },
13151
13152     /**
13153      * Get the indexes of the selected nodes.
13154      * @return {Array}
13155      */
13156     getSelectedIndexes : function(){
13157         var indexes = [], s = this.selections;
13158         for(var i = 0, len = s.length; i < len; i++){
13159             indexes.push(s[i].nodeIndex);
13160         }
13161         return indexes;
13162     },
13163
13164     /**
13165      * Clear all selections
13166      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
13167      */
13168     clearSelections : function(suppressEvent){
13169         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
13170             this.cmp.elements = this.selections;
13171             this.cmp.removeClass(this.selectedClass);
13172             this.selections = [];
13173             if(!suppressEvent){
13174                 this.fireEvent("selectionchange", this, this.selections);
13175             }
13176         }
13177     },
13178
13179     /**
13180      * Returns true if the passed node is selected
13181      * @param {HTMLElement/Number} node The node or node index
13182      * @return {Boolean}
13183      */
13184     isSelected : function(node){
13185         var s = this.selections;
13186         if(s.length < 1){
13187             return false;
13188         }
13189         node = this.getNode(node);
13190         return s.indexOf(node) !== -1;
13191     },
13192
13193     /**
13194      * Selects nodes.
13195      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
13196      * @param {Boolean} keepExisting (optional) true to keep existing selections
13197      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13198      */
13199     select : function(nodeInfo, keepExisting, suppressEvent){
13200         if(nodeInfo instanceof Array){
13201             if(!keepExisting){
13202                 this.clearSelections(true);
13203             }
13204             for(var i = 0, len = nodeInfo.length; i < len; i++){
13205                 this.select(nodeInfo[i], true, true);
13206             }
13207             return;
13208         } 
13209         var node = this.getNode(nodeInfo);
13210         if(!node || this.isSelected(node)){
13211             return; // already selected.
13212         }
13213         if(!keepExisting){
13214             this.clearSelections(true);
13215         }
13216         
13217         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
13218             Roo.fly(node).addClass(this.selectedClass);
13219             this.selections.push(node);
13220             if(!suppressEvent){
13221                 this.fireEvent("selectionchange", this, this.selections);
13222             }
13223         }
13224         
13225         
13226     },
13227       /**
13228      * Unselects nodes.
13229      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
13230      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
13231      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13232      */
13233     unselect : function(nodeInfo, keepExisting, suppressEvent)
13234     {
13235         if(nodeInfo instanceof Array){
13236             Roo.each(this.selections, function(s) {
13237                 this.unselect(s, nodeInfo);
13238             }, this);
13239             return;
13240         }
13241         var node = this.getNode(nodeInfo);
13242         if(!node || !this.isSelected(node)){
13243             //Roo.log("not selected");
13244             return; // not selected.
13245         }
13246         // fireevent???
13247         var ns = [];
13248         Roo.each(this.selections, function(s) {
13249             if (s == node ) {
13250                 Roo.fly(node).removeClass(this.selectedClass);
13251
13252                 return;
13253             }
13254             ns.push(s);
13255         },this);
13256         
13257         this.selections= ns;
13258         this.fireEvent("selectionchange", this, this.selections);
13259     },
13260
13261     /**
13262      * Gets a template node.
13263      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13264      * @return {HTMLElement} The node or null if it wasn't found
13265      */
13266     getNode : function(nodeInfo){
13267         if(typeof nodeInfo == "string"){
13268             return document.getElementById(nodeInfo);
13269         }else if(typeof nodeInfo == "number"){
13270             return this.nodes[nodeInfo];
13271         }
13272         return nodeInfo;
13273     },
13274
13275     /**
13276      * Gets a range template nodes.
13277      * @param {Number} startIndex
13278      * @param {Number} endIndex
13279      * @return {Array} An array of nodes
13280      */
13281     getNodes : function(start, end){
13282         var ns = this.nodes;
13283         start = start || 0;
13284         end = typeof end == "undefined" ? ns.length - 1 : end;
13285         var nodes = [];
13286         if(start <= end){
13287             for(var i = start; i <= end; i++){
13288                 nodes.push(ns[i]);
13289             }
13290         } else{
13291             for(var i = start; i >= end; i--){
13292                 nodes.push(ns[i]);
13293             }
13294         }
13295         return nodes;
13296     },
13297
13298     /**
13299      * Finds the index of the passed node
13300      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13301      * @return {Number} The index of the node or -1
13302      */
13303     indexOf : function(node){
13304         node = this.getNode(node);
13305         if(typeof node.nodeIndex == "number"){
13306             return node.nodeIndex;
13307         }
13308         var ns = this.nodes;
13309         for(var i = 0, len = ns.length; i < len; i++){
13310             if(ns[i] == node){
13311                 return i;
13312             }
13313         }
13314         return -1;
13315     }
13316 });
13317 /*
13318  * - LGPL
13319  *
13320  * based on jquery fullcalendar
13321  * 
13322  */
13323
13324 Roo.bootstrap = Roo.bootstrap || {};
13325 /**
13326  * @class Roo.bootstrap.Calendar
13327  * @extends Roo.bootstrap.Component
13328  * Bootstrap Calendar class
13329  * @cfg {Boolean} loadMask (true|false) default false
13330  * @cfg {Object} header generate the user specific header of the calendar, default false
13331
13332  * @constructor
13333  * Create a new Container
13334  * @param {Object} config The config object
13335  */
13336
13337
13338
13339 Roo.bootstrap.Calendar = function(config){
13340     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
13341      this.addEvents({
13342         /**
13343              * @event select
13344              * Fires when a date is selected
13345              * @param {DatePicker} this
13346              * @param {Date} date The selected date
13347              */
13348         'select': true,
13349         /**
13350              * @event monthchange
13351              * Fires when the displayed month changes 
13352              * @param {DatePicker} this
13353              * @param {Date} date The selected month
13354              */
13355         'monthchange': true,
13356         /**
13357              * @event evententer
13358              * Fires when mouse over an event
13359              * @param {Calendar} this
13360              * @param {event} Event
13361              */
13362         'evententer': true,
13363         /**
13364              * @event eventleave
13365              * Fires when the mouse leaves an
13366              * @param {Calendar} this
13367              * @param {event}
13368              */
13369         'eventleave': true,
13370         /**
13371              * @event eventclick
13372              * Fires when the mouse click an
13373              * @param {Calendar} this
13374              * @param {event}
13375              */
13376         'eventclick': true
13377         
13378     });
13379
13380 };
13381
13382 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
13383     
13384      /**
13385      * @cfg {Number} startDay
13386      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
13387      */
13388     startDay : 0,
13389     
13390     loadMask : false,
13391     
13392     header : false,
13393       
13394     getAutoCreate : function(){
13395         
13396         
13397         var fc_button = function(name, corner, style, content ) {
13398             return Roo.apply({},{
13399                 tag : 'span',
13400                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
13401                          (corner.length ?
13402                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
13403                             ''
13404                         ),
13405                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
13406                 unselectable: 'on'
13407             });
13408         };
13409         
13410         var header = {};
13411         
13412         if(!this.header){
13413             header = {
13414                 tag : 'table',
13415                 cls : 'fc-header',
13416                 style : 'width:100%',
13417                 cn : [
13418                     {
13419                         tag: 'tr',
13420                         cn : [
13421                             {
13422                                 tag : 'td',
13423                                 cls : 'fc-header-left',
13424                                 cn : [
13425                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
13426                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
13427                                     { tag: 'span', cls: 'fc-header-space' },
13428                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
13429
13430
13431                                 ]
13432                             },
13433
13434                             {
13435                                 tag : 'td',
13436                                 cls : 'fc-header-center',
13437                                 cn : [
13438                                     {
13439                                         tag: 'span',
13440                                         cls: 'fc-header-title',
13441                                         cn : {
13442                                             tag: 'H2',
13443                                             html : 'month / year'
13444                                         }
13445                                     }
13446
13447                                 ]
13448                             },
13449                             {
13450                                 tag : 'td',
13451                                 cls : 'fc-header-right',
13452                                 cn : [
13453                               /*      fc_button('month', 'left', '', 'month' ),
13454                                     fc_button('week', '', '', 'week' ),
13455                                     fc_button('day', 'right', '', 'day' )
13456                                 */    
13457
13458                                 ]
13459                             }
13460
13461                         ]
13462                     }
13463                 ]
13464             };
13465         }
13466         
13467         header = this.header;
13468         
13469        
13470         var cal_heads = function() {
13471             var ret = [];
13472             // fixme - handle this.
13473             
13474             for (var i =0; i < Date.dayNames.length; i++) {
13475                 var d = Date.dayNames[i];
13476                 ret.push({
13477                     tag: 'th',
13478                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13479                     html : d.substring(0,3)
13480                 });
13481                 
13482             }
13483             ret[0].cls += ' fc-first';
13484             ret[6].cls += ' fc-last';
13485             return ret;
13486         };
13487         var cal_cell = function(n) {
13488             return  {
13489                 tag: 'td',
13490                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13491                 cn : [
13492                     {
13493                         cn : [
13494                             {
13495                                 cls: 'fc-day-number',
13496                                 html: 'D'
13497                             },
13498                             {
13499                                 cls: 'fc-day-content',
13500                              
13501                                 cn : [
13502                                      {
13503                                         style: 'position: relative;' // height: 17px;
13504                                     }
13505                                 ]
13506                             }
13507                             
13508                             
13509                         ]
13510                     }
13511                 ]
13512                 
13513             }
13514         };
13515         var cal_rows = function() {
13516             
13517             var ret = [];
13518             for (var r = 0; r < 6; r++) {
13519                 var row= {
13520                     tag : 'tr',
13521                     cls : 'fc-week',
13522                     cn : []
13523                 };
13524                 
13525                 for (var i =0; i < Date.dayNames.length; i++) {
13526                     var d = Date.dayNames[i];
13527                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13528
13529                 }
13530                 row.cn[0].cls+=' fc-first';
13531                 row.cn[0].cn[0].style = 'min-height:90px';
13532                 row.cn[6].cls+=' fc-last';
13533                 ret.push(row);
13534                 
13535             }
13536             ret[0].cls += ' fc-first';
13537             ret[4].cls += ' fc-prev-last';
13538             ret[5].cls += ' fc-last';
13539             return ret;
13540             
13541         };
13542         
13543         var cal_table = {
13544             tag: 'table',
13545             cls: 'fc-border-separate',
13546             style : 'width:100%',
13547             cellspacing  : 0,
13548             cn : [
13549                 { 
13550                     tag: 'thead',
13551                     cn : [
13552                         { 
13553                             tag: 'tr',
13554                             cls : 'fc-first fc-last',
13555                             cn : cal_heads()
13556                         }
13557                     ]
13558                 },
13559                 { 
13560                     tag: 'tbody',
13561                     cn : cal_rows()
13562                 }
13563                   
13564             ]
13565         };
13566          
13567          var cfg = {
13568             cls : 'fc fc-ltr',
13569             cn : [
13570                 header,
13571                 {
13572                     cls : 'fc-content',
13573                     style : "position: relative;",
13574                     cn : [
13575                         {
13576                             cls : 'fc-view fc-view-month fc-grid',
13577                             style : 'position: relative',
13578                             unselectable : 'on',
13579                             cn : [
13580                                 {
13581                                     cls : 'fc-event-container',
13582                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13583                                 },
13584                                 cal_table
13585                             ]
13586                         }
13587                     ]
13588     
13589                 }
13590            ] 
13591             
13592         };
13593         
13594          
13595         
13596         return cfg;
13597     },
13598     
13599     
13600     initEvents : function()
13601     {
13602         if(!this.store){
13603             throw "can not find store for calendar";
13604         }
13605         
13606         var mark = {
13607             tag: "div",
13608             cls:"x-dlg-mask",
13609             style: "text-align:center",
13610             cn: [
13611                 {
13612                     tag: "div",
13613                     style: "background-color:white;width:50%;margin:250 auto",
13614                     cn: [
13615                         {
13616                             tag: "img",
13617                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13618                         },
13619                         {
13620                             tag: "span",
13621                             html: "Loading"
13622                         }
13623                         
13624                     ]
13625                 }
13626             ]
13627         }
13628         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13629         
13630         var size = this.el.select('.fc-content', true).first().getSize();
13631         this.maskEl.setSize(size.width, size.height);
13632         this.maskEl.enableDisplayMode("block");
13633         if(!this.loadMask){
13634             this.maskEl.hide();
13635         }
13636         
13637         this.store = Roo.factory(this.store, Roo.data);
13638         this.store.on('load', this.onLoad, this);
13639         this.store.on('beforeload', this.onBeforeLoad, this);
13640         
13641         this.resize();
13642         
13643         this.cells = this.el.select('.fc-day',true);
13644         //Roo.log(this.cells);
13645         this.textNodes = this.el.query('.fc-day-number');
13646         this.cells.addClassOnOver('fc-state-hover');
13647         
13648         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13649         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13650         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13651         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13652         
13653         this.on('monthchange', this.onMonthChange, this);
13654         
13655         this.update(new Date().clearTime());
13656     },
13657     
13658     resize : function() {
13659         var sz  = this.el.getSize();
13660         
13661         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13662         this.el.select('.fc-day-content div',true).setHeight(34);
13663     },
13664     
13665     
13666     // private
13667     showPrevMonth : function(e){
13668         this.update(this.activeDate.add("mo", -1));
13669     },
13670     showToday : function(e){
13671         this.update(new Date().clearTime());
13672     },
13673     // private
13674     showNextMonth : function(e){
13675         this.update(this.activeDate.add("mo", 1));
13676     },
13677
13678     // private
13679     showPrevYear : function(){
13680         this.update(this.activeDate.add("y", -1));
13681     },
13682
13683     // private
13684     showNextYear : function(){
13685         this.update(this.activeDate.add("y", 1));
13686     },
13687
13688     
13689    // private
13690     update : function(date)
13691     {
13692         var vd = this.activeDate;
13693         this.activeDate = date;
13694 //        if(vd && this.el){
13695 //            var t = date.getTime();
13696 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13697 //                Roo.log('using add remove');
13698 //                
13699 //                this.fireEvent('monthchange', this, date);
13700 //                
13701 //                this.cells.removeClass("fc-state-highlight");
13702 //                this.cells.each(function(c){
13703 //                   if(c.dateValue == t){
13704 //                       c.addClass("fc-state-highlight");
13705 //                       setTimeout(function(){
13706 //                            try{c.dom.firstChild.focus();}catch(e){}
13707 //                       }, 50);
13708 //                       return false;
13709 //                   }
13710 //                   return true;
13711 //                });
13712 //                return;
13713 //            }
13714 //        }
13715         
13716         var days = date.getDaysInMonth();
13717         
13718         var firstOfMonth = date.getFirstDateOfMonth();
13719         var startingPos = firstOfMonth.getDay()-this.startDay;
13720         
13721         if(startingPos < this.startDay){
13722             startingPos += 7;
13723         }
13724         
13725         var pm = date.add(Date.MONTH, -1);
13726         var prevStart = pm.getDaysInMonth()-startingPos;
13727 //        
13728         this.cells = this.el.select('.fc-day',true);
13729         this.textNodes = this.el.query('.fc-day-number');
13730         this.cells.addClassOnOver('fc-state-hover');
13731         
13732         var cells = this.cells.elements;
13733         var textEls = this.textNodes;
13734         
13735         Roo.each(cells, function(cell){
13736             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13737         });
13738         
13739         days += startingPos;
13740
13741         // convert everything to numbers so it's fast
13742         var day = 86400000;
13743         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13744         //Roo.log(d);
13745         //Roo.log(pm);
13746         //Roo.log(prevStart);
13747         
13748         var today = new Date().clearTime().getTime();
13749         var sel = date.clearTime().getTime();
13750         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13751         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13752         var ddMatch = this.disabledDatesRE;
13753         var ddText = this.disabledDatesText;
13754         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13755         var ddaysText = this.disabledDaysText;
13756         var format = this.format;
13757         
13758         var setCellClass = function(cal, cell){
13759             cell.row = 0;
13760             cell.events = [];
13761             cell.more = [];
13762             //Roo.log('set Cell Class');
13763             cell.title = "";
13764             var t = d.getTime();
13765             
13766             //Roo.log(d);
13767             
13768             cell.dateValue = t;
13769             if(t == today){
13770                 cell.className += " fc-today";
13771                 cell.className += " fc-state-highlight";
13772                 cell.title = cal.todayText;
13773             }
13774             if(t == sel){
13775                 // disable highlight in other month..
13776                 //cell.className += " fc-state-highlight";
13777                 
13778             }
13779             // disabling
13780             if(t < min) {
13781                 cell.className = " fc-state-disabled";
13782                 cell.title = cal.minText;
13783                 return;
13784             }
13785             if(t > max) {
13786                 cell.className = " fc-state-disabled";
13787                 cell.title = cal.maxText;
13788                 return;
13789             }
13790             if(ddays){
13791                 if(ddays.indexOf(d.getDay()) != -1){
13792                     cell.title = ddaysText;
13793                     cell.className = " fc-state-disabled";
13794                 }
13795             }
13796             if(ddMatch && format){
13797                 var fvalue = d.dateFormat(format);
13798                 if(ddMatch.test(fvalue)){
13799                     cell.title = ddText.replace("%0", fvalue);
13800                     cell.className = " fc-state-disabled";
13801                 }
13802             }
13803             
13804             if (!cell.initialClassName) {
13805                 cell.initialClassName = cell.dom.className;
13806             }
13807             
13808             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13809         };
13810
13811         var i = 0;
13812         
13813         for(; i < startingPos; i++) {
13814             textEls[i].innerHTML = (++prevStart);
13815             d.setDate(d.getDate()+1);
13816             
13817             cells[i].className = "fc-past fc-other-month";
13818             setCellClass(this, cells[i]);
13819         }
13820         
13821         var intDay = 0;
13822         
13823         for(; i < days; i++){
13824             intDay = i - startingPos + 1;
13825             textEls[i].innerHTML = (intDay);
13826             d.setDate(d.getDate()+1);
13827             
13828             cells[i].className = ''; // "x-date-active";
13829             setCellClass(this, cells[i]);
13830         }
13831         var extraDays = 0;
13832         
13833         for(; i < 42; i++) {
13834             textEls[i].innerHTML = (++extraDays);
13835             d.setDate(d.getDate()+1);
13836             
13837             cells[i].className = "fc-future fc-other-month";
13838             setCellClass(this, cells[i]);
13839         }
13840         
13841         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13842         
13843         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13844         
13845         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13846         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13847         
13848         if(totalRows != 6){
13849             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13850             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13851         }
13852         
13853         this.fireEvent('monthchange', this, date);
13854         
13855         
13856         /*
13857         if(!this.internalRender){
13858             var main = this.el.dom.firstChild;
13859             var w = main.offsetWidth;
13860             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13861             Roo.fly(main).setWidth(w);
13862             this.internalRender = true;
13863             // opera does not respect the auto grow header center column
13864             // then, after it gets a width opera refuses to recalculate
13865             // without a second pass
13866             if(Roo.isOpera && !this.secondPass){
13867                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13868                 this.secondPass = true;
13869                 this.update.defer(10, this, [date]);
13870             }
13871         }
13872         */
13873         
13874     },
13875     
13876     findCell : function(dt) {
13877         dt = dt.clearTime().getTime();
13878         var ret = false;
13879         this.cells.each(function(c){
13880             //Roo.log("check " +c.dateValue + '?=' + dt);
13881             if(c.dateValue == dt){
13882                 ret = c;
13883                 return false;
13884             }
13885             return true;
13886         });
13887         
13888         return ret;
13889     },
13890     
13891     findCells : function(ev) {
13892         var s = ev.start.clone().clearTime().getTime();
13893        // Roo.log(s);
13894         var e= ev.end.clone().clearTime().getTime();
13895        // Roo.log(e);
13896         var ret = [];
13897         this.cells.each(function(c){
13898              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13899             
13900             if(c.dateValue > e){
13901                 return ;
13902             }
13903             if(c.dateValue < s){
13904                 return ;
13905             }
13906             ret.push(c);
13907         });
13908         
13909         return ret;    
13910     },
13911     
13912 //    findBestRow: function(cells)
13913 //    {
13914 //        var ret = 0;
13915 //        
13916 //        for (var i =0 ; i < cells.length;i++) {
13917 //            ret  = Math.max(cells[i].rows || 0,ret);
13918 //        }
13919 //        return ret;
13920 //        
13921 //    },
13922     
13923     
13924     addItem : function(ev)
13925     {
13926         // look for vertical location slot in
13927         var cells = this.findCells(ev);
13928         
13929 //        ev.row = this.findBestRow(cells);
13930         
13931         // work out the location.
13932         
13933         var crow = false;
13934         var rows = [];
13935         for(var i =0; i < cells.length; i++) {
13936             
13937             cells[i].row = cells[0].row;
13938             
13939             if(i == 0){
13940                 cells[i].row = cells[i].row + 1;
13941             }
13942             
13943             if (!crow) {
13944                 crow = {
13945                     start : cells[i],
13946                     end :  cells[i]
13947                 };
13948                 continue;
13949             }
13950             if (crow.start.getY() == cells[i].getY()) {
13951                 // on same row.
13952                 crow.end = cells[i];
13953                 continue;
13954             }
13955             // different row.
13956             rows.push(crow);
13957             crow = {
13958                 start: cells[i],
13959                 end : cells[i]
13960             };
13961             
13962         }
13963         
13964         rows.push(crow);
13965         ev.els = [];
13966         ev.rows = rows;
13967         ev.cells = cells;
13968         
13969         cells[0].events.push(ev);
13970         
13971         this.calevents.push(ev);
13972     },
13973     
13974     clearEvents: function() {
13975         
13976         if(!this.calevents){
13977             return;
13978         }
13979         
13980         Roo.each(this.cells.elements, function(c){
13981             c.row = 0;
13982             c.events = [];
13983             c.more = [];
13984         });
13985         
13986         Roo.each(this.calevents, function(e) {
13987             Roo.each(e.els, function(el) {
13988                 el.un('mouseenter' ,this.onEventEnter, this);
13989                 el.un('mouseleave' ,this.onEventLeave, this);
13990                 el.remove();
13991             },this);
13992         },this);
13993         
13994         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13995             e.remove();
13996         });
13997         
13998     },
13999     
14000     renderEvents: function()
14001     {   
14002         var _this = this;
14003         
14004         this.cells.each(function(c) {
14005             
14006             if(c.row < 5){
14007                 return;
14008             }
14009             
14010             var ev = c.events;
14011             
14012             var r = 4;
14013             if(c.row != c.events.length){
14014                 r = 4 - (4 - (c.row - c.events.length));
14015             }
14016             
14017             c.events = ev.slice(0, r);
14018             c.more = ev.slice(r);
14019             
14020             if(c.more.length && c.more.length == 1){
14021                 c.events.push(c.more.pop());
14022             }
14023             
14024             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14025             
14026         });
14027             
14028         this.cells.each(function(c) {
14029             
14030             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14031             
14032             
14033             for (var e = 0; e < c.events.length; e++){
14034                 var ev = c.events[e];
14035                 var rows = ev.rows;
14036                 
14037                 for(var i = 0; i < rows.length; i++) {
14038                 
14039                     // how many rows should it span..
14040
14041                     var  cfg = {
14042                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14043                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14044
14045                         unselectable : "on",
14046                         cn : [
14047                             {
14048                                 cls: 'fc-event-inner',
14049                                 cn : [
14050     //                                {
14051     //                                  tag:'span',
14052     //                                  cls: 'fc-event-time',
14053     //                                  html : cells.length > 1 ? '' : ev.time
14054     //                                },
14055                                     {
14056                                       tag:'span',
14057                                       cls: 'fc-event-title',
14058                                       html : String.format('{0}', ev.title)
14059                                     }
14060
14061
14062                                 ]
14063                             },
14064                             {
14065                                 cls: 'ui-resizable-handle ui-resizable-e',
14066                                 html : '&nbsp;&nbsp;&nbsp'
14067                             }
14068
14069                         ]
14070                     };
14071
14072                     if (i == 0) {
14073                         cfg.cls += ' fc-event-start';
14074                     }
14075                     if ((i+1) == rows.length) {
14076                         cfg.cls += ' fc-event-end';
14077                     }
14078
14079                     var ctr = _this.el.select('.fc-event-container',true).first();
14080                     var cg = ctr.createChild(cfg);
14081
14082                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
14083                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
14084
14085                     var r = (c.more.length) ? 1 : 0;
14086                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
14087                     cg.setWidth(ebox.right - sbox.x -2);
14088
14089                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
14090                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
14091                     cg.on('click', _this.onEventClick, _this, ev);
14092
14093                     ev.els.push(cg);
14094                     
14095                 }
14096                 
14097             }
14098             
14099             
14100             if(c.more.length){
14101                 var  cfg = {
14102                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
14103                     style : 'position: absolute',
14104                     unselectable : "on",
14105                     cn : [
14106                         {
14107                             cls: 'fc-event-inner',
14108                             cn : [
14109                                 {
14110                                   tag:'span',
14111                                   cls: 'fc-event-title',
14112                                   html : 'More'
14113                                 }
14114
14115
14116                             ]
14117                         },
14118                         {
14119                             cls: 'ui-resizable-handle ui-resizable-e',
14120                             html : '&nbsp;&nbsp;&nbsp'
14121                         }
14122
14123                     ]
14124                 };
14125
14126                 var ctr = _this.el.select('.fc-event-container',true).first();
14127                 var cg = ctr.createChild(cfg);
14128
14129                 var sbox = c.select('.fc-day-content',true).first().getBox();
14130                 var ebox = c.select('.fc-day-content',true).first().getBox();
14131                 //Roo.log(cg);
14132                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
14133                 cg.setWidth(ebox.right - sbox.x -2);
14134
14135                 cg.on('click', _this.onMoreEventClick, _this, c.more);
14136                 
14137             }
14138             
14139         });
14140         
14141         
14142         
14143     },
14144     
14145     onEventEnter: function (e, el,event,d) {
14146         this.fireEvent('evententer', this, el, event);
14147     },
14148     
14149     onEventLeave: function (e, el,event,d) {
14150         this.fireEvent('eventleave', this, el, event);
14151     },
14152     
14153     onEventClick: function (e, el,event,d) {
14154         this.fireEvent('eventclick', this, el, event);
14155     },
14156     
14157     onMonthChange: function () {
14158         this.store.load();
14159     },
14160     
14161     onMoreEventClick: function(e, el, more)
14162     {
14163         var _this = this;
14164         
14165         this.calpopover.placement = 'right';
14166         this.calpopover.setTitle('More');
14167         
14168         this.calpopover.setContent('');
14169         
14170         var ctr = this.calpopover.el.select('.popover-content', true).first();
14171         
14172         Roo.each(more, function(m){
14173             var cfg = {
14174                 cls : 'fc-event-hori fc-event-draggable',
14175                 html : m.title
14176             }
14177             var cg = ctr.createChild(cfg);
14178             
14179             cg.on('click', _this.onEventClick, _this, m);
14180         });
14181         
14182         this.calpopover.show(el);
14183         
14184         
14185     },
14186     
14187     onLoad: function () 
14188     {   
14189         this.calevents = [];
14190         var cal = this;
14191         
14192         if(this.store.getCount() > 0){
14193             this.store.data.each(function(d){
14194                cal.addItem({
14195                     id : d.data.id,
14196                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
14197                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
14198                     time : d.data.start_time,
14199                     title : d.data.title,
14200                     description : d.data.description,
14201                     venue : d.data.venue
14202                 });
14203             });
14204         }
14205         
14206         this.renderEvents();
14207         
14208         if(this.calevents.length && this.loadMask){
14209             this.maskEl.hide();
14210         }
14211     },
14212     
14213     onBeforeLoad: function()
14214     {
14215         this.clearEvents();
14216         if(this.loadMask){
14217             this.maskEl.show();
14218         }
14219     }
14220 });
14221
14222  
14223  /*
14224  * - LGPL
14225  *
14226  * element
14227  * 
14228  */
14229
14230 /**
14231  * @class Roo.bootstrap.Popover
14232  * @extends Roo.bootstrap.Component
14233  * Bootstrap Popover class
14234  * @cfg {String} html contents of the popover   (or false to use children..)
14235  * @cfg {String} title of popover (or false to hide)
14236  * @cfg {String} placement how it is placed
14237  * @cfg {String} trigger click || hover (or false to trigger manually)
14238  * @cfg {String} over what (parent or false to trigger manually.)
14239  * @cfg {Number} delay - delay before showing
14240  
14241  * @constructor
14242  * Create a new Popover
14243  * @param {Object} config The config object
14244  */
14245
14246 Roo.bootstrap.Popover = function(config){
14247     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
14248 };
14249
14250 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
14251     
14252     title: 'Fill in a title',
14253     html: false,
14254     
14255     placement : 'right',
14256     trigger : 'hover', // hover
14257     
14258     delay : 0,
14259     
14260     over: 'parent',
14261     
14262     can_build_overlaid : false,
14263     
14264     getChildContainer : function()
14265     {
14266         return this.el.select('.popover-content',true).first();
14267     },
14268     
14269     getAutoCreate : function(){
14270          Roo.log('make popover?');
14271         var cfg = {
14272            cls : 'popover roo-dynamic',
14273            style: 'display:block',
14274            cn : [
14275                 {
14276                     cls : 'arrow'
14277                 },
14278                 {
14279                     cls : 'popover-inner',
14280                     cn : [
14281                         {
14282                             tag: 'h3',
14283                             cls: 'popover-title',
14284                             html : this.title
14285                         },
14286                         {
14287                             cls : 'popover-content',
14288                             html : this.html
14289                         }
14290                     ]
14291                     
14292                 }
14293            ]
14294         };
14295         
14296         return cfg;
14297     },
14298     setTitle: function(str)
14299     {
14300         this.el.select('.popover-title',true).first().dom.innerHTML = str;
14301     },
14302     setContent: function(str)
14303     {
14304         this.el.select('.popover-content',true).first().dom.innerHTML = str;
14305     },
14306     // as it get's added to the bottom of the page.
14307     onRender : function(ct, position)
14308     {
14309         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
14310         if(!this.el){
14311             var cfg = Roo.apply({},  this.getAutoCreate());
14312             cfg.id = Roo.id();
14313             
14314             if (this.cls) {
14315                 cfg.cls += ' ' + this.cls;
14316             }
14317             if (this.style) {
14318                 cfg.style = this.style;
14319             }
14320             Roo.log("adding to ")
14321             this.el = Roo.get(document.body).createChild(cfg, position);
14322             Roo.log(this.el);
14323         }
14324         this.initEvents();
14325     },
14326     
14327     initEvents : function()
14328     {
14329         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
14330         this.el.enableDisplayMode('block');
14331         this.el.hide();
14332         if (this.over === false) {
14333             return; 
14334         }
14335         if (this.triggers === false) {
14336             return;
14337         }
14338         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14339         var triggers = this.trigger ? this.trigger.split(' ') : [];
14340         Roo.each(triggers, function(trigger) {
14341         
14342             if (trigger == 'click') {
14343                 on_el.on('click', this.toggle, this);
14344             } else if (trigger != 'manual') {
14345                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
14346                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
14347       
14348                 on_el.on(eventIn  ,this.enter, this);
14349                 on_el.on(eventOut, this.leave, this);
14350             }
14351         }, this);
14352         
14353     },
14354     
14355     
14356     // private
14357     timeout : null,
14358     hoverState : null,
14359     
14360     toggle : function () {
14361         this.hoverState == 'in' ? this.leave() : this.enter();
14362     },
14363     
14364     enter : function () {
14365        
14366     
14367         clearTimeout(this.timeout);
14368     
14369         this.hoverState = 'in';
14370     
14371         if (!this.delay || !this.delay.show) {
14372             this.show();
14373             return;
14374         }
14375         var _t = this;
14376         this.timeout = setTimeout(function () {
14377             if (_t.hoverState == 'in') {
14378                 _t.show();
14379             }
14380         }, this.delay.show)
14381     },
14382     leave : function() {
14383         clearTimeout(this.timeout);
14384     
14385         this.hoverState = 'out';
14386     
14387         if (!this.delay || !this.delay.hide) {
14388             this.hide();
14389             return;
14390         }
14391         var _t = this;
14392         this.timeout = setTimeout(function () {
14393             if (_t.hoverState == 'out') {
14394                 _t.hide();
14395             }
14396         }, this.delay.hide)
14397     },
14398     
14399     show : function (on_el)
14400     {
14401         if (!on_el) {
14402             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14403         }
14404         // set content.
14405         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
14406         if (this.html !== false) {
14407             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
14408         }
14409         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
14410         if (!this.title.length) {
14411             this.el.select('.popover-title',true).hide();
14412         }
14413         
14414         var placement = typeof this.placement == 'function' ?
14415             this.placement.call(this, this.el, on_el) :
14416             this.placement;
14417             
14418         var autoToken = /\s?auto?\s?/i;
14419         var autoPlace = autoToken.test(placement);
14420         if (autoPlace) {
14421             placement = placement.replace(autoToken, '') || 'top';
14422         }
14423         
14424         //this.el.detach()
14425         //this.el.setXY([0,0]);
14426         this.el.show();
14427         this.el.dom.style.display='block';
14428         this.el.addClass(placement);
14429         
14430         //this.el.appendTo(on_el);
14431         
14432         var p = this.getPosition();
14433         var box = this.el.getBox();
14434         
14435         if (autoPlace) {
14436             // fixme..
14437         }
14438         var align = Roo.bootstrap.Popover.alignment[placement];
14439         this.el.alignTo(on_el, align[0],align[1]);
14440         //var arrow = this.el.select('.arrow',true).first();
14441         //arrow.set(align[2], 
14442         
14443         this.el.addClass('in');
14444         this.hoverState = null;
14445         
14446         if (this.el.hasClass('fade')) {
14447             // fade it?
14448         }
14449         
14450     },
14451     hide : function()
14452     {
14453         this.el.setXY([0,0]);
14454         this.el.removeClass('in');
14455         this.el.hide();
14456         
14457     }
14458     
14459 });
14460
14461 Roo.bootstrap.Popover.alignment = {
14462     'left' : ['r-l', [-10,0], 'right'],
14463     'right' : ['l-r', [10,0], 'left'],
14464     'bottom' : ['t-b', [0,10], 'top'],
14465     'top' : [ 'b-t', [0,-10], 'bottom']
14466 };
14467
14468  /*
14469  * - LGPL
14470  *
14471  * Progress
14472  * 
14473  */
14474
14475 /**
14476  * @class Roo.bootstrap.Progress
14477  * @extends Roo.bootstrap.Component
14478  * Bootstrap Progress class
14479  * @cfg {Boolean} striped striped of the progress bar
14480  * @cfg {Boolean} active animated of the progress bar
14481  * 
14482  * 
14483  * @constructor
14484  * Create a new Progress
14485  * @param {Object} config The config object
14486  */
14487
14488 Roo.bootstrap.Progress = function(config){
14489     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14490 };
14491
14492 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14493     
14494     striped : false,
14495     active: false,
14496     
14497     getAutoCreate : function(){
14498         var cfg = {
14499             tag: 'div',
14500             cls: 'progress'
14501         };
14502         
14503         
14504         if(this.striped){
14505             cfg.cls += ' progress-striped';
14506         }
14507       
14508         if(this.active){
14509             cfg.cls += ' active';
14510         }
14511         
14512         
14513         return cfg;
14514     }
14515    
14516 });
14517
14518  
14519
14520  /*
14521  * - LGPL
14522  *
14523  * ProgressBar
14524  * 
14525  */
14526
14527 /**
14528  * @class Roo.bootstrap.ProgressBar
14529  * @extends Roo.bootstrap.Component
14530  * Bootstrap ProgressBar class
14531  * @cfg {Number} aria_valuenow aria-value now
14532  * @cfg {Number} aria_valuemin aria-value min
14533  * @cfg {Number} aria_valuemax aria-value max
14534  * @cfg {String} label label for the progress bar
14535  * @cfg {String} panel (success | info | warning | danger )
14536  * @cfg {String} role role of the progress bar
14537  * @cfg {String} sr_only text
14538  * 
14539  * 
14540  * @constructor
14541  * Create a new ProgressBar
14542  * @param {Object} config The config object
14543  */
14544
14545 Roo.bootstrap.ProgressBar = function(config){
14546     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14547 };
14548
14549 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14550     
14551     aria_valuenow : 0,
14552     aria_valuemin : 0,
14553     aria_valuemax : 100,
14554     label : false,
14555     panel : false,
14556     role : false,
14557     sr_only: false,
14558     
14559     getAutoCreate : function()
14560     {
14561         
14562         var cfg = {
14563             tag: 'div',
14564             cls: 'progress-bar',
14565             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14566         };
14567         
14568         if(this.sr_only){
14569             cfg.cn = {
14570                 tag: 'span',
14571                 cls: 'sr-only',
14572                 html: this.sr_only
14573             }
14574         }
14575         
14576         if(this.role){
14577             cfg.role = this.role;
14578         }
14579         
14580         if(this.aria_valuenow){
14581             cfg['aria-valuenow'] = this.aria_valuenow;
14582         }
14583         
14584         if(this.aria_valuemin){
14585             cfg['aria-valuemin'] = this.aria_valuemin;
14586         }
14587         
14588         if(this.aria_valuemax){
14589             cfg['aria-valuemax'] = this.aria_valuemax;
14590         }
14591         
14592         if(this.label && !this.sr_only){
14593             cfg.html = this.label;
14594         }
14595         
14596         if(this.panel){
14597             cfg.cls += ' progress-bar-' + this.panel;
14598         }
14599         
14600         return cfg;
14601     },
14602     
14603     update : function(aria_valuenow)
14604     {
14605         this.aria_valuenow = aria_valuenow;
14606         
14607         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14608     }
14609    
14610 });
14611
14612  
14613
14614  /*
14615  * - LGPL
14616  *
14617  * column
14618  * 
14619  */
14620
14621 /**
14622  * @class Roo.bootstrap.TabGroup
14623  * @extends Roo.bootstrap.Column
14624  * Bootstrap Column class
14625  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14626  * @cfg {Boolean} carousel true to make the group behave like a carousel
14627  * @cfg {Number} bullets show the panel pointer.. default 0
14628  * @cfg {String} bullets_cls apply the cls to bullets.. default false
14629  * @cfg {Boolena} autoslide (true|false) auto slide .. default false
14630  * @cfg {Number} timer auto slide timer .. default 0 millisecond
14631  * 
14632  * @constructor
14633  * Create a new TabGroup
14634  * @param {Object} config The config object
14635  */
14636
14637 Roo.bootstrap.TabGroup = function(config){
14638     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14639     if (!this.navId) {
14640         this.navId = Roo.id();
14641     }
14642     this.tabs = [];
14643     Roo.bootstrap.TabGroup.register(this);
14644     
14645 };
14646
14647 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14648     
14649     carousel : false,
14650     transition : false,
14651     bullets : 0,
14652     timer : 0,
14653     autoslide : false,
14654     slideFn : false,
14655     bullets_cls : false,
14656     
14657     getAutoCreate : function()
14658     {
14659         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14660         
14661         cfg.cls += ' tab-content';
14662         
14663         Roo.log('get auto create...............');
14664         
14665         if (this.carousel) {
14666             cfg.cls += ' carousel slide';
14667             
14668             cfg.cn = [{
14669                cls : 'carousel-inner'
14670             }];
14671         
14672             if(this.bullets > 0){
14673                 
14674                 var bullets = {
14675                     cls : 'carousel-bullets',
14676                     cn : []
14677                 };
14678                 
14679                 if(this.bullets_cls){
14680                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
14681                 }
14682                 
14683                 for (var i = 0; i < this.bullets; i++){
14684                     bullets.cn.push({
14685                         cls : 'bullet bullet-' + i
14686                     });
14687                 }
14688                 
14689                 bullets.cn.push({
14690                     cls : 'clear'
14691                 });
14692                 
14693                 cfg.cn[0].cn = bullets;
14694             }
14695         }
14696         
14697         return cfg;
14698     },
14699     
14700     initEvents:  function()
14701     {
14702         Roo.log('-------- init events on tab group ---------');
14703         
14704         var _this = this;
14705         
14706         if(this.bullets > 0){
14707             
14708             for (var i = 0; i < this.bullets; i++){
14709                 var bullet = this.el.select('.bullet-' + i, true).first();
14710                 
14711                 if(!bullet){
14712                     continue;
14713                 }
14714                 
14715                 bullet.on('click', (function(e, el, o, ii, t){
14716                     
14717                     e.preventDefault();
14718                     
14719                     _this.showPanel(ii);
14720                     
14721                     if(_this.autoslide && _this.slideFn){
14722                         clearInterval(_this.slideFn);
14723                         _this.slideFn = window.setInterval(function() {
14724                             _this.showPanelNext();
14725                         }, _this.timer);
14726                     }
14727                     
14728                 }).createDelegate(this, [i, bullet], true));
14729             }
14730         }
14731         
14732         if(this.autoslide){
14733             this.slideFn = window.setInterval(function() {
14734                 _this.showPanelNext();
14735             }, this.timer);
14736         }
14737     },
14738     
14739     getChildContainer : function()
14740     {
14741         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14742     },
14743     
14744     /**
14745     * register a Navigation item
14746     * @param {Roo.bootstrap.NavItem} the navitem to add
14747     */
14748     register : function(item)
14749     {
14750         this.tabs.push( item);
14751         item.navId = this.navId; // not really needed..
14752     
14753     },
14754     
14755     getActivePanel : function()
14756     {
14757         var r = false;
14758         Roo.each(this.tabs, function(t) {
14759             if (t.active) {
14760                 r = t;
14761                 return false;
14762             }
14763             return null;
14764         });
14765         return r;
14766         
14767     },
14768     getPanelByName : function(n)
14769     {
14770         var r = false;
14771         Roo.each(this.tabs, function(t) {
14772             if (t.tabId == n) {
14773                 r = t;
14774                 return false;
14775             }
14776             return null;
14777         });
14778         return r;
14779     },
14780     indexOfPanel : function(p)
14781     {
14782         var r = false;
14783         Roo.each(this.tabs, function(t,i) {
14784             if (t.tabId == p.tabId) {
14785                 r = i;
14786                 return false;
14787             }
14788             return null;
14789         });
14790         return r;
14791     },
14792     /**
14793      * show a specific panel
14794      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14795      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14796      */
14797     showPanel : function (pan)
14798     {
14799         if(this.transition){
14800             Roo.log("waiting for the transitionend");
14801             return;
14802         }
14803         
14804         if (typeof(pan) == 'number') {
14805             pan = this.tabs[pan];
14806         }
14807         if (typeof(pan) == 'string') {
14808             pan = this.getPanelByName(pan);
14809         }
14810         if (pan.tabId == this.getActivePanel().tabId) {
14811             return true;
14812         }
14813         var cur = this.getActivePanel();
14814         
14815         if (false === cur.fireEvent('beforedeactivate')) {
14816             return false;
14817         }
14818         
14819         if(this.bullets > 0){
14820             this.setActiveBullet(this.indexOfPanel(pan));
14821         }
14822         
14823         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
14824             
14825             this.transition = true;
14826             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14827             var lr = dir == 'next' ? 'left' : 'right';
14828             pan.el.addClass(dir); // or prev
14829             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14830             cur.el.addClass(lr); // or right
14831             pan.el.addClass(lr);
14832             
14833             var _this = this;
14834             cur.el.on('transitionend', function() {
14835                 Roo.log("trans end?");
14836                 
14837                 pan.el.removeClass([lr,dir]);
14838                 pan.setActive(true);
14839                 
14840                 cur.el.removeClass([lr]);
14841                 cur.setActive(false);
14842                 
14843                 _this.transition = false;
14844                 
14845             }, this, { single:  true } );
14846             
14847             return true;
14848         }
14849         
14850         cur.setActive(false);
14851         pan.setActive(true);
14852         
14853         return true;
14854         
14855     },
14856     showPanelNext : function()
14857     {
14858         var i = this.indexOfPanel(this.getActivePanel());
14859         
14860         if (i >= this.tabs.length - 1 && !this.autoslide) {
14861             return;
14862         }
14863         
14864         if (i >= this.tabs.length - 1 && this.autoslide) {
14865             i = -1;
14866         }
14867         
14868         this.showPanel(this.tabs[i+1]);
14869     },
14870     
14871     showPanelPrev : function()
14872     {
14873         var i = this.indexOfPanel(this.getActivePanel());
14874         
14875         if (i  < 1 && !this.autoslide) {
14876             return;
14877         }
14878         
14879         if (i < 1 && this.autoslide) {
14880             i = this.tabs.length;
14881         }
14882         
14883         this.showPanel(this.tabs[i-1]);
14884     },
14885     
14886     setActiveBullet : function(i)
14887     {
14888         Roo.each(this.el.select('.bullet', true).elements, function(el){
14889             el.removeClass('selected');
14890         });
14891
14892         var bullet = this.el.select('.bullet-' + i, true).first();
14893         
14894         if(!bullet){
14895             return;
14896         }
14897         
14898         bullet.addClass('selected');
14899     }
14900     
14901     
14902   
14903 });
14904
14905  
14906
14907  
14908  
14909 Roo.apply(Roo.bootstrap.TabGroup, {
14910     
14911     groups: {},
14912      /**
14913     * register a Navigation Group
14914     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14915     */
14916     register : function(navgrp)
14917     {
14918         this.groups[navgrp.navId] = navgrp;
14919         
14920     },
14921     /**
14922     * fetch a Navigation Group based on the navigation ID
14923     * if one does not exist , it will get created.
14924     * @param {string} the navgroup to add
14925     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14926     */
14927     get: function(navId) {
14928         if (typeof(this.groups[navId]) == 'undefined') {
14929             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14930         }
14931         return this.groups[navId] ;
14932     }
14933     
14934     
14935     
14936 });
14937
14938  /*
14939  * - LGPL
14940  *
14941  * TabPanel
14942  * 
14943  */
14944
14945 /**
14946  * @class Roo.bootstrap.TabPanel
14947  * @extends Roo.bootstrap.Component
14948  * Bootstrap TabPanel class
14949  * @cfg {Boolean} active panel active
14950  * @cfg {String} html panel content
14951  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14952  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14953  * 
14954  * 
14955  * @constructor
14956  * Create a new TabPanel
14957  * @param {Object} config The config object
14958  */
14959
14960 Roo.bootstrap.TabPanel = function(config){
14961     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14962     this.addEvents({
14963         /**
14964              * @event changed
14965              * Fires when the active status changes
14966              * @param {Roo.bootstrap.TabPanel} this
14967              * @param {Boolean} state the new state
14968             
14969          */
14970         'changed': true,
14971         /**
14972              * @event beforedeactivate
14973              * Fires before a tab is de-activated - can be used to do validation on a form.
14974              * @param {Roo.bootstrap.TabPanel} this
14975              * @return {Boolean} false if there is an error
14976             
14977          */
14978         'beforedeactivate': true
14979      });
14980     
14981     this.tabId = this.tabId || Roo.id();
14982   
14983 };
14984
14985 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14986     
14987     active: false,
14988     html: false,
14989     tabId: false,
14990     navId : false,
14991     
14992     getAutoCreate : function(){
14993         var cfg = {
14994             tag: 'div',
14995             // item is needed for carousel - not sure if it has any effect otherwise
14996             cls: 'tab-pane item',
14997             html: this.html || ''
14998         };
14999         
15000         if(this.active){
15001             cfg.cls += ' active';
15002         }
15003         
15004         if(this.tabId){
15005             cfg.tabId = this.tabId;
15006         }
15007         
15008         
15009         return cfg;
15010     },
15011     
15012     initEvents:  function()
15013     {
15014         Roo.log('-------- init events on tab panel ---------');
15015         
15016         var p = this.parent();
15017         this.navId = this.navId || p.navId;
15018         
15019         if (typeof(this.navId) != 'undefined') {
15020             // not really needed.. but just in case.. parent should be a NavGroup.
15021             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15022             Roo.log(['register', tg, this]);
15023             tg.register(this);
15024             
15025             var i = tg.tabs.length - 1;
15026             
15027             if(this.active && tg.bullets > 0 && i < tg.bullets){
15028                 tg.setActiveBullet(i);
15029             }
15030         }
15031         
15032     },
15033     
15034     
15035     onRender : function(ct, position)
15036     {
15037        // Roo.log("Call onRender: " + this.xtype);
15038         
15039         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
15040         
15041         
15042         
15043         
15044         
15045     },
15046     
15047     setActive: function(state)
15048     {
15049         Roo.log("panel - set active " + this.tabId + "=" + state);
15050         
15051         this.active = state;
15052         if (!state) {
15053             this.el.removeClass('active');
15054             
15055         } else  if (!this.el.hasClass('active')) {
15056             this.el.addClass('active');
15057         }
15058         
15059         this.fireEvent('changed', this, state);
15060     }
15061     
15062     
15063 });
15064  
15065
15066  
15067
15068  /*
15069  * - LGPL
15070  *
15071  * DateField
15072  * 
15073  */
15074
15075 /**
15076  * @class Roo.bootstrap.DateField
15077  * @extends Roo.bootstrap.Input
15078  * Bootstrap DateField class
15079  * @cfg {Number} weekStart default 0
15080  * @cfg {String} viewMode default empty, (months|years)
15081  * @cfg {String} minViewMode default empty, (months|years)
15082  * @cfg {Number} startDate default -Infinity
15083  * @cfg {Number} endDate default Infinity
15084  * @cfg {Boolean} todayHighlight default false
15085  * @cfg {Boolean} todayBtn default false
15086  * @cfg {Boolean} calendarWeeks default false
15087  * @cfg {Object} daysOfWeekDisabled default empty
15088  * @cfg {Boolean} singleMode default false (true | false)
15089  * 
15090  * @cfg {Boolean} keyboardNavigation default true
15091  * @cfg {String} language default en
15092  * 
15093  * @constructor
15094  * Create a new DateField
15095  * @param {Object} config The config object
15096  */
15097
15098 Roo.bootstrap.DateField = function(config){
15099     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
15100      this.addEvents({
15101             /**
15102              * @event show
15103              * Fires when this field show.
15104              * @param {Roo.bootstrap.DateField} this
15105              * @param {Mixed} date The date value
15106              */
15107             show : true,
15108             /**
15109              * @event show
15110              * Fires when this field hide.
15111              * @param {Roo.bootstrap.DateField} this
15112              * @param {Mixed} date The date value
15113              */
15114             hide : true,
15115             /**
15116              * @event select
15117              * Fires when select a date.
15118              * @param {Roo.bootstrap.DateField} this
15119              * @param {Mixed} date The date value
15120              */
15121             select : true
15122         });
15123 };
15124
15125 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
15126     
15127     /**
15128      * @cfg {String} format
15129      * The default date format string which can be overriden for localization support.  The format must be
15130      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
15131      */
15132     format : "m/d/y",
15133     /**
15134      * @cfg {String} altFormats
15135      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
15136      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
15137      */
15138     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
15139     
15140     weekStart : 0,
15141     
15142     viewMode : '',
15143     
15144     minViewMode : '',
15145     
15146     todayHighlight : false,
15147     
15148     todayBtn: false,
15149     
15150     language: 'en',
15151     
15152     keyboardNavigation: true,
15153     
15154     calendarWeeks: false,
15155     
15156     startDate: -Infinity,
15157     
15158     endDate: Infinity,
15159     
15160     daysOfWeekDisabled: [],
15161     
15162     _events: [],
15163     
15164     singleMode : false,
15165     
15166     UTCDate: function()
15167     {
15168         return new Date(Date.UTC.apply(Date, arguments));
15169     },
15170     
15171     UTCToday: function()
15172     {
15173         var today = new Date();
15174         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
15175     },
15176     
15177     getDate: function() {
15178             var d = this.getUTCDate();
15179             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
15180     },
15181     
15182     getUTCDate: function() {
15183             return this.date;
15184     },
15185     
15186     setDate: function(d) {
15187             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
15188     },
15189     
15190     setUTCDate: function(d) {
15191             this.date = d;
15192             this.setValue(this.formatDate(this.date));
15193     },
15194         
15195     onRender: function(ct, position)
15196     {
15197         
15198         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
15199         
15200         this.language = this.language || 'en';
15201         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
15202         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
15203         
15204         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
15205         this.format = this.format || 'm/d/y';
15206         this.isInline = false;
15207         this.isInput = true;
15208         this.component = this.el.select('.add-on', true).first() || false;
15209         this.component = (this.component && this.component.length === 0) ? false : this.component;
15210         this.hasInput = this.component && this.inputEL().length;
15211         
15212         if (typeof(this.minViewMode === 'string')) {
15213             switch (this.minViewMode) {
15214                 case 'months':
15215                     this.minViewMode = 1;
15216                     break;
15217                 case 'years':
15218                     this.minViewMode = 2;
15219                     break;
15220                 default:
15221                     this.minViewMode = 0;
15222                     break;
15223             }
15224         }
15225         
15226         if (typeof(this.viewMode === 'string')) {
15227             switch (this.viewMode) {
15228                 case 'months':
15229                     this.viewMode = 1;
15230                     break;
15231                 case 'years':
15232                     this.viewMode = 2;
15233                     break;
15234                 default:
15235                     this.viewMode = 0;
15236                     break;
15237             }
15238         }
15239                 
15240         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
15241         
15242 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
15243         
15244         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15245         
15246         this.picker().on('mousedown', this.onMousedown, this);
15247         this.picker().on('click', this.onClick, this);
15248         
15249         this.picker().addClass('datepicker-dropdown');
15250         
15251         this.startViewMode = this.viewMode;
15252         
15253         if(this.singleMode){
15254             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
15255                 v.setVisibilityMode(Roo.Element.DISPLAY)
15256                 v.hide();
15257             });
15258             
15259             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
15260                 v.setStyle('width', '189px');
15261             });
15262         }
15263         
15264         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
15265             if(!this.calendarWeeks){
15266                 v.remove();
15267                 return;
15268             }
15269             
15270             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15271             v.attr('colspan', function(i, val){
15272                 return parseInt(val) + 1;
15273             });
15274         })
15275                         
15276         
15277         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
15278         
15279         this.setStartDate(this.startDate);
15280         this.setEndDate(this.endDate);
15281         
15282         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
15283         
15284         this.fillDow();
15285         this.fillMonths();
15286         this.update();
15287         this.showMode();
15288         
15289         if(this.isInline) {
15290             this.show();
15291         }
15292     },
15293     
15294     picker : function()
15295     {
15296         return this.pickerEl;
15297 //        return this.el.select('.datepicker', true).first();
15298     },
15299     
15300     fillDow: function()
15301     {
15302         var dowCnt = this.weekStart;
15303         
15304         var dow = {
15305             tag: 'tr',
15306             cn: [
15307                 
15308             ]
15309         };
15310         
15311         if(this.calendarWeeks){
15312             dow.cn.push({
15313                 tag: 'th',
15314                 cls: 'cw',
15315                 html: '&nbsp;'
15316             })
15317         }
15318         
15319         while (dowCnt < this.weekStart + 7) {
15320             dow.cn.push({
15321                 tag: 'th',
15322                 cls: 'dow',
15323                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
15324             });
15325         }
15326         
15327         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
15328     },
15329     
15330     fillMonths: function()
15331     {    
15332         var i = 0;
15333         var months = this.picker().select('>.datepicker-months td', true).first();
15334         
15335         months.dom.innerHTML = '';
15336         
15337         while (i < 12) {
15338             var month = {
15339                 tag: 'span',
15340                 cls: 'month',
15341                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
15342             }
15343             
15344             months.createChild(month);
15345         }
15346         
15347     },
15348     
15349     update: function()
15350     {
15351         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;
15352         
15353         if (this.date < this.startDate) {
15354             this.viewDate = new Date(this.startDate);
15355         } else if (this.date > this.endDate) {
15356             this.viewDate = new Date(this.endDate);
15357         } else {
15358             this.viewDate = new Date(this.date);
15359         }
15360         
15361         this.fill();
15362     },
15363     
15364     fill: function() 
15365     {
15366         var d = new Date(this.viewDate),
15367                 year = d.getUTCFullYear(),
15368                 month = d.getUTCMonth(),
15369                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
15370                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
15371                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
15372                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
15373                 currentDate = this.date && this.date.valueOf(),
15374                 today = this.UTCToday();
15375         
15376         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
15377         
15378 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15379         
15380 //        this.picker.select('>tfoot th.today').
15381 //                                              .text(dates[this.language].today)
15382 //                                              .toggle(this.todayBtn !== false);
15383     
15384         this.updateNavArrows();
15385         this.fillMonths();
15386                                                 
15387         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
15388         
15389         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
15390          
15391         prevMonth.setUTCDate(day);
15392         
15393         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
15394         
15395         var nextMonth = new Date(prevMonth);
15396         
15397         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
15398         
15399         nextMonth = nextMonth.valueOf();
15400         
15401         var fillMonths = false;
15402         
15403         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
15404         
15405         while(prevMonth.valueOf() < nextMonth) {
15406             var clsName = '';
15407             
15408             if (prevMonth.getUTCDay() === this.weekStart) {
15409                 if(fillMonths){
15410                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
15411                 }
15412                     
15413                 fillMonths = {
15414                     tag: 'tr',
15415                     cn: []
15416                 };
15417                 
15418                 if(this.calendarWeeks){
15419                     // ISO 8601: First week contains first thursday.
15420                     // ISO also states week starts on Monday, but we can be more abstract here.
15421                     var
15422                     // Start of current week: based on weekstart/current date
15423                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
15424                     // Thursday of this week
15425                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
15426                     // First Thursday of year, year from thursday
15427                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
15428                     // Calendar week: ms between thursdays, div ms per day, div 7 days
15429                     calWeek =  (th - yth) / 864e5 / 7 + 1;
15430                     
15431                     fillMonths.cn.push({
15432                         tag: 'td',
15433                         cls: 'cw',
15434                         html: calWeek
15435                     });
15436                 }
15437             }
15438             
15439             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
15440                 clsName += ' old';
15441             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
15442                 clsName += ' new';
15443             }
15444             if (this.todayHighlight &&
15445                 prevMonth.getUTCFullYear() == today.getFullYear() &&
15446                 prevMonth.getUTCMonth() == today.getMonth() &&
15447                 prevMonth.getUTCDate() == today.getDate()) {
15448                 clsName += ' today';
15449             }
15450             
15451             if (currentDate && prevMonth.valueOf() === currentDate) {
15452                 clsName += ' active';
15453             }
15454             
15455             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
15456                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
15457                     clsName += ' disabled';
15458             }
15459             
15460             fillMonths.cn.push({
15461                 tag: 'td',
15462                 cls: 'day ' + clsName,
15463                 html: prevMonth.getDate()
15464             })
15465             
15466             prevMonth.setDate(prevMonth.getDate()+1);
15467         }
15468           
15469         var currentYear = this.date && this.date.getUTCFullYear();
15470         var currentMonth = this.date && this.date.getUTCMonth();
15471         
15472         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
15473         
15474         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
15475             v.removeClass('active');
15476             
15477             if(currentYear === year && k === currentMonth){
15478                 v.addClass('active');
15479             }
15480             
15481             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
15482                 v.addClass('disabled');
15483             }
15484             
15485         });
15486         
15487         
15488         year = parseInt(year/10, 10) * 10;
15489         
15490         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
15491         
15492         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
15493         
15494         year -= 1;
15495         for (var i = -1; i < 11; i++) {
15496             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
15497                 tag: 'span',
15498                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
15499                 html: year
15500             })
15501             
15502             year += 1;
15503         }
15504     },
15505     
15506     showMode: function(dir) 
15507     {
15508         if (dir) {
15509             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15510         }
15511         
15512         Roo.each(this.picker().select('>div',true).elements, function(v){
15513             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15514             v.hide();
15515         });
15516         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15517     },
15518     
15519     place: function()
15520     {
15521         if(this.isInline) return;
15522         
15523         this.picker().removeClass(['bottom', 'top']);
15524         
15525         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15526             /*
15527              * place to the top of element!
15528              *
15529              */
15530             
15531             this.picker().addClass('top');
15532             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15533             
15534             return;
15535         }
15536         
15537         this.picker().addClass('bottom');
15538         
15539         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15540     },
15541     
15542     parseDate : function(value)
15543     {
15544         if(!value || value instanceof Date){
15545             return value;
15546         }
15547         var v = Date.parseDate(value, this.format);
15548         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15549             v = Date.parseDate(value, 'Y-m-d');
15550         }
15551         if(!v && this.altFormats){
15552             if(!this.altFormatsArray){
15553                 this.altFormatsArray = this.altFormats.split("|");
15554             }
15555             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15556                 v = Date.parseDate(value, this.altFormatsArray[i]);
15557             }
15558         }
15559         return v;
15560     },
15561     
15562     formatDate : function(date, fmt)
15563     {   
15564         return (!date || !(date instanceof Date)) ?
15565         date : date.dateFormat(fmt || this.format);
15566     },
15567     
15568     onFocus : function()
15569     {
15570         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15571         this.show();
15572     },
15573     
15574     onBlur : function()
15575     {
15576         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15577         
15578         var d = this.inputEl().getValue();
15579         
15580         this.setValue(d);
15581                 
15582         this.hide();
15583     },
15584     
15585     show : function()
15586     {
15587         this.picker().show();
15588         this.update();
15589         this.place();
15590         
15591         this.fireEvent('show', this, this.date);
15592     },
15593     
15594     hide : function()
15595     {
15596         if(this.isInline) return;
15597         this.picker().hide();
15598         this.viewMode = this.startViewMode;
15599         this.showMode();
15600         
15601         this.fireEvent('hide', this, this.date);
15602         
15603     },
15604     
15605     onMousedown: function(e)
15606     {
15607         e.stopPropagation();
15608         e.preventDefault();
15609     },
15610     
15611     keyup: function(e)
15612     {
15613         Roo.bootstrap.DateField.superclass.keyup.call(this);
15614         this.update();
15615     },
15616
15617     setValue: function(v)
15618     {
15619         
15620         // v can be a string or a date..
15621         
15622         
15623         var d = new Date(this.parseDate(v) ).clearTime();
15624         
15625         if(isNaN(d.getTime())){
15626             this.date = this.viewDate = '';
15627             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15628             return;
15629         }
15630         
15631         v = this.formatDate(d);
15632         
15633         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15634         
15635         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15636      
15637         this.update();
15638
15639         this.fireEvent('select', this, this.date);
15640         
15641     },
15642     
15643     getValue: function()
15644     {
15645         return this.formatDate(this.date);
15646     },
15647     
15648     fireKey: function(e)
15649     {
15650         if (!this.picker().isVisible()){
15651             if (e.keyCode == 27) // allow escape to hide and re-show picker
15652                 this.show();
15653             return;
15654         }
15655         
15656         var dateChanged = false,
15657         dir, day, month,
15658         newDate, newViewDate;
15659         
15660         switch(e.keyCode){
15661             case 27: // escape
15662                 this.hide();
15663                 e.preventDefault();
15664                 break;
15665             case 37: // left
15666             case 39: // right
15667                 if (!this.keyboardNavigation) break;
15668                 dir = e.keyCode == 37 ? -1 : 1;
15669                 
15670                 if (e.ctrlKey){
15671                     newDate = this.moveYear(this.date, dir);
15672                     newViewDate = this.moveYear(this.viewDate, dir);
15673                 } else if (e.shiftKey){
15674                     newDate = this.moveMonth(this.date, dir);
15675                     newViewDate = this.moveMonth(this.viewDate, dir);
15676                 } else {
15677                     newDate = new Date(this.date);
15678                     newDate.setUTCDate(this.date.getUTCDate() + dir);
15679                     newViewDate = new Date(this.viewDate);
15680                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15681                 }
15682                 if (this.dateWithinRange(newDate)){
15683                     this.date = newDate;
15684                     this.viewDate = newViewDate;
15685                     this.setValue(this.formatDate(this.date));
15686 //                    this.update();
15687                     e.preventDefault();
15688                     dateChanged = true;
15689                 }
15690                 break;
15691             case 38: // up
15692             case 40: // down
15693                 if (!this.keyboardNavigation) break;
15694                 dir = e.keyCode == 38 ? -1 : 1;
15695                 if (e.ctrlKey){
15696                     newDate = this.moveYear(this.date, dir);
15697                     newViewDate = this.moveYear(this.viewDate, dir);
15698                 } else if (e.shiftKey){
15699                     newDate = this.moveMonth(this.date, dir);
15700                     newViewDate = this.moveMonth(this.viewDate, dir);
15701                 } else {
15702                     newDate = new Date(this.date);
15703                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15704                     newViewDate = new Date(this.viewDate);
15705                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15706                 }
15707                 if (this.dateWithinRange(newDate)){
15708                     this.date = newDate;
15709                     this.viewDate = newViewDate;
15710                     this.setValue(this.formatDate(this.date));
15711 //                    this.update();
15712                     e.preventDefault();
15713                     dateChanged = true;
15714                 }
15715                 break;
15716             case 13: // enter
15717                 this.setValue(this.formatDate(this.date));
15718                 this.hide();
15719                 e.preventDefault();
15720                 break;
15721             case 9: // tab
15722                 this.setValue(this.formatDate(this.date));
15723                 this.hide();
15724                 break;
15725             case 16: // shift
15726             case 17: // ctrl
15727             case 18: // alt
15728                 break;
15729             default :
15730                 this.hide();
15731                 
15732         }
15733     },
15734     
15735     
15736     onClick: function(e) 
15737     {
15738         e.stopPropagation();
15739         e.preventDefault();
15740         
15741         var target = e.getTarget();
15742         
15743         if(target.nodeName.toLowerCase() === 'i'){
15744             target = Roo.get(target).dom.parentNode;
15745         }
15746         
15747         var nodeName = target.nodeName;
15748         var className = target.className;
15749         var html = target.innerHTML;
15750         //Roo.log(nodeName);
15751         
15752         switch(nodeName.toLowerCase()) {
15753             case 'th':
15754                 switch(className) {
15755                     case 'switch':
15756                         this.showMode(1);
15757                         break;
15758                     case 'prev':
15759                     case 'next':
15760                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15761                         switch(this.viewMode){
15762                                 case 0:
15763                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15764                                         break;
15765                                 case 1:
15766                                 case 2:
15767                                         this.viewDate = this.moveYear(this.viewDate, dir);
15768                                         break;
15769                         }
15770                         this.fill();
15771                         break;
15772                     case 'today':
15773                         var date = new Date();
15774                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15775 //                        this.fill()
15776                         this.setValue(this.formatDate(this.date));
15777                         
15778                         this.hide();
15779                         break;
15780                 }
15781                 break;
15782             case 'span':
15783                 if (className.indexOf('disabled') < 0) {
15784                     this.viewDate.setUTCDate(1);
15785                     if (className.indexOf('month') > -1) {
15786                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15787                     } else {
15788                         var year = parseInt(html, 10) || 0;
15789                         this.viewDate.setUTCFullYear(year);
15790                         
15791                     }
15792                     
15793                     if(this.singleMode){
15794                         this.setValue(this.formatDate(this.viewDate));
15795                         this.hide();
15796                         return;
15797                     }
15798                     
15799                     this.showMode(-1);
15800                     this.fill();
15801                 }
15802                 break;
15803                 
15804             case 'td':
15805                 //Roo.log(className);
15806                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15807                     var day = parseInt(html, 10) || 1;
15808                     var year = this.viewDate.getUTCFullYear(),
15809                         month = this.viewDate.getUTCMonth();
15810
15811                     if (className.indexOf('old') > -1) {
15812                         if(month === 0 ){
15813                             month = 11;
15814                             year -= 1;
15815                         }else{
15816                             month -= 1;
15817                         }
15818                     } else if (className.indexOf('new') > -1) {
15819                         if (month == 11) {
15820                             month = 0;
15821                             year += 1;
15822                         } else {
15823                             month += 1;
15824                         }
15825                     }
15826                     //Roo.log([year,month,day]);
15827                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15828                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15829 //                    this.fill();
15830                     //Roo.log(this.formatDate(this.date));
15831                     this.setValue(this.formatDate(this.date));
15832                     this.hide();
15833                 }
15834                 break;
15835         }
15836     },
15837     
15838     setStartDate: function(startDate)
15839     {
15840         this.startDate = startDate || -Infinity;
15841         if (this.startDate !== -Infinity) {
15842             this.startDate = this.parseDate(this.startDate);
15843         }
15844         this.update();
15845         this.updateNavArrows();
15846     },
15847
15848     setEndDate: function(endDate)
15849     {
15850         this.endDate = endDate || Infinity;
15851         if (this.endDate !== Infinity) {
15852             this.endDate = this.parseDate(this.endDate);
15853         }
15854         this.update();
15855         this.updateNavArrows();
15856     },
15857     
15858     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15859     {
15860         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15861         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15862             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15863         }
15864         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15865             return parseInt(d, 10);
15866         });
15867         this.update();
15868         this.updateNavArrows();
15869     },
15870     
15871     updateNavArrows: function() 
15872     {
15873         if(this.singleMode){
15874             return;
15875         }
15876         
15877         var d = new Date(this.viewDate),
15878         year = d.getUTCFullYear(),
15879         month = d.getUTCMonth();
15880         
15881         Roo.each(this.picker().select('.prev', true).elements, function(v){
15882             v.show();
15883             switch (this.viewMode) {
15884                 case 0:
15885
15886                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15887                         v.hide();
15888                     }
15889                     break;
15890                 case 1:
15891                 case 2:
15892                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15893                         v.hide();
15894                     }
15895                     break;
15896             }
15897         });
15898         
15899         Roo.each(this.picker().select('.next', true).elements, function(v){
15900             v.show();
15901             switch (this.viewMode) {
15902                 case 0:
15903
15904                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15905                         v.hide();
15906                     }
15907                     break;
15908                 case 1:
15909                 case 2:
15910                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15911                         v.hide();
15912                     }
15913                     break;
15914             }
15915         })
15916     },
15917     
15918     moveMonth: function(date, dir)
15919     {
15920         if (!dir) return date;
15921         var new_date = new Date(date.valueOf()),
15922         day = new_date.getUTCDate(),
15923         month = new_date.getUTCMonth(),
15924         mag = Math.abs(dir),
15925         new_month, test;
15926         dir = dir > 0 ? 1 : -1;
15927         if (mag == 1){
15928             test = dir == -1
15929             // If going back one month, make sure month is not current month
15930             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15931             ? function(){
15932                 return new_date.getUTCMonth() == month;
15933             }
15934             // If going forward one month, make sure month is as expected
15935             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15936             : function(){
15937                 return new_date.getUTCMonth() != new_month;
15938             };
15939             new_month = month + dir;
15940             new_date.setUTCMonth(new_month);
15941             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15942             if (new_month < 0 || new_month > 11)
15943                 new_month = (new_month + 12) % 12;
15944         } else {
15945             // For magnitudes >1, move one month at a time...
15946             for (var i=0; i<mag; i++)
15947                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15948                 new_date = this.moveMonth(new_date, dir);
15949             // ...then reset the day, keeping it in the new month
15950             new_month = new_date.getUTCMonth();
15951             new_date.setUTCDate(day);
15952             test = function(){
15953                 return new_month != new_date.getUTCMonth();
15954             };
15955         }
15956         // Common date-resetting loop -- if date is beyond end of month, make it
15957         // end of month
15958         while (test()){
15959             new_date.setUTCDate(--day);
15960             new_date.setUTCMonth(new_month);
15961         }
15962         return new_date;
15963     },
15964
15965     moveYear: function(date, dir)
15966     {
15967         return this.moveMonth(date, dir*12);
15968     },
15969
15970     dateWithinRange: function(date)
15971     {
15972         return date >= this.startDate && date <= this.endDate;
15973     },
15974
15975     
15976     remove: function() 
15977     {
15978         this.picker().remove();
15979     }
15980    
15981 });
15982
15983 Roo.apply(Roo.bootstrap.DateField,  {
15984     
15985     head : {
15986         tag: 'thead',
15987         cn: [
15988         {
15989             tag: 'tr',
15990             cn: [
15991             {
15992                 tag: 'th',
15993                 cls: 'prev',
15994                 html: '<i class="fa fa-arrow-left"/>'
15995             },
15996             {
15997                 tag: 'th',
15998                 cls: 'switch',
15999                 colspan: '5'
16000             },
16001             {
16002                 tag: 'th',
16003                 cls: 'next',
16004                 html: '<i class="fa fa-arrow-right"/>'
16005             }
16006
16007             ]
16008         }
16009         ]
16010     },
16011     
16012     content : {
16013         tag: 'tbody',
16014         cn: [
16015         {
16016             tag: 'tr',
16017             cn: [
16018             {
16019                 tag: 'td',
16020                 colspan: '7'
16021             }
16022             ]
16023         }
16024         ]
16025     },
16026     
16027     footer : {
16028         tag: 'tfoot',
16029         cn: [
16030         {
16031             tag: 'tr',
16032             cn: [
16033             {
16034                 tag: 'th',
16035                 colspan: '7',
16036                 cls: 'today'
16037             }
16038                     
16039             ]
16040         }
16041         ]
16042     },
16043     
16044     dates:{
16045         en: {
16046             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
16047             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
16048             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
16049             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16050             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
16051             today: "Today"
16052         }
16053     },
16054     
16055     modes: [
16056     {
16057         clsName: 'days',
16058         navFnc: 'Month',
16059         navStep: 1
16060     },
16061     {
16062         clsName: 'months',
16063         navFnc: 'FullYear',
16064         navStep: 1
16065     },
16066     {
16067         clsName: 'years',
16068         navFnc: 'FullYear',
16069         navStep: 10
16070     }]
16071 });
16072
16073 Roo.apply(Roo.bootstrap.DateField,  {
16074   
16075     template : {
16076         tag: 'div',
16077         cls: 'datepicker dropdown-menu roo-dynamic',
16078         cn: [
16079         {
16080             tag: 'div',
16081             cls: 'datepicker-days',
16082             cn: [
16083             {
16084                 tag: 'table',
16085                 cls: 'table-condensed',
16086                 cn:[
16087                 Roo.bootstrap.DateField.head,
16088                 {
16089                     tag: 'tbody'
16090                 },
16091                 Roo.bootstrap.DateField.footer
16092                 ]
16093             }
16094             ]
16095         },
16096         {
16097             tag: 'div',
16098             cls: 'datepicker-months',
16099             cn: [
16100             {
16101                 tag: 'table',
16102                 cls: 'table-condensed',
16103                 cn:[
16104                 Roo.bootstrap.DateField.head,
16105                 Roo.bootstrap.DateField.content,
16106                 Roo.bootstrap.DateField.footer
16107                 ]
16108             }
16109             ]
16110         },
16111         {
16112             tag: 'div',
16113             cls: 'datepicker-years',
16114             cn: [
16115             {
16116                 tag: 'table',
16117                 cls: 'table-condensed',
16118                 cn:[
16119                 Roo.bootstrap.DateField.head,
16120                 Roo.bootstrap.DateField.content,
16121                 Roo.bootstrap.DateField.footer
16122                 ]
16123             }
16124             ]
16125         }
16126         ]
16127     }
16128 });
16129
16130  
16131
16132  /*
16133  * - LGPL
16134  *
16135  * TimeField
16136  * 
16137  */
16138
16139 /**
16140  * @class Roo.bootstrap.TimeField
16141  * @extends Roo.bootstrap.Input
16142  * Bootstrap DateField class
16143  * 
16144  * 
16145  * @constructor
16146  * Create a new TimeField
16147  * @param {Object} config The config object
16148  */
16149
16150 Roo.bootstrap.TimeField = function(config){
16151     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
16152     this.addEvents({
16153             /**
16154              * @event show
16155              * Fires when this field show.
16156              * @param {Roo.bootstrap.DateField} thisthis
16157              * @param {Mixed} date The date value
16158              */
16159             show : true,
16160             /**
16161              * @event show
16162              * Fires when this field hide.
16163              * @param {Roo.bootstrap.DateField} this
16164              * @param {Mixed} date The date value
16165              */
16166             hide : true,
16167             /**
16168              * @event select
16169              * Fires when select a date.
16170              * @param {Roo.bootstrap.DateField} this
16171              * @param {Mixed} date The date value
16172              */
16173             select : true
16174         });
16175 };
16176
16177 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
16178     
16179     /**
16180      * @cfg {String} format
16181      * The default time format string which can be overriden for localization support.  The format must be
16182      * valid according to {@link Date#parseDate} (defaults to 'H:i').
16183      */
16184     format : "H:i",
16185        
16186     onRender: function(ct, position)
16187     {
16188         
16189         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
16190                 
16191         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
16192         
16193         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16194         
16195         this.pop = this.picker().select('>.datepicker-time',true).first();
16196         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16197         
16198         this.picker().on('mousedown', this.onMousedown, this);
16199         this.picker().on('click', this.onClick, this);
16200         
16201         this.picker().addClass('datepicker-dropdown');
16202     
16203         this.fillTime();
16204         this.update();
16205             
16206         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
16207         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
16208         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
16209         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
16210         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
16211         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
16212
16213     },
16214     
16215     fireKey: function(e){
16216         if (!this.picker().isVisible()){
16217             if (e.keyCode == 27) { // allow escape to hide and re-show picker
16218                 this.show();
16219             }
16220             return;
16221         }
16222
16223         e.preventDefault();
16224         
16225         switch(e.keyCode){
16226             case 27: // escape
16227                 this.hide();
16228                 break;
16229             case 37: // left
16230             case 39: // right
16231                 this.onTogglePeriod();
16232                 break;
16233             case 38: // up
16234                 this.onIncrementMinutes();
16235                 break;
16236             case 40: // down
16237                 this.onDecrementMinutes();
16238                 break;
16239             case 13: // enter
16240             case 9: // tab
16241                 this.setTime();
16242                 break;
16243         }
16244     },
16245     
16246     onClick: function(e) {
16247         e.stopPropagation();
16248         e.preventDefault();
16249     },
16250     
16251     picker : function()
16252     {
16253         return this.el.select('.datepicker', true).first();
16254     },
16255     
16256     fillTime: function()
16257     {    
16258         var time = this.pop.select('tbody', true).first();
16259         
16260         time.dom.innerHTML = '';
16261         
16262         time.createChild({
16263             tag: 'tr',
16264             cn: [
16265                 {
16266                     tag: 'td',
16267                     cn: [
16268                         {
16269                             tag: 'a',
16270                             href: '#',
16271                             cls: 'btn',
16272                             cn: [
16273                                 {
16274                                     tag: 'span',
16275                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
16276                                 }
16277                             ]
16278                         } 
16279                     ]
16280                 },
16281                 {
16282                     tag: 'td',
16283                     cls: 'separator'
16284                 },
16285                 {
16286                     tag: 'td',
16287                     cn: [
16288                         {
16289                             tag: 'a',
16290                             href: '#',
16291                             cls: 'btn',
16292                             cn: [
16293                                 {
16294                                     tag: 'span',
16295                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
16296                                 }
16297                             ]
16298                         }
16299                     ]
16300                 },
16301                 {
16302                     tag: 'td',
16303                     cls: 'separator'
16304                 }
16305             ]
16306         });
16307         
16308         time.createChild({
16309             tag: 'tr',
16310             cn: [
16311                 {
16312                     tag: 'td',
16313                     cn: [
16314                         {
16315                             tag: 'span',
16316                             cls: 'timepicker-hour',
16317                             html: '00'
16318                         }  
16319                     ]
16320                 },
16321                 {
16322                     tag: 'td',
16323                     cls: 'separator',
16324                     html: ':'
16325                 },
16326                 {
16327                     tag: 'td',
16328                     cn: [
16329                         {
16330                             tag: 'span',
16331                             cls: 'timepicker-minute',
16332                             html: '00'
16333                         }  
16334                     ]
16335                 },
16336                 {
16337                     tag: 'td',
16338                     cls: 'separator'
16339                 },
16340                 {
16341                     tag: 'td',
16342                     cn: [
16343                         {
16344                             tag: 'button',
16345                             type: 'button',
16346                             cls: 'btn btn-primary period',
16347                             html: 'AM'
16348                             
16349                         }
16350                     ]
16351                 }
16352             ]
16353         });
16354         
16355         time.createChild({
16356             tag: 'tr',
16357             cn: [
16358                 {
16359                     tag: 'td',
16360                     cn: [
16361                         {
16362                             tag: 'a',
16363                             href: '#',
16364                             cls: 'btn',
16365                             cn: [
16366                                 {
16367                                     tag: 'span',
16368                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
16369                                 }
16370                             ]
16371                         }
16372                     ]
16373                 },
16374                 {
16375                     tag: 'td',
16376                     cls: 'separator'
16377                 },
16378                 {
16379                     tag: 'td',
16380                     cn: [
16381                         {
16382                             tag: 'a',
16383                             href: '#',
16384                             cls: 'btn',
16385                             cn: [
16386                                 {
16387                                     tag: 'span',
16388                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
16389                                 }
16390                             ]
16391                         }
16392                     ]
16393                 },
16394                 {
16395                     tag: 'td',
16396                     cls: 'separator'
16397                 }
16398             ]
16399         });
16400         
16401     },
16402     
16403     update: function()
16404     {
16405         
16406         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
16407         
16408         this.fill();
16409     },
16410     
16411     fill: function() 
16412     {
16413         var hours = this.time.getHours();
16414         var minutes = this.time.getMinutes();
16415         var period = 'AM';
16416         
16417         if(hours > 11){
16418             period = 'PM';
16419         }
16420         
16421         if(hours == 0){
16422             hours = 12;
16423         }
16424         
16425         
16426         if(hours > 12){
16427             hours = hours - 12;
16428         }
16429         
16430         if(hours < 10){
16431             hours = '0' + hours;
16432         }
16433         
16434         if(minutes < 10){
16435             minutes = '0' + minutes;
16436         }
16437         
16438         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
16439         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
16440         this.pop.select('button', true).first().dom.innerHTML = period;
16441         
16442     },
16443     
16444     place: function()
16445     {   
16446         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
16447         
16448         var cls = ['bottom'];
16449         
16450         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
16451             cls.pop();
16452             cls.push('top');
16453         }
16454         
16455         cls.push('right');
16456         
16457         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
16458             cls.pop();
16459             cls.push('left');
16460         }
16461         
16462         this.picker().addClass(cls.join('-'));
16463         
16464         var _this = this;
16465         
16466         Roo.each(cls, function(c){
16467             if(c == 'bottom'){
16468                 _this.picker().setTop(_this.inputEl().getHeight());
16469                 return;
16470             }
16471             if(c == 'top'){
16472                 _this.picker().setTop(0 - _this.picker().getHeight());
16473                 return;
16474             }
16475             
16476             if(c == 'left'){
16477                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
16478                 return;
16479             }
16480             if(c == 'right'){
16481                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
16482                 return;
16483             }
16484         });
16485         
16486     },
16487   
16488     onFocus : function()
16489     {
16490         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
16491         this.show();
16492     },
16493     
16494     onBlur : function()
16495     {
16496         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
16497         this.hide();
16498     },
16499     
16500     show : function()
16501     {
16502         this.picker().show();
16503         this.pop.show();
16504         this.update();
16505         this.place();
16506         
16507         this.fireEvent('show', this, this.date);
16508     },
16509     
16510     hide : function()
16511     {
16512         this.picker().hide();
16513         this.pop.hide();
16514         
16515         this.fireEvent('hide', this, this.date);
16516     },
16517     
16518     setTime : function()
16519     {
16520         this.hide();
16521         this.setValue(this.time.format(this.format));
16522         
16523         this.fireEvent('select', this, this.date);
16524         
16525         
16526     },
16527     
16528     onMousedown: function(e){
16529         e.stopPropagation();
16530         e.preventDefault();
16531     },
16532     
16533     onIncrementHours: function()
16534     {
16535         Roo.log('onIncrementHours');
16536         this.time = this.time.add(Date.HOUR, 1);
16537         this.update();
16538         
16539     },
16540     
16541     onDecrementHours: function()
16542     {
16543         Roo.log('onDecrementHours');
16544         this.time = this.time.add(Date.HOUR, -1);
16545         this.update();
16546     },
16547     
16548     onIncrementMinutes: function()
16549     {
16550         Roo.log('onIncrementMinutes');
16551         this.time = this.time.add(Date.MINUTE, 1);
16552         this.update();
16553     },
16554     
16555     onDecrementMinutes: function()
16556     {
16557         Roo.log('onDecrementMinutes');
16558         this.time = this.time.add(Date.MINUTE, -1);
16559         this.update();
16560     },
16561     
16562     onTogglePeriod: function()
16563     {
16564         Roo.log('onTogglePeriod');
16565         this.time = this.time.add(Date.HOUR, 12);
16566         this.update();
16567     }
16568     
16569    
16570 });
16571
16572 Roo.apply(Roo.bootstrap.TimeField,  {
16573     
16574     content : {
16575         tag: 'tbody',
16576         cn: [
16577             {
16578                 tag: 'tr',
16579                 cn: [
16580                 {
16581                     tag: 'td',
16582                     colspan: '7'
16583                 }
16584                 ]
16585             }
16586         ]
16587     },
16588     
16589     footer : {
16590         tag: 'tfoot',
16591         cn: [
16592             {
16593                 tag: 'tr',
16594                 cn: [
16595                 {
16596                     tag: 'th',
16597                     colspan: '7',
16598                     cls: '',
16599                     cn: [
16600                         {
16601                             tag: 'button',
16602                             cls: 'btn btn-info ok',
16603                             html: 'OK'
16604                         }
16605                     ]
16606                 }
16607
16608                 ]
16609             }
16610         ]
16611     }
16612 });
16613
16614 Roo.apply(Roo.bootstrap.TimeField,  {
16615   
16616     template : {
16617         tag: 'div',
16618         cls: 'datepicker dropdown-menu',
16619         cn: [
16620             {
16621                 tag: 'div',
16622                 cls: 'datepicker-time',
16623                 cn: [
16624                 {
16625                     tag: 'table',
16626                     cls: 'table-condensed',
16627                     cn:[
16628                     Roo.bootstrap.TimeField.content,
16629                     Roo.bootstrap.TimeField.footer
16630                     ]
16631                 }
16632                 ]
16633             }
16634         ]
16635     }
16636 });
16637
16638  
16639
16640  /*
16641  * - LGPL
16642  *
16643  * MonthField
16644  * 
16645  */
16646
16647 /**
16648  * @class Roo.bootstrap.MonthField
16649  * @extends Roo.bootstrap.Input
16650  * Bootstrap MonthField class
16651  * 
16652  * @cfg {String} language default en
16653  * 
16654  * @constructor
16655  * Create a new MonthField
16656  * @param {Object} config The config object
16657  */
16658
16659 Roo.bootstrap.MonthField = function(config){
16660     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
16661     
16662     this.addEvents({
16663         /**
16664          * @event show
16665          * Fires when this field show.
16666          * @param {Roo.bootstrap.MonthField} this
16667          * @param {Mixed} date The date value
16668          */
16669         show : true,
16670         /**
16671          * @event show
16672          * Fires when this field hide.
16673          * @param {Roo.bootstrap.MonthField} this
16674          * @param {Mixed} date The date value
16675          */
16676         hide : true,
16677         /**
16678          * @event select
16679          * Fires when select a date.
16680          * @param {Roo.bootstrap.MonthField} this
16681          * @param {String} oldvalue The old value
16682          * @param {String} newvalue The new value
16683          */
16684         select : true
16685     });
16686 };
16687
16688 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
16689     
16690     onRender: function(ct, position)
16691     {
16692         
16693         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
16694         
16695         this.language = this.language || 'en';
16696         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
16697         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
16698         
16699         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
16700         this.isInline = false;
16701         this.isInput = true;
16702         this.component = this.el.select('.add-on', true).first() || false;
16703         this.component = (this.component && this.component.length === 0) ? false : this.component;
16704         this.hasInput = this.component && this.inputEL().length;
16705         
16706         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
16707         
16708         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16709         
16710         this.picker().on('mousedown', this.onMousedown, this);
16711         this.picker().on('click', this.onClick, this);
16712         
16713         this.picker().addClass('datepicker-dropdown');
16714         
16715         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16716             v.setStyle('width', '189px');
16717         });
16718         
16719         this.fillMonths();
16720         
16721         this.update();
16722         
16723         if(this.isInline) {
16724             this.show();
16725         }
16726         
16727     },
16728     
16729     setValue: function(v, suppressEvent)
16730     {   
16731         var o = this.getValue();
16732         
16733         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
16734         
16735         this.update();
16736
16737         if(suppressEvent !== true){
16738             this.fireEvent('select', this, o, v);
16739         }
16740         
16741     },
16742     
16743     getValue: function()
16744     {
16745         return this.value;
16746     },
16747     
16748     onClick: function(e) 
16749     {
16750         e.stopPropagation();
16751         e.preventDefault();
16752         
16753         var target = e.getTarget();
16754         
16755         if(target.nodeName.toLowerCase() === 'i'){
16756             target = Roo.get(target).dom.parentNode;
16757         }
16758         
16759         var nodeName = target.nodeName;
16760         var className = target.className;
16761         var html = target.innerHTML;
16762         
16763         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
16764             return;
16765         }
16766         
16767         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
16768         
16769         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16770         
16771         this.hide();
16772                         
16773     },
16774     
16775     picker : function()
16776     {
16777         return this.pickerEl;
16778     },
16779     
16780     fillMonths: function()
16781     {    
16782         var i = 0;
16783         var months = this.picker().select('>.datepicker-months td', true).first();
16784         
16785         months.dom.innerHTML = '';
16786         
16787         while (i < 12) {
16788             var month = {
16789                 tag: 'span',
16790                 cls: 'month',
16791                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
16792             }
16793             
16794             months.createChild(month);
16795         }
16796         
16797     },
16798     
16799     update: function()
16800     {
16801         var _this = this;
16802         
16803         if(typeof(this.vIndex) == 'undefined' && this.value.length){
16804             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
16805         }
16806         
16807         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
16808             e.removeClass('active');
16809             
16810             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
16811                 e.addClass('active');
16812             }
16813         })
16814     },
16815     
16816     place: function()
16817     {
16818         if(this.isInline) return;
16819         
16820         this.picker().removeClass(['bottom', 'top']);
16821         
16822         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16823             /*
16824              * place to the top of element!
16825              *
16826              */
16827             
16828             this.picker().addClass('top');
16829             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16830             
16831             return;
16832         }
16833         
16834         this.picker().addClass('bottom');
16835         
16836         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16837     },
16838     
16839     onFocus : function()
16840     {
16841         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
16842         this.show();
16843     },
16844     
16845     onBlur : function()
16846     {
16847         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
16848         
16849         var d = this.inputEl().getValue();
16850         
16851         this.setValue(d);
16852                 
16853         this.hide();
16854     },
16855     
16856     show : function()
16857     {
16858         this.picker().show();
16859         this.picker().select('>.datepicker-months', true).first().show();
16860         this.update();
16861         this.place();
16862         
16863         this.fireEvent('show', this, this.date);
16864     },
16865     
16866     hide : function()
16867     {
16868         if(this.isInline) return;
16869         this.picker().hide();
16870         this.fireEvent('hide', this, this.date);
16871         
16872     },
16873     
16874     onMousedown: function(e)
16875     {
16876         e.stopPropagation();
16877         e.preventDefault();
16878     },
16879     
16880     keyup: function(e)
16881     {
16882         Roo.bootstrap.MonthField.superclass.keyup.call(this);
16883         this.update();
16884     },
16885
16886     fireKey: function(e)
16887     {
16888         if (!this.picker().isVisible()){
16889             if (e.keyCode == 27) // allow escape to hide and re-show picker
16890                 this.show();
16891             return;
16892         }
16893         
16894         var dir;
16895         
16896         switch(e.keyCode){
16897             case 27: // escape
16898                 this.hide();
16899                 e.preventDefault();
16900                 break;
16901             case 37: // left
16902             case 39: // right
16903                 dir = e.keyCode == 37 ? -1 : 1;
16904                 
16905                 this.vIndex = this.vIndex + dir;
16906                 
16907                 if(this.vIndex < 0){
16908                     this.vIndex = 0;
16909                 }
16910                 
16911                 if(this.vIndex > 11){
16912                     this.vIndex = 11;
16913                 }
16914                 
16915                 if(isNaN(this.vIndex)){
16916                     this.vIndex = 0;
16917                 }
16918                 
16919                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16920                 
16921                 break;
16922             case 38: // up
16923             case 40: // down
16924                 
16925                 dir = e.keyCode == 38 ? -1 : 1;
16926                 
16927                 this.vIndex = this.vIndex + dir * 4;
16928                 
16929                 if(this.vIndex < 0){
16930                     this.vIndex = 0;
16931                 }
16932                 
16933                 if(this.vIndex > 11){
16934                     this.vIndex = 11;
16935                 }
16936                 
16937                 if(isNaN(this.vIndex)){
16938                     this.vIndex = 0;
16939                 }
16940                 
16941                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16942                 break;
16943                 
16944             case 13: // enter
16945                 
16946                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16947                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16948                 }
16949                 
16950                 this.hide();
16951                 e.preventDefault();
16952                 break;
16953             case 9: // tab
16954                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16955                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16956                 }
16957                 this.hide();
16958                 break;
16959             case 16: // shift
16960             case 17: // ctrl
16961             case 18: // alt
16962                 break;
16963             default :
16964                 this.hide();
16965                 
16966         }
16967     },
16968     
16969     remove: function() 
16970     {
16971         this.picker().remove();
16972     }
16973    
16974 });
16975
16976 Roo.apply(Roo.bootstrap.MonthField,  {
16977     
16978     content : {
16979         tag: 'tbody',
16980         cn: [
16981         {
16982             tag: 'tr',
16983             cn: [
16984             {
16985                 tag: 'td',
16986                 colspan: '7'
16987             }
16988             ]
16989         }
16990         ]
16991     },
16992     
16993     dates:{
16994         en: {
16995             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16996             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
16997         }
16998     }
16999 });
17000
17001 Roo.apply(Roo.bootstrap.MonthField,  {
17002   
17003     template : {
17004         tag: 'div',
17005         cls: 'datepicker dropdown-menu roo-dynamic',
17006         cn: [
17007             {
17008                 tag: 'div',
17009                 cls: 'datepicker-months',
17010                 cn: [
17011                 {
17012                     tag: 'table',
17013                     cls: 'table-condensed',
17014                     cn:[
17015                         Roo.bootstrap.DateField.content
17016                     ]
17017                 }
17018                 ]
17019             }
17020         ]
17021     }
17022 });
17023
17024  
17025
17026  
17027  /*
17028  * - LGPL
17029  *
17030  * CheckBox
17031  * 
17032  */
17033
17034 /**
17035  * @class Roo.bootstrap.CheckBox
17036  * @extends Roo.bootstrap.Input
17037  * Bootstrap CheckBox class
17038  * 
17039  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17040  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17041  * @cfg {String} boxLabel The text that appears beside the checkbox
17042  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
17043  * @cfg {Boolean} checked initnal the element
17044  * @cfg {Boolean} inline inline the element (default false)
17045  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
17046  * 
17047  * @constructor
17048  * Create a new CheckBox
17049  * @param {Object} config The config object
17050  */
17051
17052 Roo.bootstrap.CheckBox = function(config){
17053     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
17054    
17055     this.addEvents({
17056         /**
17057         * @event check
17058         * Fires when the element is checked or unchecked.
17059         * @param {Roo.bootstrap.CheckBox} this This input
17060         * @param {Boolean} checked The new checked value
17061         */
17062        check : true
17063     });
17064     
17065 };
17066
17067 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
17068   
17069     inputType: 'checkbox',
17070     inputValue: 1,
17071     valueOff: 0,
17072     boxLabel: false,
17073     checked: false,
17074     weight : false,
17075     inline: false,
17076     
17077     getAutoCreate : function()
17078     {
17079         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17080         
17081         var id = Roo.id();
17082         
17083         var cfg = {};
17084         
17085         cfg.cls = 'form-group ' + this.inputType; //input-group
17086         
17087         if(this.inline){
17088             cfg.cls += ' ' + this.inputType + '-inline';
17089         }
17090         
17091         var input =  {
17092             tag: 'input',
17093             id : id,
17094             type : this.inputType,
17095             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
17096             cls : 'roo-' + this.inputType, //'form-box',
17097             placeholder : this.placeholder || ''
17098             
17099         };
17100         
17101         if (this.weight) { // Validity check?
17102             cfg.cls += " " + this.inputType + "-" + this.weight;
17103         }
17104         
17105         if (this.disabled) {
17106             input.disabled=true;
17107         }
17108         
17109         if(this.checked){
17110             input.checked = this.checked;
17111         }
17112         
17113         if (this.name) {
17114             input.name = this.name;
17115         }
17116         
17117         if (this.size) {
17118             input.cls += ' input-' + this.size;
17119         }
17120         
17121         var settings=this;
17122         
17123         ['xs','sm','md','lg'].map(function(size){
17124             if (settings[size]) {
17125                 cfg.cls += ' col-' + size + '-' + settings[size];
17126             }
17127         });
17128         
17129         var inputblock = input;
17130          
17131         if (this.before || this.after) {
17132             
17133             inputblock = {
17134                 cls : 'input-group',
17135                 cn :  [] 
17136             };
17137             
17138             if (this.before) {
17139                 inputblock.cn.push({
17140                     tag :'span',
17141                     cls : 'input-group-addon',
17142                     html : this.before
17143                 });
17144             }
17145             
17146             inputblock.cn.push(input);
17147             
17148             if (this.after) {
17149                 inputblock.cn.push({
17150                     tag :'span',
17151                     cls : 'input-group-addon',
17152                     html : this.after
17153                 });
17154             }
17155             
17156         }
17157         
17158         if (align ==='left' && this.fieldLabel.length) {
17159                 Roo.log("left and has label");
17160                 cfg.cn = [
17161                     
17162                     {
17163                         tag: 'label',
17164                         'for' :  id,
17165                         cls : 'control-label col-md-' + this.labelWidth,
17166                         html : this.fieldLabel
17167                         
17168                     },
17169                     {
17170                         cls : "col-md-" + (12 - this.labelWidth), 
17171                         cn: [
17172                             inputblock
17173                         ]
17174                     }
17175                     
17176                 ];
17177         } else if ( this.fieldLabel.length) {
17178                 Roo.log(" label");
17179                 cfg.cn = [
17180                    
17181                     {
17182                         tag: this.boxLabel ? 'span' : 'label',
17183                         'for': id,
17184                         cls: 'control-label box-input-label',
17185                         //cls : 'input-group-addon',
17186                         html : this.fieldLabel
17187                         
17188                     },
17189                     
17190                     inputblock
17191                     
17192                 ];
17193
17194         } else {
17195             
17196                 Roo.log(" no label && no align");
17197                 cfg.cn = [  inputblock ] ;
17198                 
17199                 
17200         }
17201         if(this.boxLabel){
17202              var boxLabelCfg = {
17203                 tag: 'label',
17204                 //'for': id, // box label is handled by onclick - so no for...
17205                 cls: 'box-label',
17206                 html: this.boxLabel
17207             }
17208             
17209             if(this.tooltip){
17210                 boxLabelCfg.tooltip = this.tooltip;
17211             }
17212              
17213             cfg.cn.push(boxLabelCfg);
17214         }
17215         
17216         
17217        
17218         return cfg;
17219         
17220     },
17221     
17222     /**
17223      * return the real input element.
17224      */
17225     inputEl: function ()
17226     {
17227         return this.el.select('input.roo-' + this.inputType,true).first();
17228     },
17229     
17230     labelEl: function()
17231     {
17232         return this.el.select('label.control-label',true).first();
17233     },
17234     /* depricated... */
17235     
17236     label: function()
17237     {
17238         return this.labelEl();
17239     },
17240     
17241     initEvents : function()
17242     {
17243 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17244         
17245         this.inputEl().on('click', this.onClick,  this);
17246         
17247         if (this.boxLabel) { 
17248             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
17249         }
17250         
17251         this.startValue = this.getValue();
17252         
17253         if(this.groupId){
17254             Roo.bootstrap.CheckBox.register(this);
17255         }
17256     },
17257     
17258     onClick : function()
17259     {   
17260         this.setChecked(!this.checked);
17261     },
17262     
17263     setChecked : function(state,suppressEvent)
17264     {
17265         this.startValue = this.getValue();
17266         
17267         if(this.inputType == 'radio'){
17268             
17269             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17270                 e.dom.checked = false;
17271             });
17272             
17273             this.inputEl().dom.checked = true;
17274             
17275             this.inputEl().dom.value = this.inputValue;
17276             
17277             if(suppressEvent !== true){
17278                 this.fireEvent('check', this, true);
17279             }
17280             
17281             this.validate();
17282             
17283             return;
17284         }
17285         
17286         this.checked = state;
17287         
17288         this.inputEl().dom.checked = state;
17289         
17290         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17291         
17292         if(suppressEvent !== true){
17293             this.fireEvent('check', this, state);
17294         }
17295         
17296         this.validate();
17297     },
17298     
17299     getValue : function()
17300     {
17301         if(this.inputType == 'radio'){
17302             return this.getGroupValue();
17303         }
17304         
17305         return this.inputEl().getValue();
17306         
17307     },
17308     
17309     getGroupValue : function()
17310     {
17311         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
17312             return '';
17313         }
17314         
17315         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
17316     },
17317     
17318     setValue : function(v,suppressEvent)
17319     {
17320         if(this.inputType == 'radio'){
17321             this.setGroupValue(v, suppressEvent);
17322             return;
17323         }
17324         
17325         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
17326         
17327         this.validate();
17328     },
17329     
17330     setGroupValue : function(v, suppressEvent)
17331     {
17332         this.startValue = this.getValue();
17333         
17334         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17335             e.dom.checked = false;
17336             
17337             if(e.dom.value == v){
17338                 e.dom.checked = true;
17339             }
17340         });
17341         
17342         if(suppressEvent !== true){
17343             this.fireEvent('check', this, true);
17344         }
17345
17346         this.validate();
17347         
17348         return;
17349     },
17350     
17351     validate : function()
17352     {
17353         if(
17354                 this.disabled || 
17355                 (this.inputType == 'radio' && this.validateRadio()) ||
17356                 (this.inputType == 'checkbox' && this.validateCheckbox())
17357         ){
17358             this.markValid();
17359             return true;
17360         }
17361         
17362         this.markInvalid();
17363         return false;
17364     },
17365     
17366     validateRadio : function()
17367     {
17368         var valid = false;
17369         
17370         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17371             if(!e.dom.checked){
17372                 return;
17373             }
17374             
17375             valid = true;
17376             
17377             return false;
17378         });
17379         
17380         return valid;
17381     },
17382     
17383     validateCheckbox : function()
17384     {
17385         if(!this.groupId){
17386             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
17387         }
17388         
17389         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17390         
17391         if(!group){
17392             return false;
17393         }
17394         
17395         var r = false;
17396         
17397         for(var i in group){
17398             if(r){
17399                 break;
17400             }
17401             
17402             r = (group[i].getValue() == group[i].inputValue) ? true : false;
17403         }
17404         
17405         return r;
17406     },
17407     
17408     /**
17409      * Mark this field as valid
17410      */
17411     markValid : function()
17412     {
17413         if(this.allowBlank){
17414             return;
17415         }
17416         
17417         var _this = this;
17418         
17419         this.fireEvent('valid', this);
17420         
17421         if(this.inputType == 'radio'){
17422             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17423                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17424                 e.findParent('.form-group', false, true).addClass(_this.validClass);
17425             });
17426             
17427             return;
17428         }
17429         
17430         if(!this.groupId){
17431             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17432             this.el.findParent('.form-group', false, true).addClass(this.validClass);
17433             return;
17434         }
17435         
17436         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17437             
17438         if(!group){
17439             return;
17440         }
17441         
17442         for(var i in group){
17443             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17444             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
17445         }
17446     },
17447     
17448      /**
17449      * Mark this field as invalid
17450      * @param {String} msg The validation message
17451      */
17452     markInvalid : function(msg)
17453     {
17454         if(this.allowBlank){
17455             return;
17456         }
17457         
17458         var _this = this;
17459         
17460         this.fireEvent('invalid', this, msg);
17461         
17462         if(this.inputType == 'radio'){
17463             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17464                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17465                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
17466             });
17467             
17468             return;
17469         }
17470         
17471         if(!this.groupId){
17472             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17473             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
17474             return;
17475         }
17476         
17477         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17478             
17479         if(!group){
17480             return;
17481         }
17482         
17483         for(var i in group){
17484             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17485             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
17486         }
17487         
17488     }
17489     
17490 });
17491
17492 Roo.apply(Roo.bootstrap.CheckBox, {
17493     
17494     groups: {},
17495     
17496      /**
17497     * register a CheckBox Group
17498     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
17499     */
17500     register : function(checkbox)
17501     {
17502         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
17503             this.groups[checkbox.groupId] = {};
17504         }
17505         
17506         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
17507             return;
17508         }
17509         
17510         this.groups[checkbox.groupId][checkbox.name] = checkbox;
17511         
17512     },
17513     /**
17514     * fetch a CheckBox Group based on the group ID
17515     * @param {string} the group ID
17516     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
17517     */
17518     get: function(groupId) {
17519         if (typeof(this.groups[groupId]) == 'undefined') {
17520             return false;
17521         }
17522         
17523         return this.groups[groupId] ;
17524     }
17525     
17526     
17527 });
17528 /*
17529  * - LGPL
17530  *
17531  * Radio
17532  *
17533  *
17534  * not inline
17535  *<div class="radio">
17536   <label>
17537     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
17538     Option one is this and that&mdash;be sure to include why it's great
17539   </label>
17540 </div>
17541  *
17542  *
17543  *inline
17544  *<span>
17545  *<label class="radio-inline">fieldLabel</label>
17546  *<label class="radio-inline">
17547   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
17548 </label>
17549 <span>
17550  * 
17551  * 
17552  */
17553
17554 /**
17555  * @class Roo.bootstrap.Radio
17556  * @extends Roo.bootstrap.CheckBox
17557  * Bootstrap Radio class
17558
17559  * @constructor
17560  * Create a new Radio
17561  * @param {Object} config The config object
17562  */
17563
17564 Roo.bootstrap.Radio = function(config){
17565     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
17566    
17567 };
17568
17569 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
17570     
17571     inputType: 'radio',
17572     inputValue: '',
17573     valueOff: '',
17574     
17575     getAutoCreate : function()
17576     {
17577         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17578         align = align || 'left'; // default...
17579         
17580         
17581         
17582         var id = Roo.id();
17583         
17584         var cfg = {
17585                 tag : this.inline ? 'span' : 'div',
17586                 cls : '',
17587                 cn : []
17588         };
17589         
17590         var inline = this.inline ? ' radio-inline' : '';
17591         
17592         var lbl = {
17593                 tag: 'label' ,
17594                 // does not need for, as we wrap the input with it..
17595                 'for' : id,
17596                 cls : 'control-label box-label' + inline,
17597                 cn : []
17598         };
17599         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
17600         
17601         var fieldLabel = {
17602             tag: 'label' ,
17603             //cls : 'control-label' + inline,
17604             html : this.fieldLabel,
17605             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
17606         };
17607         
17608  
17609         
17610         
17611         var input =  {
17612             tag: 'input',
17613             id : id,
17614             type : this.inputType,
17615             //value : (!this.checked) ? this.valueOff : this.inputValue,
17616             value : this.inputValue,
17617             cls : 'roo-radio',
17618             placeholder : this.placeholder || '' // ?? needed????
17619             
17620         };
17621         if (this.weight) { // Validity check?
17622             input.cls += " radio-" + this.weight;
17623         }
17624         if (this.disabled) {
17625             input.disabled=true;
17626         }
17627         
17628         if(this.checked){
17629             input.checked = this.checked;
17630         }
17631         
17632         if (this.name) {
17633             input.name = this.name;
17634         }
17635         
17636         if (this.size) {
17637             input.cls += ' input-' + this.size;
17638         }
17639         
17640         //?? can span's inline have a width??
17641         
17642         var settings=this;
17643         ['xs','sm','md','lg'].map(function(size){
17644             if (settings[size]) {
17645                 cfg.cls += ' col-' + size + '-' + settings[size];
17646             }
17647         });
17648         
17649         var inputblock = input;
17650         
17651         if (this.before || this.after) {
17652             
17653             inputblock = {
17654                 cls : 'input-group',
17655                 tag : 'span',
17656                 cn :  [] 
17657             };
17658             if (this.before) {
17659                 inputblock.cn.push({
17660                     tag :'span',
17661                     cls : 'input-group-addon',
17662                     html : this.before
17663                 });
17664             }
17665             inputblock.cn.push(input);
17666             if (this.after) {
17667                 inputblock.cn.push({
17668                     tag :'span',
17669                     cls : 'input-group-addon',
17670                     html : this.after
17671                 });
17672             }
17673             
17674         };
17675         
17676         
17677         if (this.fieldLabel && this.fieldLabel.length) {
17678             cfg.cn.push(fieldLabel);
17679         }
17680        
17681         // normal bootstrap puts the input inside the label.
17682         // however with our styled version - it has to go after the input.
17683        
17684         //lbl.cn.push(inputblock);
17685         
17686         var lblwrap =  {
17687             tag: 'span',
17688             cls: 'radio' + inline,
17689             cn: [
17690                 inputblock,
17691                 lbl
17692             ]
17693         };
17694         
17695         cfg.cn.push( lblwrap);
17696         
17697         if(this.boxLabel){
17698             lbl.cn.push({
17699                 tag: 'span',
17700                 html: this.boxLabel
17701             })
17702         }
17703          
17704         
17705         return cfg;
17706         
17707     },
17708     
17709     initEvents : function()
17710     {
17711 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17712         
17713         this.inputEl().on('click', this.onClick,  this);
17714         if (this.boxLabel) {
17715             Roo.log('find label')
17716             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
17717         }
17718         
17719     },
17720     
17721     inputEl: function ()
17722     {
17723         return this.el.select('input.roo-radio',true).first();
17724     },
17725     onClick : function()
17726     {   
17727         Roo.log("click");
17728         this.setChecked(true);
17729     },
17730     
17731     setChecked : function(state,suppressEvent)
17732     {
17733         if(state){
17734             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17735                 v.dom.checked = false;
17736             });
17737         }
17738         Roo.log(this.inputEl().dom);
17739         this.checked = state;
17740         this.inputEl().dom.checked = state;
17741         
17742         if(suppressEvent !== true){
17743             this.fireEvent('check', this, state);
17744         }
17745         
17746         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17747         
17748     },
17749     
17750     getGroupValue : function()
17751     {
17752         var value = '';
17753         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17754             if(v.dom.checked == true){
17755                 value = v.dom.value;
17756             }
17757         });
17758         
17759         return value;
17760     },
17761     
17762     /**
17763      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
17764      * @return {Mixed} value The field value
17765      */
17766     getValue : function(){
17767         return this.getGroupValue();
17768     }
17769     
17770 });
17771
17772  
17773 //<script type="text/javascript">
17774
17775 /*
17776  * Based  Ext JS Library 1.1.1
17777  * Copyright(c) 2006-2007, Ext JS, LLC.
17778  * LGPL
17779  *
17780  */
17781  
17782 /**
17783  * @class Roo.HtmlEditorCore
17784  * @extends Roo.Component
17785  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
17786  *
17787  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
17788  */
17789
17790 Roo.HtmlEditorCore = function(config){
17791     
17792     
17793     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
17794     
17795     
17796     this.addEvents({
17797         /**
17798          * @event initialize
17799          * Fires when the editor is fully initialized (including the iframe)
17800          * @param {Roo.HtmlEditorCore} this
17801          */
17802         initialize: true,
17803         /**
17804          * @event activate
17805          * Fires when the editor is first receives the focus. Any insertion must wait
17806          * until after this event.
17807          * @param {Roo.HtmlEditorCore} this
17808          */
17809         activate: true,
17810          /**
17811          * @event beforesync
17812          * Fires before the textarea is updated with content from the editor iframe. Return false
17813          * to cancel the sync.
17814          * @param {Roo.HtmlEditorCore} this
17815          * @param {String} html
17816          */
17817         beforesync: true,
17818          /**
17819          * @event beforepush
17820          * Fires before the iframe editor is updated with content from the textarea. Return false
17821          * to cancel the push.
17822          * @param {Roo.HtmlEditorCore} this
17823          * @param {String} html
17824          */
17825         beforepush: true,
17826          /**
17827          * @event sync
17828          * Fires when the textarea is updated with content from the editor iframe.
17829          * @param {Roo.HtmlEditorCore} this
17830          * @param {String} html
17831          */
17832         sync: true,
17833          /**
17834          * @event push
17835          * Fires when the iframe editor is updated with content from the textarea.
17836          * @param {Roo.HtmlEditorCore} this
17837          * @param {String} html
17838          */
17839         push: true,
17840         
17841         /**
17842          * @event editorevent
17843          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17844          * @param {Roo.HtmlEditorCore} this
17845          */
17846         editorevent: true
17847         
17848     });
17849     
17850     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
17851     
17852     // defaults : white / black...
17853     this.applyBlacklists();
17854     
17855     
17856     
17857 };
17858
17859
17860 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
17861
17862
17863      /**
17864      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
17865      */
17866     
17867     owner : false,
17868     
17869      /**
17870      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17871      *                        Roo.resizable.
17872      */
17873     resizable : false,
17874      /**
17875      * @cfg {Number} height (in pixels)
17876      */   
17877     height: 300,
17878    /**
17879      * @cfg {Number} width (in pixels)
17880      */   
17881     width: 500,
17882     
17883     /**
17884      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17885      * 
17886      */
17887     stylesheets: false,
17888     
17889     // id of frame..
17890     frameId: false,
17891     
17892     // private properties
17893     validationEvent : false,
17894     deferHeight: true,
17895     initialized : false,
17896     activated : false,
17897     sourceEditMode : false,
17898     onFocus : Roo.emptyFn,
17899     iframePad:3,
17900     hideMode:'offsets',
17901     
17902     clearUp: true,
17903     
17904     // blacklist + whitelisted elements..
17905     black: false,
17906     white: false,
17907      
17908     
17909
17910     /**
17911      * Protected method that will not generally be called directly. It
17912      * is called when the editor initializes the iframe with HTML contents. Override this method if you
17913      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
17914      */
17915     getDocMarkup : function(){
17916         // body styles..
17917         var st = '';
17918         
17919         // inherit styels from page...?? 
17920         if (this.stylesheets === false) {
17921             
17922             Roo.get(document.head).select('style').each(function(node) {
17923                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17924             });
17925             
17926             Roo.get(document.head).select('link').each(function(node) { 
17927                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17928             });
17929             
17930         } else if (!this.stylesheets.length) {
17931                 // simple..
17932                 st = '<style type="text/css">' +
17933                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17934                    '</style>';
17935         } else { 
17936             
17937         }
17938         
17939         st +=  '<style type="text/css">' +
17940             'IMG { cursor: pointer } ' +
17941         '</style>';
17942
17943         
17944         return '<html><head>' + st  +
17945             //<style type="text/css">' +
17946             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17947             //'</style>' +
17948             ' </head><body class="roo-htmleditor-body"></body></html>';
17949     },
17950
17951     // private
17952     onRender : function(ct, position)
17953     {
17954         var _t = this;
17955         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
17956         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
17957         
17958         
17959         this.el.dom.style.border = '0 none';
17960         this.el.dom.setAttribute('tabIndex', -1);
17961         this.el.addClass('x-hidden hide');
17962         
17963         
17964         
17965         if(Roo.isIE){ // fix IE 1px bogus margin
17966             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
17967         }
17968        
17969         
17970         this.frameId = Roo.id();
17971         
17972          
17973         
17974         var iframe = this.owner.wrap.createChild({
17975             tag: 'iframe',
17976             cls: 'form-control', // bootstrap..
17977             id: this.frameId,
17978             name: this.frameId,
17979             frameBorder : 'no',
17980             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
17981         }, this.el
17982         );
17983         
17984         
17985         this.iframe = iframe.dom;
17986
17987          this.assignDocWin();
17988         
17989         this.doc.designMode = 'on';
17990        
17991         this.doc.open();
17992         this.doc.write(this.getDocMarkup());
17993         this.doc.close();
17994
17995         
17996         var task = { // must defer to wait for browser to be ready
17997             run : function(){
17998                 //console.log("run task?" + this.doc.readyState);
17999                 this.assignDocWin();
18000                 if(this.doc.body || this.doc.readyState == 'complete'){
18001                     try {
18002                         this.doc.designMode="on";
18003                     } catch (e) {
18004                         return;
18005                     }
18006                     Roo.TaskMgr.stop(task);
18007                     this.initEditor.defer(10, this);
18008                 }
18009             },
18010             interval : 10,
18011             duration: 10000,
18012             scope: this
18013         };
18014         Roo.TaskMgr.start(task);
18015
18016     },
18017
18018     // private
18019     onResize : function(w, h)
18020     {
18021          Roo.log('resize: ' +w + ',' + h );
18022         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18023         if(!this.iframe){
18024             return;
18025         }
18026         if(typeof w == 'number'){
18027             
18028             this.iframe.style.width = w + 'px';
18029         }
18030         if(typeof h == 'number'){
18031             
18032             this.iframe.style.height = h + 'px';
18033             if(this.doc){
18034                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18035             }
18036         }
18037         
18038     },
18039
18040     /**
18041      * Toggles the editor between standard and source edit mode.
18042      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18043      */
18044     toggleSourceEdit : function(sourceEditMode){
18045         
18046         this.sourceEditMode = sourceEditMode === true;
18047         
18048         if(this.sourceEditMode){
18049  
18050             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
18051             
18052         }else{
18053             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
18054             //this.iframe.className = '';
18055             this.deferFocus();
18056         }
18057         //this.setSize(this.owner.wrap.getSize());
18058         //this.fireEvent('editmodechange', this, this.sourceEditMode);
18059     },
18060
18061     
18062   
18063
18064     /**
18065      * Protected method that will not generally be called directly. If you need/want
18066      * custom HTML cleanup, this is the method you should override.
18067      * @param {String} html The HTML to be cleaned
18068      * return {String} The cleaned HTML
18069      */
18070     cleanHtml : function(html){
18071         html = String(html);
18072         if(html.length > 5){
18073             if(Roo.isSafari){ // strip safari nonsense
18074                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
18075             }
18076         }
18077         if(html == '&nbsp;'){
18078             html = '';
18079         }
18080         return html;
18081     },
18082
18083     /**
18084      * HTML Editor -> Textarea
18085      * Protected method that will not generally be called directly. Syncs the contents
18086      * of the editor iframe with the textarea.
18087      */
18088     syncValue : function(){
18089         if(this.initialized){
18090             var bd = (this.doc.body || this.doc.documentElement);
18091             //this.cleanUpPaste(); -- this is done else where and causes havoc..
18092             var html = bd.innerHTML;
18093             if(Roo.isSafari){
18094                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
18095                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
18096                 if(m && m[1]){
18097                     html = '<div style="'+m[0]+'">' + html + '</div>';
18098                 }
18099             }
18100             html = this.cleanHtml(html);
18101             // fix up the special chars.. normaly like back quotes in word...
18102             // however we do not want to do this with chinese..
18103             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
18104                 var cc = b.charCodeAt();
18105                 if (
18106                     (cc >= 0x4E00 && cc < 0xA000 ) ||
18107                     (cc >= 0x3400 && cc < 0x4E00 ) ||
18108                     (cc >= 0xf900 && cc < 0xfb00 )
18109                 ) {
18110                         return b;
18111                 }
18112                 return "&#"+cc+";" 
18113             });
18114             if(this.owner.fireEvent('beforesync', this, html) !== false){
18115                 this.el.dom.value = html;
18116                 this.owner.fireEvent('sync', this, html);
18117             }
18118         }
18119     },
18120
18121     /**
18122      * Protected method that will not generally be called directly. Pushes the value of the textarea
18123      * into the iframe editor.
18124      */
18125     pushValue : function(){
18126         if(this.initialized){
18127             var v = this.el.dom.value.trim();
18128             
18129 //            if(v.length < 1){
18130 //                v = '&#160;';
18131 //            }
18132             
18133             if(this.owner.fireEvent('beforepush', this, v) !== false){
18134                 var d = (this.doc.body || this.doc.documentElement);
18135                 d.innerHTML = v;
18136                 this.cleanUpPaste();
18137                 this.el.dom.value = d.innerHTML;
18138                 this.owner.fireEvent('push', this, v);
18139             }
18140         }
18141     },
18142
18143     // private
18144     deferFocus : function(){
18145         this.focus.defer(10, this);
18146     },
18147
18148     // doc'ed in Field
18149     focus : function(){
18150         if(this.win && !this.sourceEditMode){
18151             this.win.focus();
18152         }else{
18153             this.el.focus();
18154         }
18155     },
18156     
18157     assignDocWin: function()
18158     {
18159         var iframe = this.iframe;
18160         
18161          if(Roo.isIE){
18162             this.doc = iframe.contentWindow.document;
18163             this.win = iframe.contentWindow;
18164         } else {
18165 //            if (!Roo.get(this.frameId)) {
18166 //                return;
18167 //            }
18168 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18169 //            this.win = Roo.get(this.frameId).dom.contentWindow;
18170             
18171             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
18172                 return;
18173             }
18174             
18175             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18176             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
18177         }
18178     },
18179     
18180     // private
18181     initEditor : function(){
18182         //console.log("INIT EDITOR");
18183         this.assignDocWin();
18184         
18185         
18186         
18187         this.doc.designMode="on";
18188         this.doc.open();
18189         this.doc.write(this.getDocMarkup());
18190         this.doc.close();
18191         
18192         var dbody = (this.doc.body || this.doc.documentElement);
18193         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
18194         // this copies styles from the containing element into thsi one..
18195         // not sure why we need all of this..
18196         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
18197         
18198         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
18199         //ss['background-attachment'] = 'fixed'; // w3c
18200         dbody.bgProperties = 'fixed'; // ie
18201         //Roo.DomHelper.applyStyles(dbody, ss);
18202         Roo.EventManager.on(this.doc, {
18203             //'mousedown': this.onEditorEvent,
18204             'mouseup': this.onEditorEvent,
18205             'dblclick': this.onEditorEvent,
18206             'click': this.onEditorEvent,
18207             'keyup': this.onEditorEvent,
18208             buffer:100,
18209             scope: this
18210         });
18211         if(Roo.isGecko){
18212             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
18213         }
18214         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
18215             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
18216         }
18217         this.initialized = true;
18218
18219         this.owner.fireEvent('initialize', this);
18220         this.pushValue();
18221     },
18222
18223     // private
18224     onDestroy : function(){
18225         
18226         
18227         
18228         if(this.rendered){
18229             
18230             //for (var i =0; i < this.toolbars.length;i++) {
18231             //    // fixme - ask toolbars for heights?
18232             //    this.toolbars[i].onDestroy();
18233            // }
18234             
18235             //this.wrap.dom.innerHTML = '';
18236             //this.wrap.remove();
18237         }
18238     },
18239
18240     // private
18241     onFirstFocus : function(){
18242         
18243         this.assignDocWin();
18244         
18245         
18246         this.activated = true;
18247          
18248     
18249         if(Roo.isGecko){ // prevent silly gecko errors
18250             this.win.focus();
18251             var s = this.win.getSelection();
18252             if(!s.focusNode || s.focusNode.nodeType != 3){
18253                 var r = s.getRangeAt(0);
18254                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
18255                 r.collapse(true);
18256                 this.deferFocus();
18257             }
18258             try{
18259                 this.execCmd('useCSS', true);
18260                 this.execCmd('styleWithCSS', false);
18261             }catch(e){}
18262         }
18263         this.owner.fireEvent('activate', this);
18264     },
18265
18266     // private
18267     adjustFont: function(btn){
18268         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
18269         //if(Roo.isSafari){ // safari
18270         //    adjust *= 2;
18271        // }
18272         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
18273         if(Roo.isSafari){ // safari
18274             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
18275             v =  (v < 10) ? 10 : v;
18276             v =  (v > 48) ? 48 : v;
18277             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
18278             
18279         }
18280         
18281         
18282         v = Math.max(1, v+adjust);
18283         
18284         this.execCmd('FontSize', v  );
18285     },
18286
18287     onEditorEvent : function(e){
18288         this.owner.fireEvent('editorevent', this, e);
18289       //  this.updateToolbar();
18290         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
18291     },
18292
18293     insertTag : function(tg)
18294     {
18295         // could be a bit smarter... -> wrap the current selected tRoo..
18296         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
18297             
18298             range = this.createRange(this.getSelection());
18299             var wrappingNode = this.doc.createElement(tg.toLowerCase());
18300             wrappingNode.appendChild(range.extractContents());
18301             range.insertNode(wrappingNode);
18302
18303             return;
18304             
18305             
18306             
18307         }
18308         this.execCmd("formatblock",   tg);
18309         
18310     },
18311     
18312     insertText : function(txt)
18313     {
18314         
18315         
18316         var range = this.createRange();
18317         range.deleteContents();
18318                //alert(Sender.getAttribute('label'));
18319                
18320         range.insertNode(this.doc.createTextNode(txt));
18321     } ,
18322     
18323      
18324
18325     /**
18326      * Executes a Midas editor command on the editor document and performs necessary focus and
18327      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
18328      * @param {String} cmd The Midas command
18329      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18330      */
18331     relayCmd : function(cmd, value){
18332         this.win.focus();
18333         this.execCmd(cmd, value);
18334         this.owner.fireEvent('editorevent', this);
18335         //this.updateToolbar();
18336         this.owner.deferFocus();
18337     },
18338
18339     /**
18340      * Executes a Midas editor command directly on the editor document.
18341      * For visual commands, you should use {@link #relayCmd} instead.
18342      * <b>This should only be called after the editor is initialized.</b>
18343      * @param {String} cmd The Midas command
18344      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18345      */
18346     execCmd : function(cmd, value){
18347         this.doc.execCommand(cmd, false, value === undefined ? null : value);
18348         this.syncValue();
18349     },
18350  
18351  
18352    
18353     /**
18354      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
18355      * to insert tRoo.
18356      * @param {String} text | dom node.. 
18357      */
18358     insertAtCursor : function(text)
18359     {
18360         
18361         
18362         
18363         if(!this.activated){
18364             return;
18365         }
18366         /*
18367         if(Roo.isIE){
18368             this.win.focus();
18369             var r = this.doc.selection.createRange();
18370             if(r){
18371                 r.collapse(true);
18372                 r.pasteHTML(text);
18373                 this.syncValue();
18374                 this.deferFocus();
18375             
18376             }
18377             return;
18378         }
18379         */
18380         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
18381             this.win.focus();
18382             
18383             
18384             // from jquery ui (MIT licenced)
18385             var range, node;
18386             var win = this.win;
18387             
18388             if (win.getSelection && win.getSelection().getRangeAt) {
18389                 range = win.getSelection().getRangeAt(0);
18390                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
18391                 range.insertNode(node);
18392             } else if (win.document.selection && win.document.selection.createRange) {
18393                 // no firefox support
18394                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18395                 win.document.selection.createRange().pasteHTML(txt);
18396             } else {
18397                 // no firefox support
18398                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18399                 this.execCmd('InsertHTML', txt);
18400             } 
18401             
18402             this.syncValue();
18403             
18404             this.deferFocus();
18405         }
18406     },
18407  // private
18408     mozKeyPress : function(e){
18409         if(e.ctrlKey){
18410             var c = e.getCharCode(), cmd;
18411           
18412             if(c > 0){
18413                 c = String.fromCharCode(c).toLowerCase();
18414                 switch(c){
18415                     case 'b':
18416                         cmd = 'bold';
18417                         break;
18418                     case 'i':
18419                         cmd = 'italic';
18420                         break;
18421                     
18422                     case 'u':
18423                         cmd = 'underline';
18424                         break;
18425                     
18426                     case 'v':
18427                         this.cleanUpPaste.defer(100, this);
18428                         return;
18429                         
18430                 }
18431                 if(cmd){
18432                     this.win.focus();
18433                     this.execCmd(cmd);
18434                     this.deferFocus();
18435                     e.preventDefault();
18436                 }
18437                 
18438             }
18439         }
18440     },
18441
18442     // private
18443     fixKeys : function(){ // load time branching for fastest keydown performance
18444         if(Roo.isIE){
18445             return function(e){
18446                 var k = e.getKey(), r;
18447                 if(k == e.TAB){
18448                     e.stopEvent();
18449                     r = this.doc.selection.createRange();
18450                     if(r){
18451                         r.collapse(true);
18452                         r.pasteHTML('&#160;&#160;&#160;&#160;');
18453                         this.deferFocus();
18454                     }
18455                     return;
18456                 }
18457                 
18458                 if(k == e.ENTER){
18459                     r = this.doc.selection.createRange();
18460                     if(r){
18461                         var target = r.parentElement();
18462                         if(!target || target.tagName.toLowerCase() != 'li'){
18463                             e.stopEvent();
18464                             r.pasteHTML('<br />');
18465                             r.collapse(false);
18466                             r.select();
18467                         }
18468                     }
18469                 }
18470                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18471                     this.cleanUpPaste.defer(100, this);
18472                     return;
18473                 }
18474                 
18475                 
18476             };
18477         }else if(Roo.isOpera){
18478             return function(e){
18479                 var k = e.getKey();
18480                 if(k == e.TAB){
18481                     e.stopEvent();
18482                     this.win.focus();
18483                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
18484                     this.deferFocus();
18485                 }
18486                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18487                     this.cleanUpPaste.defer(100, this);
18488                     return;
18489                 }
18490                 
18491             };
18492         }else if(Roo.isSafari){
18493             return function(e){
18494                 var k = e.getKey();
18495                 
18496                 if(k == e.TAB){
18497                     e.stopEvent();
18498                     this.execCmd('InsertText','\t');
18499                     this.deferFocus();
18500                     return;
18501                 }
18502                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18503                     this.cleanUpPaste.defer(100, this);
18504                     return;
18505                 }
18506                 
18507              };
18508         }
18509     }(),
18510     
18511     getAllAncestors: function()
18512     {
18513         var p = this.getSelectedNode();
18514         var a = [];
18515         if (!p) {
18516             a.push(p); // push blank onto stack..
18517             p = this.getParentElement();
18518         }
18519         
18520         
18521         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
18522             a.push(p);
18523             p = p.parentNode;
18524         }
18525         a.push(this.doc.body);
18526         return a;
18527     },
18528     lastSel : false,
18529     lastSelNode : false,
18530     
18531     
18532     getSelection : function() 
18533     {
18534         this.assignDocWin();
18535         return Roo.isIE ? this.doc.selection : this.win.getSelection();
18536     },
18537     
18538     getSelectedNode: function() 
18539     {
18540         // this may only work on Gecko!!!
18541         
18542         // should we cache this!!!!
18543         
18544         
18545         
18546          
18547         var range = this.createRange(this.getSelection()).cloneRange();
18548         
18549         if (Roo.isIE) {
18550             var parent = range.parentElement();
18551             while (true) {
18552                 var testRange = range.duplicate();
18553                 testRange.moveToElementText(parent);
18554                 if (testRange.inRange(range)) {
18555                     break;
18556                 }
18557                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
18558                     break;
18559                 }
18560                 parent = parent.parentElement;
18561             }
18562             return parent;
18563         }
18564         
18565         // is ancestor a text element.
18566         var ac =  range.commonAncestorContainer;
18567         if (ac.nodeType == 3) {
18568             ac = ac.parentNode;
18569         }
18570         
18571         var ar = ac.childNodes;
18572          
18573         var nodes = [];
18574         var other_nodes = [];
18575         var has_other_nodes = false;
18576         for (var i=0;i<ar.length;i++) {
18577             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
18578                 continue;
18579             }
18580             // fullly contained node.
18581             
18582             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
18583                 nodes.push(ar[i]);
18584                 continue;
18585             }
18586             
18587             // probably selected..
18588             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
18589                 other_nodes.push(ar[i]);
18590                 continue;
18591             }
18592             // outer..
18593             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
18594                 continue;
18595             }
18596             
18597             
18598             has_other_nodes = true;
18599         }
18600         if (!nodes.length && other_nodes.length) {
18601             nodes= other_nodes;
18602         }
18603         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
18604             return false;
18605         }
18606         
18607         return nodes[0];
18608     },
18609     createRange: function(sel)
18610     {
18611         // this has strange effects when using with 
18612         // top toolbar - not sure if it's a great idea.
18613         //this.editor.contentWindow.focus();
18614         if (typeof sel != "undefined") {
18615             try {
18616                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
18617             } catch(e) {
18618                 return this.doc.createRange();
18619             }
18620         } else {
18621             return this.doc.createRange();
18622         }
18623     },
18624     getParentElement: function()
18625     {
18626         
18627         this.assignDocWin();
18628         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
18629         
18630         var range = this.createRange(sel);
18631          
18632         try {
18633             var p = range.commonAncestorContainer;
18634             while (p.nodeType == 3) { // text node
18635                 p = p.parentNode;
18636             }
18637             return p;
18638         } catch (e) {
18639             return null;
18640         }
18641     
18642     },
18643     /***
18644      *
18645      * Range intersection.. the hard stuff...
18646      *  '-1' = before
18647      *  '0' = hits..
18648      *  '1' = after.
18649      *         [ -- selected range --- ]
18650      *   [fail]                        [fail]
18651      *
18652      *    basically..
18653      *      if end is before start or  hits it. fail.
18654      *      if start is after end or hits it fail.
18655      *
18656      *   if either hits (but other is outside. - then it's not 
18657      *   
18658      *    
18659      **/
18660     
18661     
18662     // @see http://www.thismuchiknow.co.uk/?p=64.
18663     rangeIntersectsNode : function(range, node)
18664     {
18665         var nodeRange = node.ownerDocument.createRange();
18666         try {
18667             nodeRange.selectNode(node);
18668         } catch (e) {
18669             nodeRange.selectNodeContents(node);
18670         }
18671     
18672         var rangeStartRange = range.cloneRange();
18673         rangeStartRange.collapse(true);
18674     
18675         var rangeEndRange = range.cloneRange();
18676         rangeEndRange.collapse(false);
18677     
18678         var nodeStartRange = nodeRange.cloneRange();
18679         nodeStartRange.collapse(true);
18680     
18681         var nodeEndRange = nodeRange.cloneRange();
18682         nodeEndRange.collapse(false);
18683     
18684         return rangeStartRange.compareBoundaryPoints(
18685                  Range.START_TO_START, nodeEndRange) == -1 &&
18686                rangeEndRange.compareBoundaryPoints(
18687                  Range.START_TO_START, nodeStartRange) == 1;
18688         
18689          
18690     },
18691     rangeCompareNode : function(range, node)
18692     {
18693         var nodeRange = node.ownerDocument.createRange();
18694         try {
18695             nodeRange.selectNode(node);
18696         } catch (e) {
18697             nodeRange.selectNodeContents(node);
18698         }
18699         
18700         
18701         range.collapse(true);
18702     
18703         nodeRange.collapse(true);
18704      
18705         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
18706         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
18707          
18708         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
18709         
18710         var nodeIsBefore   =  ss == 1;
18711         var nodeIsAfter    = ee == -1;
18712         
18713         if (nodeIsBefore && nodeIsAfter)
18714             return 0; // outer
18715         if (!nodeIsBefore && nodeIsAfter)
18716             return 1; //right trailed.
18717         
18718         if (nodeIsBefore && !nodeIsAfter)
18719             return 2;  // left trailed.
18720         // fully contined.
18721         return 3;
18722     },
18723
18724     // private? - in a new class?
18725     cleanUpPaste :  function()
18726     {
18727         // cleans up the whole document..
18728         Roo.log('cleanuppaste');
18729         
18730         this.cleanUpChildren(this.doc.body);
18731         var clean = this.cleanWordChars(this.doc.body.innerHTML);
18732         if (clean != this.doc.body.innerHTML) {
18733             this.doc.body.innerHTML = clean;
18734         }
18735         
18736     },
18737     
18738     cleanWordChars : function(input) {// change the chars to hex code
18739         var he = Roo.HtmlEditorCore;
18740         
18741         var output = input;
18742         Roo.each(he.swapCodes, function(sw) { 
18743             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
18744             
18745             output = output.replace(swapper, sw[1]);
18746         });
18747         
18748         return output;
18749     },
18750     
18751     
18752     cleanUpChildren : function (n)
18753     {
18754         if (!n.childNodes.length) {
18755             return;
18756         }
18757         for (var i = n.childNodes.length-1; i > -1 ; i--) {
18758            this.cleanUpChild(n.childNodes[i]);
18759         }
18760     },
18761     
18762     
18763         
18764     
18765     cleanUpChild : function (node)
18766     {
18767         var ed = this;
18768         //console.log(node);
18769         if (node.nodeName == "#text") {
18770             // clean up silly Windows -- stuff?
18771             return; 
18772         }
18773         if (node.nodeName == "#comment") {
18774             node.parentNode.removeChild(node);
18775             // clean up silly Windows -- stuff?
18776             return; 
18777         }
18778         var lcname = node.tagName.toLowerCase();
18779         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
18780         // whitelist of tags..
18781         
18782         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
18783             // remove node.
18784             node.parentNode.removeChild(node);
18785             return;
18786             
18787         }
18788         
18789         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
18790         
18791         // remove <a name=....> as rendering on yahoo mailer is borked with this.
18792         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
18793         
18794         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
18795         //    remove_keep_children = true;
18796         //}
18797         
18798         if (remove_keep_children) {
18799             this.cleanUpChildren(node);
18800             // inserts everything just before this node...
18801             while (node.childNodes.length) {
18802                 var cn = node.childNodes[0];
18803                 node.removeChild(cn);
18804                 node.parentNode.insertBefore(cn, node);
18805             }
18806             node.parentNode.removeChild(node);
18807             return;
18808         }
18809         
18810         if (!node.attributes || !node.attributes.length) {
18811             this.cleanUpChildren(node);
18812             return;
18813         }
18814         
18815         function cleanAttr(n,v)
18816         {
18817             
18818             if (v.match(/^\./) || v.match(/^\//)) {
18819                 return;
18820             }
18821             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
18822                 return;
18823             }
18824             if (v.match(/^#/)) {
18825                 return;
18826             }
18827 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
18828             node.removeAttribute(n);
18829             
18830         }
18831         
18832         var cwhite = this.cwhite;
18833         var cblack = this.cblack;
18834             
18835         function cleanStyle(n,v)
18836         {
18837             if (v.match(/expression/)) { //XSS?? should we even bother..
18838                 node.removeAttribute(n);
18839                 return;
18840             }
18841             
18842             var parts = v.split(/;/);
18843             var clean = [];
18844             
18845             Roo.each(parts, function(p) {
18846                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
18847                 if (!p.length) {
18848                     return true;
18849                 }
18850                 var l = p.split(':').shift().replace(/\s+/g,'');
18851                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
18852                 
18853                 if ( cwhite.length && cblack.indexOf(l) > -1) {
18854 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18855                     //node.removeAttribute(n);
18856                     return true;
18857                 }
18858                 //Roo.log()
18859                 // only allow 'c whitelisted system attributes'
18860                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
18861 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18862                     //node.removeAttribute(n);
18863                     return true;
18864                 }
18865                 
18866                 
18867                  
18868                 
18869                 clean.push(p);
18870                 return true;
18871             });
18872             if (clean.length) { 
18873                 node.setAttribute(n, clean.join(';'));
18874             } else {
18875                 node.removeAttribute(n);
18876             }
18877             
18878         }
18879         
18880         
18881         for (var i = node.attributes.length-1; i > -1 ; i--) {
18882             var a = node.attributes[i];
18883             //console.log(a);
18884             
18885             if (a.name.toLowerCase().substr(0,2)=='on')  {
18886                 node.removeAttribute(a.name);
18887                 continue;
18888             }
18889             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
18890                 node.removeAttribute(a.name);
18891                 continue;
18892             }
18893             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
18894                 cleanAttr(a.name,a.value); // fixme..
18895                 continue;
18896             }
18897             if (a.name == 'style') {
18898                 cleanStyle(a.name,a.value);
18899                 continue;
18900             }
18901             /// clean up MS crap..
18902             // tecnically this should be a list of valid class'es..
18903             
18904             
18905             if (a.name == 'class') {
18906                 if (a.value.match(/^Mso/)) {
18907                     node.className = '';
18908                 }
18909                 
18910                 if (a.value.match(/body/)) {
18911                     node.className = '';
18912                 }
18913                 continue;
18914             }
18915             
18916             // style cleanup!?
18917             // class cleanup?
18918             
18919         }
18920         
18921         
18922         this.cleanUpChildren(node);
18923         
18924         
18925     },
18926     /**
18927      * Clean up MS wordisms...
18928      */
18929     cleanWord : function(node)
18930     {
18931         var _t = this;
18932         var cleanWordChildren = function()
18933         {
18934             if (!node.childNodes.length) {
18935                 return;
18936             }
18937             for (var i = node.childNodes.length-1; i > -1 ; i--) {
18938                _t.cleanWord(node.childNodes[i]);
18939             }
18940         }
18941         
18942         
18943         if (!node) {
18944             this.cleanWord(this.doc.body);
18945             return;
18946         }
18947         if (node.nodeName == "#text") {
18948             // clean up silly Windows -- stuff?
18949             return; 
18950         }
18951         if (node.nodeName == "#comment") {
18952             node.parentNode.removeChild(node);
18953             // clean up silly Windows -- stuff?
18954             return; 
18955         }
18956         
18957         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
18958             node.parentNode.removeChild(node);
18959             return;
18960         }
18961         
18962         // remove - but keep children..
18963         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
18964             while (node.childNodes.length) {
18965                 var cn = node.childNodes[0];
18966                 node.removeChild(cn);
18967                 node.parentNode.insertBefore(cn, node);
18968             }
18969             node.parentNode.removeChild(node);
18970             cleanWordChildren();
18971             return;
18972         }
18973         // clean styles
18974         if (node.className.length) {
18975             
18976             var cn = node.className.split(/\W+/);
18977             var cna = [];
18978             Roo.each(cn, function(cls) {
18979                 if (cls.match(/Mso[a-zA-Z]+/)) {
18980                     return;
18981                 }
18982                 cna.push(cls);
18983             });
18984             node.className = cna.length ? cna.join(' ') : '';
18985             if (!cna.length) {
18986                 node.removeAttribute("class");
18987             }
18988         }
18989         
18990         if (node.hasAttribute("lang")) {
18991             node.removeAttribute("lang");
18992         }
18993         
18994         if (node.hasAttribute("style")) {
18995             
18996             var styles = node.getAttribute("style").split(";");
18997             var nstyle = [];
18998             Roo.each(styles, function(s) {
18999                 if (!s.match(/:/)) {
19000                     return;
19001                 }
19002                 var kv = s.split(":");
19003                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19004                     return;
19005                 }
19006                 // what ever is left... we allow.
19007                 nstyle.push(s);
19008             });
19009             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19010             if (!nstyle.length) {
19011                 node.removeAttribute('style');
19012             }
19013         }
19014         
19015         cleanWordChildren();
19016         
19017         
19018     },
19019     domToHTML : function(currentElement, depth, nopadtext) {
19020         
19021         depth = depth || 0;
19022         nopadtext = nopadtext || false;
19023     
19024         if (!currentElement) {
19025             return this.domToHTML(this.doc.body);
19026         }
19027         
19028         //Roo.log(currentElement);
19029         var j;
19030         var allText = false;
19031         var nodeName = currentElement.nodeName;
19032         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
19033         
19034         if  (nodeName == '#text') {
19035             
19036             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
19037         }
19038         
19039         
19040         var ret = '';
19041         if (nodeName != 'BODY') {
19042              
19043             var i = 0;
19044             // Prints the node tagName, such as <A>, <IMG>, etc
19045             if (tagName) {
19046                 var attr = [];
19047                 for(i = 0; i < currentElement.attributes.length;i++) {
19048                     // quoting?
19049                     var aname = currentElement.attributes.item(i).name;
19050                     if (!currentElement.attributes.item(i).value.length) {
19051                         continue;
19052                     }
19053                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
19054                 }
19055                 
19056                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
19057             } 
19058             else {
19059                 
19060                 // eack
19061             }
19062         } else {
19063             tagName = false;
19064         }
19065         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
19066             return ret;
19067         }
19068         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
19069             nopadtext = true;
19070         }
19071         
19072         
19073         // Traverse the tree
19074         i = 0;
19075         var currentElementChild = currentElement.childNodes.item(i);
19076         var allText = true;
19077         var innerHTML  = '';
19078         lastnode = '';
19079         while (currentElementChild) {
19080             // Formatting code (indent the tree so it looks nice on the screen)
19081             var nopad = nopadtext;
19082             if (lastnode == 'SPAN') {
19083                 nopad  = true;
19084             }
19085             // text
19086             if  (currentElementChild.nodeName == '#text') {
19087                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
19088                 toadd = nopadtext ? toadd : toadd.trim();
19089                 if (!nopad && toadd.length > 80) {
19090                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
19091                 }
19092                 innerHTML  += toadd;
19093                 
19094                 i++;
19095                 currentElementChild = currentElement.childNodes.item(i);
19096                 lastNode = '';
19097                 continue;
19098             }
19099             allText = false;
19100             
19101             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
19102                 
19103             // Recursively traverse the tree structure of the child node
19104             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
19105             lastnode = currentElementChild.nodeName;
19106             i++;
19107             currentElementChild=currentElement.childNodes.item(i);
19108         }
19109         
19110         ret += innerHTML;
19111         
19112         if (!allText) {
19113                 // The remaining code is mostly for formatting the tree
19114             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
19115         }
19116         
19117         
19118         if (tagName) {
19119             ret+= "</"+tagName+">";
19120         }
19121         return ret;
19122         
19123     },
19124         
19125     applyBlacklists : function()
19126     {
19127         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
19128         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
19129         
19130         this.white = [];
19131         this.black = [];
19132         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
19133             if (b.indexOf(tag) > -1) {
19134                 return;
19135             }
19136             this.white.push(tag);
19137             
19138         }, this);
19139         
19140         Roo.each(w, function(tag) {
19141             if (b.indexOf(tag) > -1) {
19142                 return;
19143             }
19144             if (this.white.indexOf(tag) > -1) {
19145                 return;
19146             }
19147             this.white.push(tag);
19148             
19149         }, this);
19150         
19151         
19152         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
19153             if (w.indexOf(tag) > -1) {
19154                 return;
19155             }
19156             this.black.push(tag);
19157             
19158         }, this);
19159         
19160         Roo.each(b, function(tag) {
19161             if (w.indexOf(tag) > -1) {
19162                 return;
19163             }
19164             if (this.black.indexOf(tag) > -1) {
19165                 return;
19166             }
19167             this.black.push(tag);
19168             
19169         }, this);
19170         
19171         
19172         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
19173         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
19174         
19175         this.cwhite = [];
19176         this.cblack = [];
19177         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
19178             if (b.indexOf(tag) > -1) {
19179                 return;
19180             }
19181             this.cwhite.push(tag);
19182             
19183         }, this);
19184         
19185         Roo.each(w, function(tag) {
19186             if (b.indexOf(tag) > -1) {
19187                 return;
19188             }
19189             if (this.cwhite.indexOf(tag) > -1) {
19190                 return;
19191             }
19192             this.cwhite.push(tag);
19193             
19194         }, this);
19195         
19196         
19197         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
19198             if (w.indexOf(tag) > -1) {
19199                 return;
19200             }
19201             this.cblack.push(tag);
19202             
19203         }, this);
19204         
19205         Roo.each(b, function(tag) {
19206             if (w.indexOf(tag) > -1) {
19207                 return;
19208             }
19209             if (this.cblack.indexOf(tag) > -1) {
19210                 return;
19211             }
19212             this.cblack.push(tag);
19213             
19214         }, this);
19215     },
19216     
19217     setStylesheets : function(stylesheets)
19218     {
19219         if(typeof(stylesheets) == 'string'){
19220             Roo.get(this.iframe.contentDocument.head).createChild({
19221                 tag : 'link',
19222                 rel : 'stylesheet',
19223                 type : 'text/css',
19224                 href : stylesheets
19225             });
19226             
19227             return;
19228         }
19229         var _this = this;
19230      
19231         Roo.each(stylesheets, function(s) {
19232             if(!s.length){
19233                 return;
19234             }
19235             
19236             Roo.get(_this.iframe.contentDocument.head).createChild({
19237                 tag : 'link',
19238                 rel : 'stylesheet',
19239                 type : 'text/css',
19240                 href : s
19241             });
19242         });
19243
19244         
19245     },
19246     
19247     removeStylesheets : function()
19248     {
19249         var _this = this;
19250         
19251         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
19252             s.remove();
19253         });
19254     }
19255     
19256     // hide stuff that is not compatible
19257     /**
19258      * @event blur
19259      * @hide
19260      */
19261     /**
19262      * @event change
19263      * @hide
19264      */
19265     /**
19266      * @event focus
19267      * @hide
19268      */
19269     /**
19270      * @event specialkey
19271      * @hide
19272      */
19273     /**
19274      * @cfg {String} fieldClass @hide
19275      */
19276     /**
19277      * @cfg {String} focusClass @hide
19278      */
19279     /**
19280      * @cfg {String} autoCreate @hide
19281      */
19282     /**
19283      * @cfg {String} inputType @hide
19284      */
19285     /**
19286      * @cfg {String} invalidClass @hide
19287      */
19288     /**
19289      * @cfg {String} invalidText @hide
19290      */
19291     /**
19292      * @cfg {String} msgFx @hide
19293      */
19294     /**
19295      * @cfg {String} validateOnBlur @hide
19296      */
19297 });
19298
19299 Roo.HtmlEditorCore.white = [
19300         'area', 'br', 'img', 'input', 'hr', 'wbr',
19301         
19302        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
19303        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
19304        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
19305        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
19306        'table',   'ul',         'xmp', 
19307        
19308        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
19309       'thead',   'tr', 
19310      
19311       'dir', 'menu', 'ol', 'ul', 'dl',
19312        
19313       'embed',  'object'
19314 ];
19315
19316
19317 Roo.HtmlEditorCore.black = [
19318     //    'embed',  'object', // enable - backend responsiblity to clean thiese
19319         'applet', // 
19320         'base',   'basefont', 'bgsound', 'blink',  'body', 
19321         'frame',  'frameset', 'head',    'html',   'ilayer', 
19322         'iframe', 'layer',  'link',     'meta',    'object',   
19323         'script', 'style' ,'title',  'xml' // clean later..
19324 ];
19325 Roo.HtmlEditorCore.clean = [
19326     'script', 'style', 'title', 'xml'
19327 ];
19328 Roo.HtmlEditorCore.remove = [
19329     'font'
19330 ];
19331 // attributes..
19332
19333 Roo.HtmlEditorCore.ablack = [
19334     'on'
19335 ];
19336     
19337 Roo.HtmlEditorCore.aclean = [ 
19338     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
19339 ];
19340
19341 // protocols..
19342 Roo.HtmlEditorCore.pwhite= [
19343         'http',  'https',  'mailto'
19344 ];
19345
19346 // white listed style attributes.
19347 Roo.HtmlEditorCore.cwhite= [
19348       //  'text-align', /// default is to allow most things..
19349       
19350          
19351 //        'font-size'//??
19352 ];
19353
19354 // black listed style attributes.
19355 Roo.HtmlEditorCore.cblack= [
19356       //  'font-size' -- this can be set by the project 
19357 ];
19358
19359
19360 Roo.HtmlEditorCore.swapCodes   =[ 
19361     [    8211, "--" ], 
19362     [    8212, "--" ], 
19363     [    8216,  "'" ],  
19364     [    8217, "'" ],  
19365     [    8220, '"' ],  
19366     [    8221, '"' ],  
19367     [    8226, "*" ],  
19368     [    8230, "..." ]
19369 ]; 
19370
19371     /*
19372  * - LGPL
19373  *
19374  * HtmlEditor
19375  * 
19376  */
19377
19378 /**
19379  * @class Roo.bootstrap.HtmlEditor
19380  * @extends Roo.bootstrap.TextArea
19381  * Bootstrap HtmlEditor class
19382
19383  * @constructor
19384  * Create a new HtmlEditor
19385  * @param {Object} config The config object
19386  */
19387
19388 Roo.bootstrap.HtmlEditor = function(config){
19389     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
19390     if (!this.toolbars) {
19391         this.toolbars = [];
19392     }
19393     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
19394     this.addEvents({
19395             /**
19396              * @event initialize
19397              * Fires when the editor is fully initialized (including the iframe)
19398              * @param {HtmlEditor} this
19399              */
19400             initialize: true,
19401             /**
19402              * @event activate
19403              * Fires when the editor is first receives the focus. Any insertion must wait
19404              * until after this event.
19405              * @param {HtmlEditor} this
19406              */
19407             activate: true,
19408              /**
19409              * @event beforesync
19410              * Fires before the textarea is updated with content from the editor iframe. Return false
19411              * to cancel the sync.
19412              * @param {HtmlEditor} this
19413              * @param {String} html
19414              */
19415             beforesync: true,
19416              /**
19417              * @event beforepush
19418              * Fires before the iframe editor is updated with content from the textarea. Return false
19419              * to cancel the push.
19420              * @param {HtmlEditor} this
19421              * @param {String} html
19422              */
19423             beforepush: true,
19424              /**
19425              * @event sync
19426              * Fires when the textarea is updated with content from the editor iframe.
19427              * @param {HtmlEditor} this
19428              * @param {String} html
19429              */
19430             sync: true,
19431              /**
19432              * @event push
19433              * Fires when the iframe editor is updated with content from the textarea.
19434              * @param {HtmlEditor} this
19435              * @param {String} html
19436              */
19437             push: true,
19438              /**
19439              * @event editmodechange
19440              * Fires when the editor switches edit modes
19441              * @param {HtmlEditor} this
19442              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
19443              */
19444             editmodechange: true,
19445             /**
19446              * @event editorevent
19447              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19448              * @param {HtmlEditor} this
19449              */
19450             editorevent: true,
19451             /**
19452              * @event firstfocus
19453              * Fires when on first focus - needed by toolbars..
19454              * @param {HtmlEditor} this
19455              */
19456             firstfocus: true,
19457             /**
19458              * @event autosave
19459              * Auto save the htmlEditor value as a file into Events
19460              * @param {HtmlEditor} this
19461              */
19462             autosave: true,
19463             /**
19464              * @event savedpreview
19465              * preview the saved version of htmlEditor
19466              * @param {HtmlEditor} this
19467              */
19468             savedpreview: true
19469         });
19470 };
19471
19472
19473 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
19474     
19475     
19476       /**
19477      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
19478      */
19479     toolbars : false,
19480    
19481      /**
19482      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19483      *                        Roo.resizable.
19484      */
19485     resizable : false,
19486      /**
19487      * @cfg {Number} height (in pixels)
19488      */   
19489     height: 300,
19490    /**
19491      * @cfg {Number} width (in pixels)
19492      */   
19493     width: false,
19494     
19495     /**
19496      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19497      * 
19498      */
19499     stylesheets: false,
19500     
19501     // id of frame..
19502     frameId: false,
19503     
19504     // private properties
19505     validationEvent : false,
19506     deferHeight: true,
19507     initialized : false,
19508     activated : false,
19509     
19510     onFocus : Roo.emptyFn,
19511     iframePad:3,
19512     hideMode:'offsets',
19513     
19514     
19515     tbContainer : false,
19516     
19517     toolbarContainer :function() {
19518         return this.wrap.select('.x-html-editor-tb',true).first();
19519     },
19520
19521     /**
19522      * Protected method that will not generally be called directly. It
19523      * is called when the editor creates its toolbar. Override this method if you need to
19524      * add custom toolbar buttons.
19525      * @param {HtmlEditor} editor
19526      */
19527     createToolbar : function(){
19528         
19529         Roo.log("create toolbars");
19530         
19531         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
19532         this.toolbars[0].render(this.toolbarContainer());
19533         
19534         return;
19535         
19536 //        if (!editor.toolbars || !editor.toolbars.length) {
19537 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
19538 //        }
19539 //        
19540 //        for (var i =0 ; i < editor.toolbars.length;i++) {
19541 //            editor.toolbars[i] = Roo.factory(
19542 //                    typeof(editor.toolbars[i]) == 'string' ?
19543 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
19544 //                Roo.bootstrap.HtmlEditor);
19545 //            editor.toolbars[i].init(editor);
19546 //        }
19547     },
19548
19549      
19550     // private
19551     onRender : function(ct, position)
19552     {
19553        // Roo.log("Call onRender: " + this.xtype);
19554         var _t = this;
19555         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
19556       
19557         this.wrap = this.inputEl().wrap({
19558             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
19559         });
19560         
19561         this.editorcore.onRender(ct, position);
19562          
19563         if (this.resizable) {
19564             this.resizeEl = new Roo.Resizable(this.wrap, {
19565                 pinned : true,
19566                 wrap: true,
19567                 dynamic : true,
19568                 minHeight : this.height,
19569                 height: this.height,
19570                 handles : this.resizable,
19571                 width: this.width,
19572                 listeners : {
19573                     resize : function(r, w, h) {
19574                         _t.onResize(w,h); // -something
19575                     }
19576                 }
19577             });
19578             
19579         }
19580         this.createToolbar(this);
19581        
19582         
19583         if(!this.width && this.resizable){
19584             this.setSize(this.wrap.getSize());
19585         }
19586         if (this.resizeEl) {
19587             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
19588             // should trigger onReize..
19589         }
19590         
19591     },
19592
19593     // private
19594     onResize : function(w, h)
19595     {
19596         Roo.log('resize: ' +w + ',' + h );
19597         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
19598         var ew = false;
19599         var eh = false;
19600         
19601         if(this.inputEl() ){
19602             if(typeof w == 'number'){
19603                 var aw = w - this.wrap.getFrameWidth('lr');
19604                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
19605                 ew = aw;
19606             }
19607             if(typeof h == 'number'){
19608                  var tbh = -11;  // fixme it needs to tool bar size!
19609                 for (var i =0; i < this.toolbars.length;i++) {
19610                     // fixme - ask toolbars for heights?
19611                     tbh += this.toolbars[i].el.getHeight();
19612                     //if (this.toolbars[i].footer) {
19613                     //    tbh += this.toolbars[i].footer.el.getHeight();
19614                     //}
19615                 }
19616               
19617                 
19618                 
19619                 
19620                 
19621                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
19622                 ah -= 5; // knock a few pixes off for look..
19623                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
19624                 var eh = ah;
19625             }
19626         }
19627         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
19628         this.editorcore.onResize(ew,eh);
19629         
19630     },
19631
19632     /**
19633      * Toggles the editor between standard and source edit mode.
19634      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19635      */
19636     toggleSourceEdit : function(sourceEditMode)
19637     {
19638         this.editorcore.toggleSourceEdit(sourceEditMode);
19639         
19640         if(this.editorcore.sourceEditMode){
19641             Roo.log('editor - showing textarea');
19642             
19643 //            Roo.log('in');
19644 //            Roo.log(this.syncValue());
19645             this.syncValue();
19646             this.inputEl().removeClass(['hide', 'x-hidden']);
19647             this.inputEl().dom.removeAttribute('tabIndex');
19648             this.inputEl().focus();
19649         }else{
19650             Roo.log('editor - hiding textarea');
19651 //            Roo.log('out')
19652 //            Roo.log(this.pushValue()); 
19653             this.pushValue();
19654             
19655             this.inputEl().addClass(['hide', 'x-hidden']);
19656             this.inputEl().dom.setAttribute('tabIndex', -1);
19657             //this.deferFocus();
19658         }
19659          
19660         if(this.resizable){
19661             this.setSize(this.wrap.getSize());
19662         }
19663         
19664         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
19665     },
19666  
19667     // private (for BoxComponent)
19668     adjustSize : Roo.BoxComponent.prototype.adjustSize,
19669
19670     // private (for BoxComponent)
19671     getResizeEl : function(){
19672         return this.wrap;
19673     },
19674
19675     // private (for BoxComponent)
19676     getPositionEl : function(){
19677         return this.wrap;
19678     },
19679
19680     // private
19681     initEvents : function(){
19682         this.originalValue = this.getValue();
19683     },
19684
19685 //    /**
19686 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19687 //     * @method
19688 //     */
19689 //    markInvalid : Roo.emptyFn,
19690 //    /**
19691 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19692 //     * @method
19693 //     */
19694 //    clearInvalid : Roo.emptyFn,
19695
19696     setValue : function(v){
19697         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
19698         this.editorcore.pushValue();
19699     },
19700
19701      
19702     // private
19703     deferFocus : function(){
19704         this.focus.defer(10, this);
19705     },
19706
19707     // doc'ed in Field
19708     focus : function(){
19709         this.editorcore.focus();
19710         
19711     },
19712       
19713
19714     // private
19715     onDestroy : function(){
19716         
19717         
19718         
19719         if(this.rendered){
19720             
19721             for (var i =0; i < this.toolbars.length;i++) {
19722                 // fixme - ask toolbars for heights?
19723                 this.toolbars[i].onDestroy();
19724             }
19725             
19726             this.wrap.dom.innerHTML = '';
19727             this.wrap.remove();
19728         }
19729     },
19730
19731     // private
19732     onFirstFocus : function(){
19733         //Roo.log("onFirstFocus");
19734         this.editorcore.onFirstFocus();
19735          for (var i =0; i < this.toolbars.length;i++) {
19736             this.toolbars[i].onFirstFocus();
19737         }
19738         
19739     },
19740     
19741     // private
19742     syncValue : function()
19743     {   
19744         this.editorcore.syncValue();
19745     },
19746     
19747     pushValue : function()
19748     {   
19749         this.editorcore.pushValue();
19750     }
19751      
19752     
19753     // hide stuff that is not compatible
19754     /**
19755      * @event blur
19756      * @hide
19757      */
19758     /**
19759      * @event change
19760      * @hide
19761      */
19762     /**
19763      * @event focus
19764      * @hide
19765      */
19766     /**
19767      * @event specialkey
19768      * @hide
19769      */
19770     /**
19771      * @cfg {String} fieldClass @hide
19772      */
19773     /**
19774      * @cfg {String} focusClass @hide
19775      */
19776     /**
19777      * @cfg {String} autoCreate @hide
19778      */
19779     /**
19780      * @cfg {String} inputType @hide
19781      */
19782     /**
19783      * @cfg {String} invalidClass @hide
19784      */
19785     /**
19786      * @cfg {String} invalidText @hide
19787      */
19788     /**
19789      * @cfg {String} msgFx @hide
19790      */
19791     /**
19792      * @cfg {String} validateOnBlur @hide
19793      */
19794 });
19795  
19796     
19797    
19798    
19799    
19800       
19801 Roo.namespace('Roo.bootstrap.htmleditor');
19802 /**
19803  * @class Roo.bootstrap.HtmlEditorToolbar1
19804  * Basic Toolbar
19805  * 
19806  * Usage:
19807  *
19808  new Roo.bootstrap.HtmlEditor({
19809     ....
19810     toolbars : [
19811         new Roo.bootstrap.HtmlEditorToolbar1({
19812             disable : { fonts: 1 , format: 1, ..., ... , ...],
19813             btns : [ .... ]
19814         })
19815     }
19816      
19817  * 
19818  * @cfg {Object} disable List of elements to disable..
19819  * @cfg {Array} btns List of additional buttons.
19820  * 
19821  * 
19822  * NEEDS Extra CSS? 
19823  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
19824  */
19825  
19826 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
19827 {
19828     
19829     Roo.apply(this, config);
19830     
19831     // default disabled, based on 'good practice'..
19832     this.disable = this.disable || {};
19833     Roo.applyIf(this.disable, {
19834         fontSize : true,
19835         colors : true,
19836         specialElements : true
19837     });
19838     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
19839     
19840     this.editor = config.editor;
19841     this.editorcore = config.editor.editorcore;
19842     
19843     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
19844     
19845     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
19846     // dont call parent... till later.
19847 }
19848 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
19849      
19850     bar : true,
19851     
19852     editor : false,
19853     editorcore : false,
19854     
19855     
19856     formats : [
19857         "p" ,  
19858         "h1","h2","h3","h4","h5","h6", 
19859         "pre", "code", 
19860         "abbr", "acronym", "address", "cite", "samp", "var",
19861         'div','span'
19862     ],
19863     
19864     onRender : function(ct, position)
19865     {
19866        // Roo.log("Call onRender: " + this.xtype);
19867         
19868        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
19869        Roo.log(this.el);
19870        this.el.dom.style.marginBottom = '0';
19871        var _this = this;
19872        var editorcore = this.editorcore;
19873        var editor= this.editor;
19874        
19875        var children = [];
19876        var btn = function(id,cmd , toggle, handler){
19877        
19878             var  event = toggle ? 'toggle' : 'click';
19879        
19880             var a = {
19881                 size : 'sm',
19882                 xtype: 'Button',
19883                 xns: Roo.bootstrap,
19884                 glyphicon : id,
19885                 cmd : id || cmd,
19886                 enableToggle:toggle !== false,
19887                 //html : 'submit'
19888                 pressed : toggle ? false : null,
19889                 listeners : {}
19890             }
19891             a.listeners[toggle ? 'toggle' : 'click'] = function() {
19892                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
19893             }
19894             children.push(a);
19895             return a;
19896        }
19897         
19898         var style = {
19899                 xtype: 'Button',
19900                 size : 'sm',
19901                 xns: Roo.bootstrap,
19902                 glyphicon : 'font',
19903                 //html : 'submit'
19904                 menu : {
19905                     xtype: 'Menu',
19906                     xns: Roo.bootstrap,
19907                     items:  []
19908                 }
19909         };
19910         Roo.each(this.formats, function(f) {
19911             style.menu.items.push({
19912                 xtype :'MenuItem',
19913                 xns: Roo.bootstrap,
19914                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
19915                 tagname : f,
19916                 listeners : {
19917                     click : function()
19918                     {
19919                         editorcore.insertTag(this.tagname);
19920                         editor.focus();
19921                     }
19922                 }
19923                 
19924             });
19925         });
19926          children.push(style);   
19927             
19928             
19929         btn('bold',false,true);
19930         btn('italic',false,true);
19931         btn('align-left', 'justifyleft',true);
19932         btn('align-center', 'justifycenter',true);
19933         btn('align-right' , 'justifyright',true);
19934         btn('link', false, false, function(btn) {
19935             //Roo.log("create link?");
19936             var url = prompt(this.createLinkText, this.defaultLinkValue);
19937             if(url && url != 'http:/'+'/'){
19938                 this.editorcore.relayCmd('createlink', url);
19939             }
19940         }),
19941         btn('list','insertunorderedlist',true);
19942         btn('pencil', false,true, function(btn){
19943                 Roo.log(this);
19944                 
19945                 this.toggleSourceEdit(btn.pressed);
19946         });
19947         /*
19948         var cog = {
19949                 xtype: 'Button',
19950                 size : 'sm',
19951                 xns: Roo.bootstrap,
19952                 glyphicon : 'cog',
19953                 //html : 'submit'
19954                 menu : {
19955                     xtype: 'Menu',
19956                     xns: Roo.bootstrap,
19957                     items:  []
19958                 }
19959         };
19960         
19961         cog.menu.items.push({
19962             xtype :'MenuItem',
19963             xns: Roo.bootstrap,
19964             html : Clean styles,
19965             tagname : f,
19966             listeners : {
19967                 click : function()
19968                 {
19969                     editorcore.insertTag(this.tagname);
19970                     editor.focus();
19971                 }
19972             }
19973             
19974         });
19975        */
19976         
19977          
19978        this.xtype = 'NavSimplebar';
19979         
19980         for(var i=0;i< children.length;i++) {
19981             
19982             this.buttons.add(this.addxtypeChild(children[i]));
19983             
19984         }
19985         
19986         editor.on('editorevent', this.updateToolbar, this);
19987     },
19988     onBtnClick : function(id)
19989     {
19990        this.editorcore.relayCmd(id);
19991        this.editorcore.focus();
19992     },
19993     
19994     /**
19995      * Protected method that will not generally be called directly. It triggers
19996      * a toolbar update by reading the markup state of the current selection in the editor.
19997      */
19998     updateToolbar: function(){
19999
20000         if(!this.editorcore.activated){
20001             this.editor.onFirstFocus(); // is this neeed?
20002             return;
20003         }
20004
20005         var btns = this.buttons; 
20006         var doc = this.editorcore.doc;
20007         btns.get('bold').setActive(doc.queryCommandState('bold'));
20008         btns.get('italic').setActive(doc.queryCommandState('italic'));
20009         //btns.get('underline').setActive(doc.queryCommandState('underline'));
20010         
20011         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
20012         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
20013         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
20014         
20015         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
20016         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
20017          /*
20018         
20019         var ans = this.editorcore.getAllAncestors();
20020         if (this.formatCombo) {
20021             
20022             
20023             var store = this.formatCombo.store;
20024             this.formatCombo.setValue("");
20025             for (var i =0; i < ans.length;i++) {
20026                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
20027                     // select it..
20028                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
20029                     break;
20030                 }
20031             }
20032         }
20033         
20034         
20035         
20036         // hides menus... - so this cant be on a menu...
20037         Roo.bootstrap.MenuMgr.hideAll();
20038         */
20039         Roo.bootstrap.MenuMgr.hideAll();
20040         //this.editorsyncValue();
20041     },
20042     onFirstFocus: function() {
20043         this.buttons.each(function(item){
20044            item.enable();
20045         });
20046     },
20047     toggleSourceEdit : function(sourceEditMode){
20048         
20049           
20050         if(sourceEditMode){
20051             Roo.log("disabling buttons");
20052            this.buttons.each( function(item){
20053                 if(item.cmd != 'pencil'){
20054                     item.disable();
20055                 }
20056             });
20057           
20058         }else{
20059             Roo.log("enabling buttons");
20060             if(this.editorcore.initialized){
20061                 this.buttons.each( function(item){
20062                     item.enable();
20063                 });
20064             }
20065             
20066         }
20067         Roo.log("calling toggole on editor");
20068         // tell the editor that it's been pressed..
20069         this.editor.toggleSourceEdit(sourceEditMode);
20070        
20071     }
20072 });
20073
20074
20075
20076
20077
20078 /**
20079  * @class Roo.bootstrap.Table.AbstractSelectionModel
20080  * @extends Roo.util.Observable
20081  * Abstract base class for grid SelectionModels.  It provides the interface that should be
20082  * implemented by descendant classes.  This class should not be directly instantiated.
20083  * @constructor
20084  */
20085 Roo.bootstrap.Table.AbstractSelectionModel = function(){
20086     this.locked = false;
20087     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
20088 };
20089
20090
20091 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
20092     /** @ignore Called by the grid automatically. Do not call directly. */
20093     init : function(grid){
20094         this.grid = grid;
20095         this.initEvents();
20096     },
20097
20098     /**
20099      * Locks the selections.
20100      */
20101     lock : function(){
20102         this.locked = true;
20103     },
20104
20105     /**
20106      * Unlocks the selections.
20107      */
20108     unlock : function(){
20109         this.locked = false;
20110     },
20111
20112     /**
20113      * Returns true if the selections are locked.
20114      * @return {Boolean}
20115      */
20116     isLocked : function(){
20117         return this.locked;
20118     }
20119 });
20120 /**
20121  * @extends Roo.bootstrap.Table.AbstractSelectionModel
20122  * @class Roo.bootstrap.Table.RowSelectionModel
20123  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
20124  * It supports multiple selections and keyboard selection/navigation. 
20125  * @constructor
20126  * @param {Object} config
20127  */
20128
20129 Roo.bootstrap.Table.RowSelectionModel = function(config){
20130     Roo.apply(this, config);
20131     this.selections = new Roo.util.MixedCollection(false, function(o){
20132         return o.id;
20133     });
20134
20135     this.last = false;
20136     this.lastActive = false;
20137
20138     this.addEvents({
20139         /**
20140              * @event selectionchange
20141              * Fires when the selection changes
20142              * @param {SelectionModel} this
20143              */
20144             "selectionchange" : true,
20145         /**
20146              * @event afterselectionchange
20147              * Fires after the selection changes (eg. by key press or clicking)
20148              * @param {SelectionModel} this
20149              */
20150             "afterselectionchange" : true,
20151         /**
20152              * @event beforerowselect
20153              * Fires when a row is selected being selected, return false to cancel.
20154              * @param {SelectionModel} this
20155              * @param {Number} rowIndex The selected index
20156              * @param {Boolean} keepExisting False if other selections will be cleared
20157              */
20158             "beforerowselect" : true,
20159         /**
20160              * @event rowselect
20161              * Fires when a row is selected.
20162              * @param {SelectionModel} this
20163              * @param {Number} rowIndex The selected index
20164              * @param {Roo.data.Record} r The record
20165              */
20166             "rowselect" : true,
20167         /**
20168              * @event rowdeselect
20169              * Fires when a row is deselected.
20170              * @param {SelectionModel} this
20171              * @param {Number} rowIndex The selected index
20172              */
20173         "rowdeselect" : true
20174     });
20175     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
20176     this.locked = false;
20177 };
20178
20179 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
20180     /**
20181      * @cfg {Boolean} singleSelect
20182      * True to allow selection of only one row at a time (defaults to false)
20183      */
20184     singleSelect : false,
20185
20186     // private
20187     initEvents : function(){
20188
20189         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
20190             this.grid.on("mousedown", this.handleMouseDown, this);
20191         }else{ // allow click to work like normal
20192             this.grid.on("rowclick", this.handleDragableRowClick, this);
20193         }
20194
20195         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
20196             "up" : function(e){
20197                 if(!e.shiftKey){
20198                     this.selectPrevious(e.shiftKey);
20199                 }else if(this.last !== false && this.lastActive !== false){
20200                     var last = this.last;
20201                     this.selectRange(this.last,  this.lastActive-1);
20202                     this.grid.getView().focusRow(this.lastActive);
20203                     if(last !== false){
20204                         this.last = last;
20205                     }
20206                 }else{
20207                     this.selectFirstRow();
20208                 }
20209                 this.fireEvent("afterselectionchange", this);
20210             },
20211             "down" : function(e){
20212                 if(!e.shiftKey){
20213                     this.selectNext(e.shiftKey);
20214                 }else if(this.last !== false && this.lastActive !== false){
20215                     var last = this.last;
20216                     this.selectRange(this.last,  this.lastActive+1);
20217                     this.grid.getView().focusRow(this.lastActive);
20218                     if(last !== false){
20219                         this.last = last;
20220                     }
20221                 }else{
20222                     this.selectFirstRow();
20223                 }
20224                 this.fireEvent("afterselectionchange", this);
20225             },
20226             scope: this
20227         });
20228
20229         var view = this.grid.view;
20230         view.on("refresh", this.onRefresh, this);
20231         view.on("rowupdated", this.onRowUpdated, this);
20232         view.on("rowremoved", this.onRemove, this);
20233     },
20234
20235     // private
20236     onRefresh : function(){
20237         var ds = this.grid.dataSource, i, v = this.grid.view;
20238         var s = this.selections;
20239         s.each(function(r){
20240             if((i = ds.indexOfId(r.id)) != -1){
20241                 v.onRowSelect(i);
20242             }else{
20243                 s.remove(r);
20244             }
20245         });
20246     },
20247
20248     // private
20249     onRemove : function(v, index, r){
20250         this.selections.remove(r);
20251     },
20252
20253     // private
20254     onRowUpdated : function(v, index, r){
20255         if(this.isSelected(r)){
20256             v.onRowSelect(index);
20257         }
20258     },
20259
20260     /**
20261      * Select records.
20262      * @param {Array} records The records to select
20263      * @param {Boolean} keepExisting (optional) True to keep existing selections
20264      */
20265     selectRecords : function(records, keepExisting){
20266         if(!keepExisting){
20267             this.clearSelections();
20268         }
20269         var ds = this.grid.dataSource;
20270         for(var i = 0, len = records.length; i < len; i++){
20271             this.selectRow(ds.indexOf(records[i]), true);
20272         }
20273     },
20274
20275     /**
20276      * Gets the number of selected rows.
20277      * @return {Number}
20278      */
20279     getCount : function(){
20280         return this.selections.length;
20281     },
20282
20283     /**
20284      * Selects the first row in the grid.
20285      */
20286     selectFirstRow : function(){
20287         this.selectRow(0);
20288     },
20289
20290     /**
20291      * Select the last row.
20292      * @param {Boolean} keepExisting (optional) True to keep existing selections
20293      */
20294     selectLastRow : function(keepExisting){
20295         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
20296     },
20297
20298     /**
20299      * Selects the row immediately following the last selected row.
20300      * @param {Boolean} keepExisting (optional) True to keep existing selections
20301      */
20302     selectNext : function(keepExisting){
20303         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
20304             this.selectRow(this.last+1, keepExisting);
20305             this.grid.getView().focusRow(this.last);
20306         }
20307     },
20308
20309     /**
20310      * Selects the row that precedes the last selected row.
20311      * @param {Boolean} keepExisting (optional) True to keep existing selections
20312      */
20313     selectPrevious : function(keepExisting){
20314         if(this.last){
20315             this.selectRow(this.last-1, keepExisting);
20316             this.grid.getView().focusRow(this.last);
20317         }
20318     },
20319
20320     /**
20321      * Returns the selected records
20322      * @return {Array} Array of selected records
20323      */
20324     getSelections : function(){
20325         return [].concat(this.selections.items);
20326     },
20327
20328     /**
20329      * Returns the first selected record.
20330      * @return {Record}
20331      */
20332     getSelected : function(){
20333         return this.selections.itemAt(0);
20334     },
20335
20336
20337     /**
20338      * Clears all selections.
20339      */
20340     clearSelections : function(fast){
20341         if(this.locked) return;
20342         if(fast !== true){
20343             var ds = this.grid.dataSource;
20344             var s = this.selections;
20345             s.each(function(r){
20346                 this.deselectRow(ds.indexOfId(r.id));
20347             }, this);
20348             s.clear();
20349         }else{
20350             this.selections.clear();
20351         }
20352         this.last = false;
20353     },
20354
20355
20356     /**
20357      * Selects all rows.
20358      */
20359     selectAll : function(){
20360         if(this.locked) return;
20361         this.selections.clear();
20362         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
20363             this.selectRow(i, true);
20364         }
20365     },
20366
20367     /**
20368      * Returns True if there is a selection.
20369      * @return {Boolean}
20370      */
20371     hasSelection : function(){
20372         return this.selections.length > 0;
20373     },
20374
20375     /**
20376      * Returns True if the specified row is selected.
20377      * @param {Number/Record} record The record or index of the record to check
20378      * @return {Boolean}
20379      */
20380     isSelected : function(index){
20381         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
20382         return (r && this.selections.key(r.id) ? true : false);
20383     },
20384
20385     /**
20386      * Returns True if the specified record id is selected.
20387      * @param {String} id The id of record to check
20388      * @return {Boolean}
20389      */
20390     isIdSelected : function(id){
20391         return (this.selections.key(id) ? true : false);
20392     },
20393
20394     // private
20395     handleMouseDown : function(e, t){
20396         var view = this.grid.getView(), rowIndex;
20397         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
20398             return;
20399         };
20400         if(e.shiftKey && this.last !== false){
20401             var last = this.last;
20402             this.selectRange(last, rowIndex, e.ctrlKey);
20403             this.last = last; // reset the last
20404             view.focusRow(rowIndex);
20405         }else{
20406             var isSelected = this.isSelected(rowIndex);
20407             if(e.button !== 0 && isSelected){
20408                 view.focusRow(rowIndex);
20409             }else if(e.ctrlKey && isSelected){
20410                 this.deselectRow(rowIndex);
20411             }else if(!isSelected){
20412                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
20413                 view.focusRow(rowIndex);
20414             }
20415         }
20416         this.fireEvent("afterselectionchange", this);
20417     },
20418     // private
20419     handleDragableRowClick :  function(grid, rowIndex, e) 
20420     {
20421         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
20422             this.selectRow(rowIndex, false);
20423             grid.view.focusRow(rowIndex);
20424              this.fireEvent("afterselectionchange", this);
20425         }
20426     },
20427     
20428     /**
20429      * Selects multiple rows.
20430      * @param {Array} rows Array of the indexes of the row to select
20431      * @param {Boolean} keepExisting (optional) True to keep existing selections
20432      */
20433     selectRows : function(rows, keepExisting){
20434         if(!keepExisting){
20435             this.clearSelections();
20436         }
20437         for(var i = 0, len = rows.length; i < len; i++){
20438             this.selectRow(rows[i], true);
20439         }
20440     },
20441
20442     /**
20443      * Selects a range of rows. All rows in between startRow and endRow are also selected.
20444      * @param {Number} startRow The index of the first row in the range
20445      * @param {Number} endRow The index of the last row in the range
20446      * @param {Boolean} keepExisting (optional) True to retain existing selections
20447      */
20448     selectRange : function(startRow, endRow, keepExisting){
20449         if(this.locked) return;
20450         if(!keepExisting){
20451             this.clearSelections();
20452         }
20453         if(startRow <= endRow){
20454             for(var i = startRow; i <= endRow; i++){
20455                 this.selectRow(i, true);
20456             }
20457         }else{
20458             for(var i = startRow; i >= endRow; i--){
20459                 this.selectRow(i, true);
20460             }
20461         }
20462     },
20463
20464     /**
20465      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
20466      * @param {Number} startRow The index of the first row in the range
20467      * @param {Number} endRow The index of the last row in the range
20468      */
20469     deselectRange : function(startRow, endRow, preventViewNotify){
20470         if(this.locked) return;
20471         for(var i = startRow; i <= endRow; i++){
20472             this.deselectRow(i, preventViewNotify);
20473         }
20474     },
20475
20476     /**
20477      * Selects a row.
20478      * @param {Number} row The index of the row to select
20479      * @param {Boolean} keepExisting (optional) True to keep existing selections
20480      */
20481     selectRow : function(index, keepExisting, preventViewNotify){
20482         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
20483         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
20484             if(!keepExisting || this.singleSelect){
20485                 this.clearSelections();
20486             }
20487             var r = this.grid.dataSource.getAt(index);
20488             this.selections.add(r);
20489             this.last = this.lastActive = index;
20490             if(!preventViewNotify){
20491                 this.grid.getView().onRowSelect(index);
20492             }
20493             this.fireEvent("rowselect", this, index, r);
20494             this.fireEvent("selectionchange", this);
20495         }
20496     },
20497
20498     /**
20499      * Deselects a row.
20500      * @param {Number} row The index of the row to deselect
20501      */
20502     deselectRow : function(index, preventViewNotify){
20503         if(this.locked) return;
20504         if(this.last == index){
20505             this.last = false;
20506         }
20507         if(this.lastActive == index){
20508             this.lastActive = false;
20509         }
20510         var r = this.grid.dataSource.getAt(index);
20511         this.selections.remove(r);
20512         if(!preventViewNotify){
20513             this.grid.getView().onRowDeselect(index);
20514         }
20515         this.fireEvent("rowdeselect", this, index);
20516         this.fireEvent("selectionchange", this);
20517     },
20518
20519     // private
20520     restoreLast : function(){
20521         if(this._last){
20522             this.last = this._last;
20523         }
20524     },
20525
20526     // private
20527     acceptsNav : function(row, col, cm){
20528         return !cm.isHidden(col) && cm.isCellEditable(col, row);
20529     },
20530
20531     // private
20532     onEditorKey : function(field, e){
20533         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
20534         if(k == e.TAB){
20535             e.stopEvent();
20536             ed.completeEdit();
20537             if(e.shiftKey){
20538                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
20539             }else{
20540                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
20541             }
20542         }else if(k == e.ENTER && !e.ctrlKey){
20543             e.stopEvent();
20544             ed.completeEdit();
20545             if(e.shiftKey){
20546                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
20547             }else{
20548                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
20549             }
20550         }else if(k == e.ESC){
20551             ed.cancelEdit();
20552         }
20553         if(newCell){
20554             g.startEditing(newCell[0], newCell[1]);
20555         }
20556     }
20557 });/*
20558  * Based on:
20559  * Ext JS Library 1.1.1
20560  * Copyright(c) 2006-2007, Ext JS, LLC.
20561  *
20562  * Originally Released Under LGPL - original licence link has changed is not relivant.
20563  *
20564  * Fork - LGPL
20565  * <script type="text/javascript">
20566  */
20567  
20568 /**
20569  * @class Roo.bootstrap.PagingToolbar
20570  * @extends Roo.Row
20571  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
20572  * @constructor
20573  * Create a new PagingToolbar
20574  * @param {Object} config The config object
20575  */
20576 Roo.bootstrap.PagingToolbar = function(config)
20577 {
20578     // old args format still supported... - xtype is prefered..
20579         // created from xtype...
20580     var ds = config.dataSource;
20581     this.toolbarItems = [];
20582     if (config.items) {
20583         this.toolbarItems = config.items;
20584 //        config.items = [];
20585     }
20586     
20587     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
20588     this.ds = ds;
20589     this.cursor = 0;
20590     if (ds) { 
20591         this.bind(ds);
20592     }
20593     
20594     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
20595     
20596 };
20597
20598 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
20599     /**
20600      * @cfg {Roo.data.Store} dataSource
20601      * The underlying data store providing the paged data
20602      */
20603     /**
20604      * @cfg {String/HTMLElement/Element} container
20605      * container The id or element that will contain the toolbar
20606      */
20607     /**
20608      * @cfg {Boolean} displayInfo
20609      * True to display the displayMsg (defaults to false)
20610      */
20611     /**
20612      * @cfg {Number} pageSize
20613      * The number of records to display per page (defaults to 20)
20614      */
20615     pageSize: 20,
20616     /**
20617      * @cfg {String} displayMsg
20618      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
20619      */
20620     displayMsg : 'Displaying {0} - {1} of {2}',
20621     /**
20622      * @cfg {String} emptyMsg
20623      * The message to display when no records are found (defaults to "No data to display")
20624      */
20625     emptyMsg : 'No data to display',
20626     /**
20627      * Customizable piece of the default paging text (defaults to "Page")
20628      * @type String
20629      */
20630     beforePageText : "Page",
20631     /**
20632      * Customizable piece of the default paging text (defaults to "of %0")
20633      * @type String
20634      */
20635     afterPageText : "of {0}",
20636     /**
20637      * Customizable piece of the default paging text (defaults to "First Page")
20638      * @type String
20639      */
20640     firstText : "First Page",
20641     /**
20642      * Customizable piece of the default paging text (defaults to "Previous Page")
20643      * @type String
20644      */
20645     prevText : "Previous Page",
20646     /**
20647      * Customizable piece of the default paging text (defaults to "Next Page")
20648      * @type String
20649      */
20650     nextText : "Next Page",
20651     /**
20652      * Customizable piece of the default paging text (defaults to "Last Page")
20653      * @type String
20654      */
20655     lastText : "Last Page",
20656     /**
20657      * Customizable piece of the default paging text (defaults to "Refresh")
20658      * @type String
20659      */
20660     refreshText : "Refresh",
20661
20662     buttons : false,
20663     // private
20664     onRender : function(ct, position) 
20665     {
20666         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
20667         this.navgroup.parentId = this.id;
20668         this.navgroup.onRender(this.el, null);
20669         // add the buttons to the navgroup
20670         
20671         if(this.displayInfo){
20672             Roo.log(this.el.select('ul.navbar-nav',true).first());
20673             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
20674             this.displayEl = this.el.select('.x-paging-info', true).first();
20675 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
20676 //            this.displayEl = navel.el.select('span',true).first();
20677         }
20678         
20679         var _this = this;
20680         
20681         if(this.buttons){
20682             Roo.each(_this.buttons, function(e){
20683                Roo.factory(e).onRender(_this.el, null);
20684             });
20685         }
20686             
20687         Roo.each(_this.toolbarItems, function(e) {
20688             _this.navgroup.addItem(e);
20689         });
20690         
20691         
20692         this.first = this.navgroup.addItem({
20693             tooltip: this.firstText,
20694             cls: "prev",
20695             icon : 'fa fa-backward',
20696             disabled: true,
20697             preventDefault: true,
20698             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
20699         });
20700         
20701         this.prev =  this.navgroup.addItem({
20702             tooltip: this.prevText,
20703             cls: "prev",
20704             icon : 'fa fa-step-backward',
20705             disabled: true,
20706             preventDefault: true,
20707             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
20708         });
20709     //this.addSeparator();
20710         
20711         
20712         var field = this.navgroup.addItem( {
20713             tagtype : 'span',
20714             cls : 'x-paging-position',
20715             
20716             html : this.beforePageText  +
20717                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
20718                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
20719          } ); //?? escaped?
20720         
20721         this.field = field.el.select('input', true).first();
20722         this.field.on("keydown", this.onPagingKeydown, this);
20723         this.field.on("focus", function(){this.dom.select();});
20724     
20725     
20726         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
20727         //this.field.setHeight(18);
20728         //this.addSeparator();
20729         this.next = this.navgroup.addItem({
20730             tooltip: this.nextText,
20731             cls: "next",
20732             html : ' <i class="fa fa-step-forward">',
20733             disabled: true,
20734             preventDefault: true,
20735             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
20736         });
20737         this.last = this.navgroup.addItem({
20738             tooltip: this.lastText,
20739             icon : 'fa fa-forward',
20740             cls: "next",
20741             disabled: true,
20742             preventDefault: true,
20743             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
20744         });
20745     //this.addSeparator();
20746         this.loading = this.navgroup.addItem({
20747             tooltip: this.refreshText,
20748             icon: 'fa fa-refresh',
20749             preventDefault: true,
20750             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
20751         });
20752
20753     },
20754
20755     // private
20756     updateInfo : function(){
20757         if(this.displayEl){
20758             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
20759             var msg = count == 0 ?
20760                 this.emptyMsg :
20761                 String.format(
20762                     this.displayMsg,
20763                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
20764                 );
20765             this.displayEl.update(msg);
20766         }
20767     },
20768
20769     // private
20770     onLoad : function(ds, r, o){
20771        this.cursor = o.params ? o.params.start : 0;
20772        var d = this.getPageData(),
20773             ap = d.activePage,
20774             ps = d.pages;
20775         
20776        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
20777        this.field.dom.value = ap;
20778        this.first.setDisabled(ap == 1);
20779        this.prev.setDisabled(ap == 1);
20780        this.next.setDisabled(ap == ps);
20781        this.last.setDisabled(ap == ps);
20782        this.loading.enable();
20783        this.updateInfo();
20784     },
20785
20786     // private
20787     getPageData : function(){
20788         var total = this.ds.getTotalCount();
20789         return {
20790             total : total,
20791             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
20792             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
20793         };
20794     },
20795
20796     // private
20797     onLoadError : function(){
20798         this.loading.enable();
20799     },
20800
20801     // private
20802     onPagingKeydown : function(e){
20803         var k = e.getKey();
20804         var d = this.getPageData();
20805         if(k == e.RETURN){
20806             var v = this.field.dom.value, pageNum;
20807             if(!v || isNaN(pageNum = parseInt(v, 10))){
20808                 this.field.dom.value = d.activePage;
20809                 return;
20810             }
20811             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
20812             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20813             e.stopEvent();
20814         }
20815         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))
20816         {
20817           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
20818           this.field.dom.value = pageNum;
20819           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
20820           e.stopEvent();
20821         }
20822         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20823         {
20824           var v = this.field.dom.value, pageNum; 
20825           var increment = (e.shiftKey) ? 10 : 1;
20826           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20827             increment *= -1;
20828           if(!v || isNaN(pageNum = parseInt(v, 10))) {
20829             this.field.dom.value = d.activePage;
20830             return;
20831           }
20832           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
20833           {
20834             this.field.dom.value = parseInt(v, 10) + increment;
20835             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
20836             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20837           }
20838           e.stopEvent();
20839         }
20840     },
20841
20842     // private
20843     beforeLoad : function(){
20844         if(this.loading){
20845             this.loading.disable();
20846         }
20847     },
20848
20849     // private
20850     onClick : function(which){
20851         
20852         var ds = this.ds;
20853         if (!ds) {
20854             return;
20855         }
20856         
20857         switch(which){
20858             case "first":
20859                 ds.load({params:{start: 0, limit: this.pageSize}});
20860             break;
20861             case "prev":
20862                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
20863             break;
20864             case "next":
20865                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
20866             break;
20867             case "last":
20868                 var total = ds.getTotalCount();
20869                 var extra = total % this.pageSize;
20870                 var lastStart = extra ? (total - extra) : total-this.pageSize;
20871                 ds.load({params:{start: lastStart, limit: this.pageSize}});
20872             break;
20873             case "refresh":
20874                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
20875             break;
20876         }
20877     },
20878
20879     /**
20880      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
20881      * @param {Roo.data.Store} store The data store to unbind
20882      */
20883     unbind : function(ds){
20884         ds.un("beforeload", this.beforeLoad, this);
20885         ds.un("load", this.onLoad, this);
20886         ds.un("loadexception", this.onLoadError, this);
20887         ds.un("remove", this.updateInfo, this);
20888         ds.un("add", this.updateInfo, this);
20889         this.ds = undefined;
20890     },
20891
20892     /**
20893      * Binds the paging toolbar to the specified {@link Roo.data.Store}
20894      * @param {Roo.data.Store} store The data store to bind
20895      */
20896     bind : function(ds){
20897         ds.on("beforeload", this.beforeLoad, this);
20898         ds.on("load", this.onLoad, this);
20899         ds.on("loadexception", this.onLoadError, this);
20900         ds.on("remove", this.updateInfo, this);
20901         ds.on("add", this.updateInfo, this);
20902         this.ds = ds;
20903     }
20904 });/*
20905  * - LGPL
20906  *
20907  * element
20908  * 
20909  */
20910
20911 /**
20912  * @class Roo.bootstrap.MessageBar
20913  * @extends Roo.bootstrap.Component
20914  * Bootstrap MessageBar class
20915  * @cfg {String} html contents of the MessageBar
20916  * @cfg {String} weight (info | success | warning | danger) default info
20917  * @cfg {String} beforeClass insert the bar before the given class
20918  * @cfg {Boolean} closable (true | false) default false
20919  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
20920  * 
20921  * @constructor
20922  * Create a new Element
20923  * @param {Object} config The config object
20924  */
20925
20926 Roo.bootstrap.MessageBar = function(config){
20927     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
20928 };
20929
20930 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
20931     
20932     html: '',
20933     weight: 'info',
20934     closable: false,
20935     fixed: false,
20936     beforeClass: 'bootstrap-sticky-wrap',
20937     
20938     getAutoCreate : function(){
20939         
20940         var cfg = {
20941             tag: 'div',
20942             cls: 'alert alert-dismissable alert-' + this.weight,
20943             cn: [
20944                 {
20945                     tag: 'span',
20946                     cls: 'message',
20947                     html: this.html || ''
20948                 }
20949             ]
20950         }
20951         
20952         if(this.fixed){
20953             cfg.cls += ' alert-messages-fixed';
20954         }
20955         
20956         if(this.closable){
20957             cfg.cn.push({
20958                 tag: 'button',
20959                 cls: 'close',
20960                 html: 'x'
20961             });
20962         }
20963         
20964         return cfg;
20965     },
20966     
20967     onRender : function(ct, position)
20968     {
20969         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20970         
20971         if(!this.el){
20972             var cfg = Roo.apply({},  this.getAutoCreate());
20973             cfg.id = Roo.id();
20974             
20975             if (this.cls) {
20976                 cfg.cls += ' ' + this.cls;
20977             }
20978             if (this.style) {
20979                 cfg.style = this.style;
20980             }
20981             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
20982             
20983             this.el.setVisibilityMode(Roo.Element.DISPLAY);
20984         }
20985         
20986         this.el.select('>button.close').on('click', this.hide, this);
20987         
20988     },
20989     
20990     show : function()
20991     {
20992         if (!this.rendered) {
20993             this.render();
20994         }
20995         
20996         this.el.show();
20997         
20998         this.fireEvent('show', this);
20999         
21000     },
21001     
21002     hide : function()
21003     {
21004         if (!this.rendered) {
21005             this.render();
21006         }
21007         
21008         this.el.hide();
21009         
21010         this.fireEvent('hide', this);
21011     },
21012     
21013     update : function()
21014     {
21015 //        var e = this.el.dom.firstChild;
21016 //        
21017 //        if(this.closable){
21018 //            e = e.nextSibling;
21019 //        }
21020 //        
21021 //        e.data = this.html || '';
21022
21023         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
21024     }
21025    
21026 });
21027
21028  
21029
21030      /*
21031  * - LGPL
21032  *
21033  * Graph
21034  * 
21035  */
21036
21037
21038 /**
21039  * @class Roo.bootstrap.Graph
21040  * @extends Roo.bootstrap.Component
21041  * Bootstrap Graph class
21042 > Prameters
21043  -sm {number} sm 4
21044  -md {number} md 5
21045  @cfg {String} graphtype  bar | vbar | pie
21046  @cfg {number} g_x coodinator | centre x (pie)
21047  @cfg {number} g_y coodinator | centre y (pie)
21048  @cfg {number} g_r radius (pie)
21049  @cfg {number} g_height height of the chart (respected by all elements in the set)
21050  @cfg {number} g_width width of the chart (respected by all elements in the set)
21051  @cfg {Object} title The title of the chart
21052     
21053  -{Array}  values
21054  -opts (object) options for the chart 
21055      o {
21056      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
21057      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
21058      o vgutter (number)
21059      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.
21060      o stacked (boolean) whether or not to tread values as in a stacked bar chart
21061      o to
21062      o stretch (boolean)
21063      o }
21064  -opts (object) options for the pie
21065      o{
21066      o cut
21067      o startAngle (number)
21068      o endAngle (number)
21069      } 
21070  *
21071  * @constructor
21072  * Create a new Input
21073  * @param {Object} config The config object
21074  */
21075
21076 Roo.bootstrap.Graph = function(config){
21077     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
21078     
21079     this.addEvents({
21080         // img events
21081         /**
21082          * @event click
21083          * The img click event for the img.
21084          * @param {Roo.EventObject} e
21085          */
21086         "click" : true
21087     });
21088 };
21089
21090 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
21091     
21092     sm: 4,
21093     md: 5,
21094     graphtype: 'bar',
21095     g_height: 250,
21096     g_width: 400,
21097     g_x: 50,
21098     g_y: 50,
21099     g_r: 30,
21100     opts:{
21101         //g_colors: this.colors,
21102         g_type: 'soft',
21103         g_gutter: '20%'
21104
21105     },
21106     title : false,
21107
21108     getAutoCreate : function(){
21109         
21110         var cfg = {
21111             tag: 'div',
21112             html : null
21113         }
21114         
21115         
21116         return  cfg;
21117     },
21118
21119     onRender : function(ct,position){
21120         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
21121         this.raphael = Raphael(this.el.dom);
21122         
21123                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21124                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21125                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21126                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
21127                 /*
21128                 r.text(160, 10, "Single Series Chart").attr(txtattr);
21129                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
21130                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
21131                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
21132                 
21133                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
21134                 r.barchart(330, 10, 300, 220, data1);
21135                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
21136                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
21137                 */
21138                 
21139                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21140                 // r.barchart(30, 30, 560, 250,  xdata, {
21141                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
21142                 //     axis : "0 0 1 1",
21143                 //     axisxlabels :  xdata
21144                 //     //yvalues : cols,
21145                    
21146                 // });
21147 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21148 //        
21149 //        this.load(null,xdata,{
21150 //                axis : "0 0 1 1",
21151 //                axisxlabels :  xdata
21152 //                });
21153
21154     },
21155
21156     load : function(graphtype,xdata,opts){
21157         this.raphael.clear();
21158         if(!graphtype) {
21159             graphtype = this.graphtype;
21160         }
21161         if(!opts){
21162             opts = this.opts;
21163         }
21164         var r = this.raphael,
21165             fin = function () {
21166                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
21167             },
21168             fout = function () {
21169                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
21170             },
21171             pfin = function() {
21172                 this.sector.stop();
21173                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
21174
21175                 if (this.label) {
21176                     this.label[0].stop();
21177                     this.label[0].attr({ r: 7.5 });
21178                     this.label[1].attr({ "font-weight": 800 });
21179                 }
21180             },
21181             pfout = function() {
21182                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
21183
21184                 if (this.label) {
21185                     this.label[0].animate({ r: 5 }, 500, "bounce");
21186                     this.label[1].attr({ "font-weight": 400 });
21187                 }
21188             };
21189
21190         switch(graphtype){
21191             case 'bar':
21192                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21193                 break;
21194             case 'hbar':
21195                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21196                 break;
21197             case 'pie':
21198 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
21199 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
21200 //            
21201                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
21202                 
21203                 break;
21204
21205         }
21206         
21207         if(this.title){
21208             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
21209         }
21210         
21211     },
21212     
21213     setTitle: function(o)
21214     {
21215         this.title = o;
21216     },
21217     
21218     initEvents: function() {
21219         
21220         if(!this.href){
21221             this.el.on('click', this.onClick, this);
21222         }
21223     },
21224     
21225     onClick : function(e)
21226     {
21227         Roo.log('img onclick');
21228         this.fireEvent('click', this, e);
21229     }
21230    
21231 });
21232
21233  
21234 /*
21235  * - LGPL
21236  *
21237  * numberBox
21238  * 
21239  */
21240 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21241
21242 /**
21243  * @class Roo.bootstrap.dash.NumberBox
21244  * @extends Roo.bootstrap.Component
21245  * Bootstrap NumberBox class
21246  * @cfg {String} headline Box headline
21247  * @cfg {String} content Box content
21248  * @cfg {String} icon Box icon
21249  * @cfg {String} footer Footer text
21250  * @cfg {String} fhref Footer href
21251  * 
21252  * @constructor
21253  * Create a new NumberBox
21254  * @param {Object} config The config object
21255  */
21256
21257
21258 Roo.bootstrap.dash.NumberBox = function(config){
21259     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
21260     
21261 };
21262
21263 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
21264     
21265     headline : '',
21266     content : '',
21267     icon : '',
21268     footer : '',
21269     fhref : '',
21270     ficon : '',
21271     
21272     getAutoCreate : function(){
21273         
21274         var cfg = {
21275             tag : 'div',
21276             cls : 'small-box ',
21277             cn : [
21278                 {
21279                     tag : 'div',
21280                     cls : 'inner',
21281                     cn :[
21282                         {
21283                             tag : 'h3',
21284                             cls : 'roo-headline',
21285                             html : this.headline
21286                         },
21287                         {
21288                             tag : 'p',
21289                             cls : 'roo-content',
21290                             html : this.content
21291                         }
21292                     ]
21293                 }
21294             ]
21295         }
21296         
21297         if(this.icon){
21298             cfg.cn.push({
21299                 tag : 'div',
21300                 cls : 'icon',
21301                 cn :[
21302                     {
21303                         tag : 'i',
21304                         cls : 'ion ' + this.icon
21305                     }
21306                 ]
21307             });
21308         }
21309         
21310         if(this.footer){
21311             var footer = {
21312                 tag : 'a',
21313                 cls : 'small-box-footer',
21314                 href : this.fhref || '#',
21315                 html : this.footer
21316             };
21317             
21318             cfg.cn.push(footer);
21319             
21320         }
21321         
21322         return  cfg;
21323     },
21324
21325     onRender : function(ct,position){
21326         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
21327
21328
21329        
21330                 
21331     },
21332
21333     setHeadline: function (value)
21334     {
21335         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
21336     },
21337     
21338     setFooter: function (value, href)
21339     {
21340         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
21341         
21342         if(href){
21343             this.el.select('a.small-box-footer',true).first().attr('href', href);
21344         }
21345         
21346     },
21347
21348     setContent: function (value)
21349     {
21350         this.el.select('.roo-content',true).first().dom.innerHTML = value;
21351     },
21352
21353     initEvents: function() 
21354     {   
21355         
21356     }
21357     
21358 });
21359
21360  
21361 /*
21362  * - LGPL
21363  *
21364  * TabBox
21365  * 
21366  */
21367 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21368
21369 /**
21370  * @class Roo.bootstrap.dash.TabBox
21371  * @extends Roo.bootstrap.Component
21372  * Bootstrap TabBox class
21373  * @cfg {String} title Title of the TabBox
21374  * @cfg {String} icon Icon of the TabBox
21375  * @cfg {Boolean} showtabs (true|false) show the tabs default true
21376  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
21377  * 
21378  * @constructor
21379  * Create a new TabBox
21380  * @param {Object} config The config object
21381  */
21382
21383
21384 Roo.bootstrap.dash.TabBox = function(config){
21385     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
21386     this.addEvents({
21387         // raw events
21388         /**
21389          * @event addpane
21390          * When a pane is added
21391          * @param {Roo.bootstrap.dash.TabPane} pane
21392          */
21393         "addpane" : true,
21394         /**
21395          * @event activatepane
21396          * When a pane is activated
21397          * @param {Roo.bootstrap.dash.TabPane} pane
21398          */
21399         "activatepane" : true
21400         
21401          
21402     });
21403     
21404     this.panes = [];
21405 };
21406
21407 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
21408
21409     title : '',
21410     icon : false,
21411     showtabs : true,
21412     tabScrollable : false,
21413     
21414     getChildContainer : function()
21415     {
21416         return this.el.select('.tab-content', true).first();
21417     },
21418     
21419     getAutoCreate : function(){
21420         
21421         var header = {
21422             tag: 'li',
21423             cls: 'pull-left header',
21424             html: this.title,
21425             cn : []
21426         };
21427         
21428         if(this.icon){
21429             header.cn.push({
21430                 tag: 'i',
21431                 cls: 'fa ' + this.icon
21432             });
21433         }
21434         
21435         var h = {
21436             tag: 'ul',
21437             cls: 'nav nav-tabs pull-right',
21438             cn: [
21439                 header
21440             ]
21441         };
21442         
21443         if(this.tabScrollable){
21444             h = {
21445                 tag: 'div',
21446                 cls: 'tab-header',
21447                 cn: [
21448                     {
21449                         tag: 'ul',
21450                         cls: 'nav nav-tabs pull-right',
21451                         cn: [
21452                             header
21453                         ]
21454                     }
21455                 ]
21456             }
21457         }
21458         
21459         var cfg = {
21460             tag: 'div',
21461             cls: 'nav-tabs-custom',
21462             cn: [
21463                 h,
21464                 {
21465                     tag: 'div',
21466                     cls: 'tab-content no-padding',
21467                     cn: []
21468                 }
21469             ]
21470         }
21471
21472         return  cfg;
21473     },
21474     initEvents : function()
21475     {
21476         //Roo.log('add add pane handler');
21477         this.on('addpane', this.onAddPane, this);
21478     },
21479      /**
21480      * Updates the box title
21481      * @param {String} html to set the title to.
21482      */
21483     setTitle : function(value)
21484     {
21485         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
21486     },
21487     onAddPane : function(pane)
21488     {
21489         this.panes.push(pane);
21490         //Roo.log('addpane');
21491         //Roo.log(pane);
21492         // tabs are rendere left to right..
21493         if(!this.showtabs){
21494             return;
21495         }
21496         
21497         var ctr = this.el.select('.nav-tabs', true).first();
21498          
21499          
21500         var existing = ctr.select('.nav-tab',true);
21501         var qty = existing.getCount();;
21502         
21503         
21504         var tab = ctr.createChild({
21505             tag : 'li',
21506             cls : 'nav-tab' + (qty ? '' : ' active'),
21507             cn : [
21508                 {
21509                     tag : 'a',
21510                     href:'#',
21511                     html : pane.title
21512                 }
21513             ]
21514         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
21515         pane.tab = tab;
21516         
21517         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
21518         if (!qty) {
21519             pane.el.addClass('active');
21520         }
21521         
21522                 
21523     },
21524     onTabClick : function(ev,un,ob,pane)
21525     {
21526         //Roo.log('tab - prev default');
21527         ev.preventDefault();
21528         
21529         
21530         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
21531         pane.tab.addClass('active');
21532         //Roo.log(pane.title);
21533         this.getChildContainer().select('.tab-pane',true).removeClass('active');
21534         // technically we should have a deactivate event.. but maybe add later.
21535         // and it should not de-activate the selected tab...
21536         this.fireEvent('activatepane', pane);
21537         pane.el.addClass('active');
21538         pane.fireEvent('activate');
21539         
21540         
21541     },
21542     
21543     getActivePane : function()
21544     {
21545         var r = false;
21546         Roo.each(this.panes, function(p) {
21547             if(p.el.hasClass('active')){
21548                 r = p;
21549                 return false;
21550             }
21551             
21552             return;
21553         });
21554         
21555         return r;
21556     }
21557     
21558     
21559 });
21560
21561  
21562 /*
21563  * - LGPL
21564  *
21565  * Tab pane
21566  * 
21567  */
21568 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21569 /**
21570  * @class Roo.bootstrap.TabPane
21571  * @extends Roo.bootstrap.Component
21572  * Bootstrap TabPane class
21573  * @cfg {Boolean} active (false | true) Default false
21574  * @cfg {String} title title of panel
21575
21576  * 
21577  * @constructor
21578  * Create a new TabPane
21579  * @param {Object} config The config object
21580  */
21581
21582 Roo.bootstrap.dash.TabPane = function(config){
21583     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
21584     
21585     this.addEvents({
21586         // raw events
21587         /**
21588          * @event activate
21589          * When a pane is activated
21590          * @param {Roo.bootstrap.dash.TabPane} pane
21591          */
21592         "activate" : true
21593          
21594     });
21595 };
21596
21597 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
21598     
21599     active : false,
21600     title : '',
21601     
21602     // the tabBox that this is attached to.
21603     tab : false,
21604      
21605     getAutoCreate : function() 
21606     {
21607         var cfg = {
21608             tag: 'div',
21609             cls: 'tab-pane'
21610         }
21611         
21612         if(this.active){
21613             cfg.cls += ' active';
21614         }
21615         
21616         return cfg;
21617     },
21618     initEvents  : function()
21619     {
21620         //Roo.log('trigger add pane handler');
21621         this.parent().fireEvent('addpane', this)
21622     },
21623     
21624      /**
21625      * Updates the tab title 
21626      * @param {String} html to set the title to.
21627      */
21628     setTitle: function(str)
21629     {
21630         if (!this.tab) {
21631             return;
21632         }
21633         this.title = str;
21634         this.tab.select('a', true).first().dom.innerHTML = str;
21635         
21636     }
21637     
21638     
21639     
21640 });
21641
21642  
21643
21644
21645  /*
21646  * - LGPL
21647  *
21648  * menu
21649  * 
21650  */
21651 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21652
21653 /**
21654  * @class Roo.bootstrap.menu.Menu
21655  * @extends Roo.bootstrap.Component
21656  * Bootstrap Menu class - container for Menu
21657  * @cfg {String} html Text of the menu
21658  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
21659  * @cfg {String} icon Font awesome icon
21660  * @cfg {String} pos Menu align to (top | bottom) default bottom
21661  * 
21662  * 
21663  * @constructor
21664  * Create a new Menu
21665  * @param {Object} config The config object
21666  */
21667
21668
21669 Roo.bootstrap.menu.Menu = function(config){
21670     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
21671     
21672     this.addEvents({
21673         /**
21674          * @event beforeshow
21675          * Fires before this menu is displayed
21676          * @param {Roo.bootstrap.menu.Menu} this
21677          */
21678         beforeshow : true,
21679         /**
21680          * @event beforehide
21681          * Fires before this menu is hidden
21682          * @param {Roo.bootstrap.menu.Menu} this
21683          */
21684         beforehide : true,
21685         /**
21686          * @event show
21687          * Fires after this menu is displayed
21688          * @param {Roo.bootstrap.menu.Menu} this
21689          */
21690         show : true,
21691         /**
21692          * @event hide
21693          * Fires after this menu is hidden
21694          * @param {Roo.bootstrap.menu.Menu} this
21695          */
21696         hide : true,
21697         /**
21698          * @event click
21699          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
21700          * @param {Roo.bootstrap.menu.Menu} this
21701          * @param {Roo.EventObject} e
21702          */
21703         click : true
21704     });
21705     
21706 };
21707
21708 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
21709     
21710     submenu : false,
21711     html : '',
21712     weight : 'default',
21713     icon : false,
21714     pos : 'bottom',
21715     
21716     
21717     getChildContainer : function() {
21718         if(this.isSubMenu){
21719             return this.el;
21720         }
21721         
21722         return this.el.select('ul.dropdown-menu', true).first();  
21723     },
21724     
21725     getAutoCreate : function()
21726     {
21727         var text = [
21728             {
21729                 tag : 'span',
21730                 cls : 'roo-menu-text',
21731                 html : this.html
21732             }
21733         ];
21734         
21735         if(this.icon){
21736             text.unshift({
21737                 tag : 'i',
21738                 cls : 'fa ' + this.icon
21739             })
21740         }
21741         
21742         
21743         var cfg = {
21744             tag : 'div',
21745             cls : 'btn-group',
21746             cn : [
21747                 {
21748                     tag : 'button',
21749                     cls : 'dropdown-button btn btn-' + this.weight,
21750                     cn : text
21751                 },
21752                 {
21753                     tag : 'button',
21754                     cls : 'dropdown-toggle btn btn-' + this.weight,
21755                     cn : [
21756                         {
21757                             tag : 'span',
21758                             cls : 'caret'
21759                         }
21760                     ]
21761                 },
21762                 {
21763                     tag : 'ul',
21764                     cls : 'dropdown-menu'
21765                 }
21766             ]
21767             
21768         };
21769         
21770         if(this.pos == 'top'){
21771             cfg.cls += ' dropup';
21772         }
21773         
21774         if(this.isSubMenu){
21775             cfg = {
21776                 tag : 'ul',
21777                 cls : 'dropdown-menu'
21778             }
21779         }
21780         
21781         return cfg;
21782     },
21783     
21784     onRender : function(ct, position)
21785     {
21786         this.isSubMenu = ct.hasClass('dropdown-submenu');
21787         
21788         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
21789     },
21790     
21791     initEvents : function() 
21792     {
21793         if(this.isSubMenu){
21794             return;
21795         }
21796         
21797         this.hidden = true;
21798         
21799         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
21800         this.triggerEl.on('click', this.onTriggerPress, this);
21801         
21802         this.buttonEl = this.el.select('button.dropdown-button', true).first();
21803         this.buttonEl.on('click', this.onClick, this);
21804         
21805     },
21806     
21807     list : function()
21808     {
21809         if(this.isSubMenu){
21810             return this.el;
21811         }
21812         
21813         return this.el.select('ul.dropdown-menu', true).first();
21814     },
21815     
21816     onClick : function(e)
21817     {
21818         this.fireEvent("click", this, e);
21819     },
21820     
21821     onTriggerPress  : function(e)
21822     {   
21823         if (this.isVisible()) {
21824             this.hide();
21825         } else {
21826             this.show();
21827         }
21828     },
21829     
21830     isVisible : function(){
21831         return !this.hidden;
21832     },
21833     
21834     show : function()
21835     {
21836         this.fireEvent("beforeshow", this);
21837         
21838         this.hidden = false;
21839         this.el.addClass('open');
21840         
21841         Roo.get(document).on("mouseup", this.onMouseUp, this);
21842         
21843         this.fireEvent("show", this);
21844         
21845         
21846     },
21847     
21848     hide : function()
21849     {
21850         this.fireEvent("beforehide", this);
21851         
21852         this.hidden = true;
21853         this.el.removeClass('open');
21854         
21855         Roo.get(document).un("mouseup", this.onMouseUp);
21856         
21857         this.fireEvent("hide", this);
21858     },
21859     
21860     onMouseUp : function()
21861     {
21862         this.hide();
21863     }
21864     
21865 });
21866
21867  
21868  /*
21869  * - LGPL
21870  *
21871  * menu item
21872  * 
21873  */
21874 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21875
21876 /**
21877  * @class Roo.bootstrap.menu.Item
21878  * @extends Roo.bootstrap.Component
21879  * Bootstrap MenuItem class
21880  * @cfg {Boolean} submenu (true | false) default false
21881  * @cfg {String} html text of the item
21882  * @cfg {String} href the link
21883  * @cfg {Boolean} disable (true | false) default false
21884  * @cfg {Boolean} preventDefault (true | false) default true
21885  * @cfg {String} icon Font awesome icon
21886  * @cfg {String} pos Submenu align to (left | right) default right 
21887  * 
21888  * 
21889  * @constructor
21890  * Create a new Item
21891  * @param {Object} config The config object
21892  */
21893
21894
21895 Roo.bootstrap.menu.Item = function(config){
21896     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
21897     this.addEvents({
21898         /**
21899          * @event mouseover
21900          * Fires when the mouse is hovering over this menu
21901          * @param {Roo.bootstrap.menu.Item} this
21902          * @param {Roo.EventObject} e
21903          */
21904         mouseover : true,
21905         /**
21906          * @event mouseout
21907          * Fires when the mouse exits this menu
21908          * @param {Roo.bootstrap.menu.Item} this
21909          * @param {Roo.EventObject} e
21910          */
21911         mouseout : true,
21912         // raw events
21913         /**
21914          * @event click
21915          * The raw click event for the entire grid.
21916          * @param {Roo.EventObject} e
21917          */
21918         click : true
21919     });
21920 };
21921
21922 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
21923     
21924     submenu : false,
21925     href : '',
21926     html : '',
21927     preventDefault: true,
21928     disable : false,
21929     icon : false,
21930     pos : 'right',
21931     
21932     getAutoCreate : function()
21933     {
21934         var text = [
21935             {
21936                 tag : 'span',
21937                 cls : 'roo-menu-item-text',
21938                 html : this.html
21939             }
21940         ];
21941         
21942         if(this.icon){
21943             text.unshift({
21944                 tag : 'i',
21945                 cls : 'fa ' + this.icon
21946             })
21947         }
21948         
21949         var cfg = {
21950             tag : 'li',
21951             cn : [
21952                 {
21953                     tag : 'a',
21954                     href : this.href || '#',
21955                     cn : text
21956                 }
21957             ]
21958         };
21959         
21960         if(this.disable){
21961             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
21962         }
21963         
21964         if(this.submenu){
21965             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
21966             
21967             if(this.pos == 'left'){
21968                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
21969             }
21970         }
21971         
21972         return cfg;
21973     },
21974     
21975     initEvents : function() 
21976     {
21977         this.el.on('mouseover', this.onMouseOver, this);
21978         this.el.on('mouseout', this.onMouseOut, this);
21979         
21980         this.el.select('a', true).first().on('click', this.onClick, this);
21981         
21982     },
21983     
21984     onClick : function(e)
21985     {
21986         if(this.preventDefault){
21987             e.preventDefault();
21988         }
21989         
21990         this.fireEvent("click", this, e);
21991     },
21992     
21993     onMouseOver : function(e)
21994     {
21995         if(this.submenu && this.pos == 'left'){
21996             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
21997         }
21998         
21999         this.fireEvent("mouseover", this, e);
22000     },
22001     
22002     onMouseOut : function(e)
22003     {
22004         this.fireEvent("mouseout", this, e);
22005     }
22006 });
22007
22008  
22009
22010  /*
22011  * - LGPL
22012  *
22013  * menu separator
22014  * 
22015  */
22016 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22017
22018 /**
22019  * @class Roo.bootstrap.menu.Separator
22020  * @extends Roo.bootstrap.Component
22021  * Bootstrap Separator class
22022  * 
22023  * @constructor
22024  * Create a new Separator
22025  * @param {Object} config The config object
22026  */
22027
22028
22029 Roo.bootstrap.menu.Separator = function(config){
22030     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
22031 };
22032
22033 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
22034     
22035     getAutoCreate : function(){
22036         var cfg = {
22037             tag : 'li',
22038             cls: 'divider'
22039         };
22040         
22041         return cfg;
22042     }
22043    
22044 });
22045
22046  
22047
22048  /*
22049  * - LGPL
22050  *
22051  * Tooltip
22052  * 
22053  */
22054
22055 /**
22056  * @class Roo.bootstrap.Tooltip
22057  * Bootstrap Tooltip class
22058  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
22059  * to determine which dom element triggers the tooltip.
22060  * 
22061  * It needs to add support for additional attributes like tooltip-position
22062  * 
22063  * @constructor
22064  * Create a new Toolti
22065  * @param {Object} config The config object
22066  */
22067
22068 Roo.bootstrap.Tooltip = function(config){
22069     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
22070 };
22071
22072 Roo.apply(Roo.bootstrap.Tooltip, {
22073     /**
22074      * @function init initialize tooltip monitoring.
22075      * @static
22076      */
22077     currentEl : false,
22078     currentTip : false,
22079     currentRegion : false,
22080     
22081     //  init : delay?
22082     
22083     init : function()
22084     {
22085         Roo.get(document).on('mouseover', this.enter ,this);
22086         Roo.get(document).on('mouseout', this.leave, this);
22087          
22088         
22089         this.currentTip = new Roo.bootstrap.Tooltip();
22090     },
22091     
22092     enter : function(ev)
22093     {
22094         var dom = ev.getTarget();
22095         
22096         //Roo.log(['enter',dom]);
22097         var el = Roo.fly(dom);
22098         if (this.currentEl) {
22099             //Roo.log(dom);
22100             //Roo.log(this.currentEl);
22101             //Roo.log(this.currentEl.contains(dom));
22102             if (this.currentEl == el) {
22103                 return;
22104             }
22105             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
22106                 return;
22107             }
22108
22109         }
22110         
22111         
22112         
22113         if (this.currentTip.el) {
22114             this.currentTip.el.hide(); // force hiding...
22115         }    
22116         //Roo.log(ev);
22117         var bindEl = el;
22118         
22119         // you can not look for children, as if el is the body.. then everythign is the child..
22120         if (!el.attr('tooltip')) { //
22121             if (!el.select("[tooltip]").elements.length) {
22122                 return;
22123             }
22124             // is the mouse over this child...?
22125             bindEl = el.select("[tooltip]").first();
22126             var xy = ev.getXY();
22127             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
22128                 //Roo.log("not in region.");
22129                 return;
22130             }
22131             //Roo.log("child element over..");
22132             
22133         }
22134         this.currentEl = bindEl;
22135         this.currentTip.bind(bindEl);
22136         this.currentRegion = Roo.lib.Region.getRegion(dom);
22137         this.currentTip.enter();
22138         
22139     },
22140     leave : function(ev)
22141     {
22142         var dom = ev.getTarget();
22143         //Roo.log(['leave',dom]);
22144         if (!this.currentEl) {
22145             return;
22146         }
22147         
22148         
22149         if (dom != this.currentEl.dom) {
22150             return;
22151         }
22152         var xy = ev.getXY();
22153         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
22154             return;
22155         }
22156         // only activate leave if mouse cursor is outside... bounding box..
22157         
22158         
22159         
22160         
22161         if (this.currentTip) {
22162             this.currentTip.leave();
22163         }
22164         //Roo.log('clear currentEl');
22165         this.currentEl = false;
22166         
22167         
22168     },
22169     alignment : {
22170         'left' : ['r-l', [-2,0], 'right'],
22171         'right' : ['l-r', [2,0], 'left'],
22172         'bottom' : ['t-b', [0,2], 'top'],
22173         'top' : [ 'b-t', [0,-2], 'bottom']
22174     }
22175     
22176 });
22177
22178
22179 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
22180     
22181     
22182     bindEl : false,
22183     
22184     delay : null, // can be { show : 300 , hide: 500}
22185     
22186     timeout : null,
22187     
22188     hoverState : null, //???
22189     
22190     placement : 'bottom', 
22191     
22192     getAutoCreate : function(){
22193     
22194         var cfg = {
22195            cls : 'tooltip',
22196            role : 'tooltip',
22197            cn : [
22198                 {
22199                     cls : 'tooltip-arrow'
22200                 },
22201                 {
22202                     cls : 'tooltip-inner'
22203                 }
22204            ]
22205         };
22206         
22207         return cfg;
22208     },
22209     bind : function(el)
22210     {
22211         this.bindEl = el;
22212     },
22213       
22214     
22215     enter : function () {
22216        
22217         if (this.timeout != null) {
22218             clearTimeout(this.timeout);
22219         }
22220         
22221         this.hoverState = 'in';
22222          //Roo.log("enter - show");
22223         if (!this.delay || !this.delay.show) {
22224             this.show();
22225             return;
22226         }
22227         var _t = this;
22228         this.timeout = setTimeout(function () {
22229             if (_t.hoverState == 'in') {
22230                 _t.show();
22231             }
22232         }, this.delay.show);
22233     },
22234     leave : function()
22235     {
22236         clearTimeout(this.timeout);
22237     
22238         this.hoverState = 'out';
22239          if (!this.delay || !this.delay.hide) {
22240             this.hide();
22241             return;
22242         }
22243        
22244         var _t = this;
22245         this.timeout = setTimeout(function () {
22246             //Roo.log("leave - timeout");
22247             
22248             if (_t.hoverState == 'out') {
22249                 _t.hide();
22250                 Roo.bootstrap.Tooltip.currentEl = false;
22251             }
22252         }, delay);
22253     },
22254     
22255     show : function ()
22256     {
22257         if (!this.el) {
22258             this.render(document.body);
22259         }
22260         // set content.
22261         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
22262         
22263         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
22264         
22265         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
22266         
22267         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
22268         
22269         var placement = typeof this.placement == 'function' ?
22270             this.placement.call(this, this.el, on_el) :
22271             this.placement;
22272             
22273         var autoToken = /\s?auto?\s?/i;
22274         var autoPlace = autoToken.test(placement);
22275         if (autoPlace) {
22276             placement = placement.replace(autoToken, '') || 'top';
22277         }
22278         
22279         //this.el.detach()
22280         //this.el.setXY([0,0]);
22281         this.el.show();
22282         //this.el.dom.style.display='block';
22283         this.el.addClass(placement);
22284         
22285         //this.el.appendTo(on_el);
22286         
22287         var p = this.getPosition();
22288         var box = this.el.getBox();
22289         
22290         if (autoPlace) {
22291             // fixme..
22292         }
22293         var align = Roo.bootstrap.Tooltip.alignment[placement];
22294         this.el.alignTo(this.bindEl, align[0],align[1]);
22295         //var arrow = this.el.select('.arrow',true).first();
22296         //arrow.set(align[2], 
22297         
22298         this.el.addClass('in fade');
22299         this.hoverState = null;
22300         
22301         if (this.el.hasClass('fade')) {
22302             // fade it?
22303         }
22304         
22305     },
22306     hide : function()
22307     {
22308          
22309         if (!this.el) {
22310             return;
22311         }
22312         //this.el.setXY([0,0]);
22313         this.el.removeClass('in');
22314         //this.el.hide();
22315         
22316     }
22317     
22318 });
22319  
22320
22321  /*
22322  * - LGPL
22323  *
22324  * Location Picker
22325  * 
22326  */
22327
22328 /**
22329  * @class Roo.bootstrap.LocationPicker
22330  * @extends Roo.bootstrap.Component
22331  * Bootstrap LocationPicker class
22332  * @cfg {Number} latitude Position when init default 0
22333  * @cfg {Number} longitude Position when init default 0
22334  * @cfg {Number} zoom default 15
22335  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
22336  * @cfg {Boolean} mapTypeControl default false
22337  * @cfg {Boolean} disableDoubleClickZoom default false
22338  * @cfg {Boolean} scrollwheel default true
22339  * @cfg {Boolean} streetViewControl default false
22340  * @cfg {Number} radius default 0
22341  * @cfg {String} locationName
22342  * @cfg {Boolean} draggable default true
22343  * @cfg {Boolean} enableAutocomplete default false
22344  * @cfg {Boolean} enableReverseGeocode default true
22345  * @cfg {String} markerTitle
22346  * 
22347  * @constructor
22348  * Create a new LocationPicker
22349  * @param {Object} config The config object
22350  */
22351
22352
22353 Roo.bootstrap.LocationPicker = function(config){
22354     
22355     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
22356     
22357     this.addEvents({
22358         /**
22359          * @event initial
22360          * Fires when the picker initialized.
22361          * @param {Roo.bootstrap.LocationPicker} this
22362          * @param {Google Location} location
22363          */
22364         initial : true,
22365         /**
22366          * @event positionchanged
22367          * Fires when the picker position changed.
22368          * @param {Roo.bootstrap.LocationPicker} this
22369          * @param {Google Location} location
22370          */
22371         positionchanged : true,
22372         /**
22373          * @event resize
22374          * Fires when the map resize.
22375          * @param {Roo.bootstrap.LocationPicker} this
22376          */
22377         resize : true,
22378         /**
22379          * @event show
22380          * Fires when the map show.
22381          * @param {Roo.bootstrap.LocationPicker} this
22382          */
22383         show : true,
22384         /**
22385          * @event hide
22386          * Fires when the map hide.
22387          * @param {Roo.bootstrap.LocationPicker} this
22388          */
22389         hide : true,
22390         /**
22391          * @event mapClick
22392          * Fires when click the map.
22393          * @param {Roo.bootstrap.LocationPicker} this
22394          * @param {Map event} e
22395          */
22396         mapClick : true,
22397         /**
22398          * @event mapRightClick
22399          * Fires when right click the map.
22400          * @param {Roo.bootstrap.LocationPicker} this
22401          * @param {Map event} e
22402          */
22403         mapRightClick : true,
22404         /**
22405          * @event markerClick
22406          * Fires when click the marker.
22407          * @param {Roo.bootstrap.LocationPicker} this
22408          * @param {Map event} e
22409          */
22410         markerClick : true,
22411         /**
22412          * @event markerRightClick
22413          * Fires when right click the marker.
22414          * @param {Roo.bootstrap.LocationPicker} this
22415          * @param {Map event} e
22416          */
22417         markerRightClick : true,
22418         /**
22419          * @event OverlayViewDraw
22420          * Fires when OverlayView Draw
22421          * @param {Roo.bootstrap.LocationPicker} this
22422          */
22423         OverlayViewDraw : true,
22424         /**
22425          * @event OverlayViewOnAdd
22426          * Fires when OverlayView Draw
22427          * @param {Roo.bootstrap.LocationPicker} this
22428          */
22429         OverlayViewOnAdd : true,
22430         /**
22431          * @event OverlayViewOnRemove
22432          * Fires when OverlayView Draw
22433          * @param {Roo.bootstrap.LocationPicker} this
22434          */
22435         OverlayViewOnRemove : true,
22436         /**
22437          * @event OverlayViewShow
22438          * Fires when OverlayView Draw
22439          * @param {Roo.bootstrap.LocationPicker} this
22440          * @param {Pixel} cpx
22441          */
22442         OverlayViewShow : true,
22443         /**
22444          * @event OverlayViewHide
22445          * Fires when OverlayView Draw
22446          * @param {Roo.bootstrap.LocationPicker} this
22447          */
22448         OverlayViewHide : true
22449     });
22450         
22451 };
22452
22453 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
22454     
22455     gMapContext: false,
22456     
22457     latitude: 0,
22458     longitude: 0,
22459     zoom: 15,
22460     mapTypeId: false,
22461     mapTypeControl: false,
22462     disableDoubleClickZoom: false,
22463     scrollwheel: true,
22464     streetViewControl: false,
22465     radius: 0,
22466     locationName: '',
22467     draggable: true,
22468     enableAutocomplete: false,
22469     enableReverseGeocode: true,
22470     markerTitle: '',
22471     
22472     getAutoCreate: function()
22473     {
22474
22475         var cfg = {
22476             tag: 'div',
22477             cls: 'roo-location-picker'
22478         };
22479         
22480         return cfg
22481     },
22482     
22483     initEvents: function(ct, position)
22484     {       
22485         if(!this.el.getWidth() || this.isApplied()){
22486             return;
22487         }
22488         
22489         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22490         
22491         this.initial();
22492     },
22493     
22494     initial: function()
22495     {
22496         if(!this.mapTypeId){
22497             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
22498         }
22499         
22500         this.gMapContext = this.GMapContext();
22501         
22502         this.initOverlayView();
22503         
22504         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
22505         
22506         var _this = this;
22507                 
22508         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
22509             _this.setPosition(_this.gMapContext.marker.position);
22510         });
22511         
22512         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
22513             _this.fireEvent('mapClick', this, event);
22514             
22515         });
22516
22517         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
22518             _this.fireEvent('mapRightClick', this, event);
22519             
22520         });
22521         
22522         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
22523             _this.fireEvent('markerClick', this, event);
22524             
22525         });
22526
22527         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
22528             _this.fireEvent('markerRightClick', this, event);
22529             
22530         });
22531         
22532         this.setPosition(this.gMapContext.location);
22533         
22534         this.fireEvent('initial', this, this.gMapContext.location);
22535     },
22536     
22537     initOverlayView: function()
22538     {
22539         var _this = this;
22540         
22541         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
22542             
22543             draw: function()
22544             {
22545                 _this.fireEvent('OverlayViewDraw', _this);
22546             },
22547             
22548             onAdd: function()
22549             {
22550                 _this.fireEvent('OverlayViewOnAdd', _this);
22551             },
22552             
22553             onRemove: function()
22554             {
22555                 _this.fireEvent('OverlayViewOnRemove', _this);
22556             },
22557             
22558             show: function(cpx)
22559             {
22560                 _this.fireEvent('OverlayViewShow', _this, cpx);
22561             },
22562             
22563             hide: function()
22564             {
22565                 _this.fireEvent('OverlayViewHide', _this);
22566             }
22567             
22568         });
22569     },
22570     
22571     fromLatLngToContainerPixel: function(event)
22572     {
22573         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
22574     },
22575     
22576     isApplied: function() 
22577     {
22578         return this.getGmapContext() == false ? false : true;
22579     },
22580     
22581     getGmapContext: function() 
22582     {
22583         return this.gMapContext
22584     },
22585     
22586     GMapContext: function() 
22587     {
22588         var position = new google.maps.LatLng(this.latitude, this.longitude);
22589         
22590         var _map = new google.maps.Map(this.el.dom, {
22591             center: position,
22592             zoom: this.zoom,
22593             mapTypeId: this.mapTypeId,
22594             mapTypeControl: this.mapTypeControl,
22595             disableDoubleClickZoom: this.disableDoubleClickZoom,
22596             scrollwheel: this.scrollwheel,
22597             streetViewControl: this.streetViewControl,
22598             locationName: this.locationName,
22599             draggable: this.draggable,
22600             enableAutocomplete: this.enableAutocomplete,
22601             enableReverseGeocode: this.enableReverseGeocode
22602         });
22603         
22604         var _marker = new google.maps.Marker({
22605             position: position,
22606             map: _map,
22607             title: this.markerTitle,
22608             draggable: this.draggable
22609         });
22610         
22611         return {
22612             map: _map,
22613             marker: _marker,
22614             circle: null,
22615             location: position,
22616             radius: this.radius,
22617             locationName: this.locationName,
22618             addressComponents: {
22619                 formatted_address: null,
22620                 addressLine1: null,
22621                 addressLine2: null,
22622                 streetName: null,
22623                 streetNumber: null,
22624                 city: null,
22625                 district: null,
22626                 state: null,
22627                 stateOrProvince: null
22628             },
22629             settings: this,
22630             domContainer: this.el.dom,
22631             geodecoder: new google.maps.Geocoder()
22632         };
22633     },
22634     
22635     drawCircle: function(center, radius, options) 
22636     {
22637         if (this.gMapContext.circle != null) {
22638             this.gMapContext.circle.setMap(null);
22639         }
22640         if (radius > 0) {
22641             radius *= 1;
22642             options = Roo.apply({}, options, {
22643                 strokeColor: "#0000FF",
22644                 strokeOpacity: .35,
22645                 strokeWeight: 2,
22646                 fillColor: "#0000FF",
22647                 fillOpacity: .2
22648             });
22649             
22650             options.map = this.gMapContext.map;
22651             options.radius = radius;
22652             options.center = center;
22653             this.gMapContext.circle = new google.maps.Circle(options);
22654             return this.gMapContext.circle;
22655         }
22656         
22657         return null;
22658     },
22659     
22660     setPosition: function(location) 
22661     {
22662         this.gMapContext.location = location;
22663         this.gMapContext.marker.setPosition(location);
22664         this.gMapContext.map.panTo(location);
22665         this.drawCircle(location, this.gMapContext.radius, {});
22666         
22667         var _this = this;
22668         
22669         if (this.gMapContext.settings.enableReverseGeocode) {
22670             this.gMapContext.geodecoder.geocode({
22671                 latLng: this.gMapContext.location
22672             }, function(results, status) {
22673                 
22674                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
22675                     _this.gMapContext.locationName = results[0].formatted_address;
22676                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
22677                     
22678                     _this.fireEvent('positionchanged', this, location);
22679                 }
22680             });
22681             
22682             return;
22683         }
22684         
22685         this.fireEvent('positionchanged', this, location);
22686     },
22687     
22688     resize: function()
22689     {
22690         google.maps.event.trigger(this.gMapContext.map, "resize");
22691         
22692         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
22693         
22694         this.fireEvent('resize', this);
22695     },
22696     
22697     setPositionByLatLng: function(latitude, longitude)
22698     {
22699         this.setPosition(new google.maps.LatLng(latitude, longitude));
22700     },
22701     
22702     getCurrentPosition: function() 
22703     {
22704         return {
22705             latitude: this.gMapContext.location.lat(),
22706             longitude: this.gMapContext.location.lng()
22707         };
22708     },
22709     
22710     getAddressName: function() 
22711     {
22712         return this.gMapContext.locationName;
22713     },
22714     
22715     getAddressComponents: function() 
22716     {
22717         return this.gMapContext.addressComponents;
22718     },
22719     
22720     address_component_from_google_geocode: function(address_components) 
22721     {
22722         var result = {};
22723         
22724         for (var i = 0; i < address_components.length; i++) {
22725             var component = address_components[i];
22726             if (component.types.indexOf("postal_code") >= 0) {
22727                 result.postalCode = component.short_name;
22728             } else if (component.types.indexOf("street_number") >= 0) {
22729                 result.streetNumber = component.short_name;
22730             } else if (component.types.indexOf("route") >= 0) {
22731                 result.streetName = component.short_name;
22732             } else if (component.types.indexOf("neighborhood") >= 0) {
22733                 result.city = component.short_name;
22734             } else if (component.types.indexOf("locality") >= 0) {
22735                 result.city = component.short_name;
22736             } else if (component.types.indexOf("sublocality") >= 0) {
22737                 result.district = component.short_name;
22738             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
22739                 result.stateOrProvince = component.short_name;
22740             } else if (component.types.indexOf("country") >= 0) {
22741                 result.country = component.short_name;
22742             }
22743         }
22744         
22745         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
22746         result.addressLine2 = "";
22747         return result;
22748     },
22749     
22750     setZoomLevel: function(zoom)
22751     {
22752         this.gMapContext.map.setZoom(zoom);
22753     },
22754     
22755     show: function()
22756     {
22757         if(!this.el){
22758             return;
22759         }
22760         
22761         this.el.show();
22762         
22763         this.resize();
22764         
22765         this.fireEvent('show', this);
22766     },
22767     
22768     hide: function()
22769     {
22770         if(!this.el){
22771             return;
22772         }
22773         
22774         this.el.hide();
22775         
22776         this.fireEvent('hide', this);
22777     }
22778     
22779 });
22780
22781 Roo.apply(Roo.bootstrap.LocationPicker, {
22782     
22783     OverlayView : function(map, options)
22784     {
22785         options = options || {};
22786         
22787         this.setMap(map);
22788     }
22789     
22790     
22791 });/*
22792  * - LGPL
22793  *
22794  * Alert
22795  * 
22796  */
22797
22798 /**
22799  * @class Roo.bootstrap.Alert
22800  * @extends Roo.bootstrap.Component
22801  * Bootstrap Alert class
22802  * @cfg {String} title The title of alert
22803  * @cfg {String} html The content of alert
22804  * @cfg {String} weight (  success | info | warning | danger )
22805  * @cfg {String} faicon font-awesomeicon
22806  * 
22807  * @constructor
22808  * Create a new alert
22809  * @param {Object} config The config object
22810  */
22811
22812
22813 Roo.bootstrap.Alert = function(config){
22814     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
22815     
22816 };
22817
22818 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
22819     
22820     title: '',
22821     html: '',
22822     weight: false,
22823     faicon: false,
22824     
22825     getAutoCreate : function()
22826     {
22827         
22828         var cfg = {
22829             tag : 'div',
22830             cls : 'alert',
22831             cn : [
22832                 {
22833                     tag : 'i',
22834                     cls : 'roo-alert-icon'
22835                     
22836                 },
22837                 {
22838                     tag : 'b',
22839                     cls : 'roo-alert-title',
22840                     html : this.title
22841                 },
22842                 {
22843                     tag : 'span',
22844                     cls : 'roo-alert-text',
22845                     html : this.html
22846                 }
22847             ]
22848         };
22849         
22850         if(this.faicon){
22851             cfg.cn[0].cls += ' fa ' + this.faicon;
22852         }
22853         
22854         if(this.weight){
22855             cfg.cls += ' alert-' + this.weight;
22856         }
22857         
22858         return cfg;
22859     },
22860     
22861     initEvents: function() 
22862     {
22863         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22864     },
22865     
22866     setTitle : function(str)
22867     {
22868         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
22869     },
22870     
22871     setText : function(str)
22872     {
22873         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
22874     },
22875     
22876     setWeight : function(weight)
22877     {
22878         if(this.weight){
22879             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
22880         }
22881         
22882         this.weight = weight;
22883         
22884         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
22885     },
22886     
22887     setIcon : function(icon)
22888     {
22889         if(this.faicon){
22890             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
22891         }
22892         
22893         this.faicon = icon
22894         
22895         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
22896     },
22897     
22898     hide: function() 
22899     {
22900         this.el.hide();   
22901     },
22902     
22903     show: function() 
22904     {  
22905         this.el.show();   
22906     }
22907     
22908 });
22909
22910