docs/default.css
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129        
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         this.initEvents();
141         
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229         return ret;
230     },
231     
232     addxtypeChild : function (tree, cntr)
233     {
234         Roo.debug && Roo.log('addxtypeChild:' + cntr);
235         var cn = this;
236         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
237         
238         
239         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
240                     (typeof(tree['flexy:foreach']) != 'undefined');
241           
242         
243         
244          skip_children = false;
245         // render the element if it's not BODY.
246         if (tree.xtype != 'Body') {
247            
248             cn = Roo.factory(tree);
249            
250             cn.parentType = this.xtype; //??
251             cn.parentId = this.id;
252             
253             var build_from_html =  Roo.XComponent.build_from_html;
254             
255             
256             // does the container contain child eleemnts with 'xtype' attributes.
257             // that match this xtype..
258             // note - when we render we create these as well..
259             // so we should check to see if body has xtype set.
260             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
261                
262                 var self_cntr_el = Roo.get(this[cntr](false));
263                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
264                 if (echild) { 
265                     Roo.log(Roo.XComponent.build_from_html);
266                     Roo.log("got echild:");
267                     Roo.log(echild);
268                 }
269                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
270                 // and are not displayed -this causes this to use up the wrong element when matching.
271                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
272                 
273                 
274                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
275                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
276                   
277                   
278                   
279                     cn.el = echild;
280                   //  Roo.log("GOT");
281                     //echild.dom.removeAttribute('xtype');
282                 } else {
283                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
284                     Roo.debug && Roo.log(self_cntr_el);
285                     Roo.debug && Roo.log(echild);
286                     Roo.debug && Roo.log(cn);
287                 }
288             }
289            
290             
291            
292             // if object has flexy:if - then it may or may not be rendered.
293             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
294                 // skip a flexy if element.
295                 Roo.debug && Roo.log('skipping render');
296                 Roo.debug && Roo.log(tree);
297                 if (!cn.el) {
298                     Roo.debug && Roo.log('skipping all children');
299                     skip_children = true;
300                 }
301                 
302              } else {
303                  
304                 // actually if flexy:foreach is found, we really want to create 
305                 // multiple copies here...
306                 //Roo.log('render');
307                 //Roo.log(this[cntr]());
308                 cn.render(this[cntr](true));
309              }
310             // then add the element..
311         }
312         
313         
314         // handle the kids..
315         
316         var nitems = [];
317         /*
318         if (typeof (tree.menu) != 'undefined') {
319             tree.menu.parentType = cn.xtype;
320             tree.menu.triggerEl = cn.el;
321             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
322             
323         }
324         */
325         if (!tree.items || !tree.items.length) {
326             cn.items = nitems;
327             return cn;
328         }
329         var items = tree.items;
330         delete tree.items;
331         
332         //Roo.log(items.length);
333             // add the items..
334         if (!skip_children) {    
335             for(var i =0;i < items.length;i++) {
336                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
337             }
338         }
339         
340         cn.items = nitems;
341         
342         this.fireEvent('childrenrendered', this);
343         
344         return cn;
345     } 
346     
347     
348 });
349
350  /*
351  * - LGPL
352  *
353  * Body
354  * 
355  */
356
357 /**
358  * @class Roo.bootstrap.Body
359  * @extends Roo.bootstrap.Component
360  * Bootstrap Body class
361  * 
362  * @constructor
363  * Create a new body
364  * @param {Object} config The config object
365  */
366
367 Roo.bootstrap.Body = function(config){
368     Roo.bootstrap.Body.superclass.constructor.call(this, config);
369     this.el = Roo.get(document.body);
370     if (this.cls && this.cls.length) {
371         Roo.get(document.body).addClass(this.cls);
372     }
373 };
374
375 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
376       
377         autoCreate : {
378         cls: 'container'
379     },
380     onRender : function(ct, position)
381     {
382        /* Roo.log("Roo.bootstrap.Body - onRender");
383         if (this.cls && this.cls.length) {
384             Roo.get(document.body).addClass(this.cls);
385         }
386         // style??? xttr???
387         */
388     }
389     
390     
391  
392    
393 });
394
395  /*
396  * - LGPL
397  *
398  * button group
399  * 
400  */
401
402
403 /**
404  * @class Roo.bootstrap.ButtonGroup
405  * @extends Roo.bootstrap.Component
406  * Bootstrap ButtonGroup class
407  * @cfg {String} size lg | sm | xs (default empty normal)
408  * @cfg {String} align vertical | justified  (default none)
409  * @cfg {String} direction up | down (default down)
410  * @cfg {Boolean} toolbar false | true
411  * @cfg {Boolean} btn true | false
412  * 
413  * 
414  * @constructor
415  * Create a new Input
416  * @param {Object} config The config object
417  */
418
419 Roo.bootstrap.ButtonGroup = function(config){
420     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
421 };
422
423 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
424     
425     size: '',
426     align: '',
427     direction: '',
428     toolbar: false,
429     btn: true,
430
431     getAutoCreate : function(){
432         var cfg = {
433             cls: 'btn-group',
434             html : null
435         }
436         
437         cfg.html = this.html || cfg.html;
438         
439         if (this.toolbar) {
440             cfg = {
441                 cls: 'btn-toolbar',
442                 html: null
443             }
444             
445             return cfg;
446         }
447         
448         if (['vertical','justified'].indexOf(this.align)!==-1) {
449             cfg.cls = 'btn-group-' + this.align;
450             
451             if (this.align == 'justified') {
452                 console.log(this.items);
453             }
454         }
455         
456         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
457             cfg.cls += ' btn-group-' + this.size;
458         }
459         
460         if (this.direction == 'up') {
461             cfg.cls += ' dropup' ;
462         }
463         
464         return cfg;
465     }
466    
467 });
468
469  /*
470  * - LGPL
471  *
472  * button
473  * 
474  */
475
476 /**
477  * @class Roo.bootstrap.Button
478  * @extends Roo.bootstrap.Component
479  * Bootstrap Button class
480  * @cfg {String} html The button content
481  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
482  * @cfg {String} size ( lg | sm | xs)
483  * @cfg {String} tag ( a | input | submit)
484  * @cfg {String} href empty or href
485  * @cfg {Boolean} disabled default false;
486  * @cfg {Boolean} isClose default false;
487  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
488  * @cfg {String} badge text for badge
489  * @cfg {String} theme default 
490  * @cfg {Boolean} inverse 
491  * @cfg {Boolean} toggle 
492  * @cfg {String} ontext text for on toggle state
493  * @cfg {String} offtext text for off toggle state
494  * @cfg {Boolean} defaulton 
495  * @cfg {Boolean} preventDefault  default true
496  * @cfg {Boolean} removeClass remove the standard class..
497  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
498  * 
499  * @constructor
500  * Create a new button
501  * @param {Object} config The config object
502  */
503
504
505 Roo.bootstrap.Button = function(config){
506     Roo.bootstrap.Button.superclass.constructor.call(this, config);
507     this.addEvents({
508         // raw events
509         /**
510          * @event click
511          * When a butotn is pressed
512          * @param {Roo.bootstrap.Button} this
513          * @param {Roo.EventObject} e
514          */
515         "click" : true,
516          /**
517          * @event toggle
518          * After the button has been toggles
519          * @param {Roo.EventObject} e
520          * @param {boolean} pressed (also available as button.pressed)
521          */
522         "toggle" : true
523     });
524 };
525
526 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
527     html: false,
528     active: false,
529     weight: '',
530     size: '',
531     tag: 'button',
532     href: '',
533     disabled: false,
534     isClose: false,
535     glyphicon: '',
536     badge: '',
537     theme: 'default',
538     inverse: false,
539     
540     toggle: false,
541     ontext: 'ON',
542     offtext: 'OFF',
543     defaulton: true,
544     preventDefault: true,
545     removeClass: false,
546     name: false,
547     target: false,
548     
549     
550     pressed : null,
551      
552     
553     getAutoCreate : function(){
554         
555         var cfg = {
556             tag : 'button',
557             cls : 'roo-button',
558             html: ''
559         };
560         
561         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
562             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
563             this.tag = 'button';
564         } else {
565             cfg.tag = this.tag;
566         }
567         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
568         
569         if (this.toggle == true) {
570             cfg={
571                 tag: 'div',
572                 cls: 'slider-frame roo-button',
573                 cn: [
574                     {
575                         tag: 'span',
576                         'data-on-text':'ON',
577                         'data-off-text':'OFF',
578                         cls: 'slider-button',
579                         html: this.offtext
580                     }
581                 ]
582             };
583             
584             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
585                 cfg.cls += ' '+this.weight;
586             }
587             
588             return cfg;
589         }
590         
591         if (this.isClose) {
592             cfg.cls += ' close';
593             
594             cfg["aria-hidden"] = true;
595             
596             cfg.html = "&times;";
597             
598             return cfg;
599         }
600         
601          
602         if (this.theme==='default') {
603             cfg.cls = 'btn roo-button';
604             
605             //if (this.parentType != 'Navbar') {
606             this.weight = this.weight.length ?  this.weight : 'default';
607             //}
608             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
609                 
610                 cfg.cls += ' btn-' + this.weight;
611             }
612         } else if (this.theme==='glow') {
613             
614             cfg.tag = 'a';
615             cfg.cls = 'btn-glow roo-button';
616             
617             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
618                 
619                 cfg.cls += ' ' + this.weight;
620             }
621         }
622    
623         
624         if (this.inverse) {
625             this.cls += ' inverse';
626         }
627         
628         
629         if (this.active) {
630             cfg.cls += ' active';
631         }
632         
633         if (this.disabled) {
634             cfg.disabled = 'disabled';
635         }
636         
637         if (this.items) {
638             Roo.log('changing to ul' );
639             cfg.tag = 'ul';
640             this.glyphicon = 'caret';
641         }
642         
643         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
644          
645         //gsRoo.log(this.parentType);
646         if (this.parentType === 'Navbar' && !this.parent().bar) {
647             Roo.log('changing to li?');
648             
649             cfg.tag = 'li';
650             
651             cfg.cls = '';
652             cfg.cn =  [{
653                 tag : 'a',
654                 cls : 'roo-button',
655                 html : this.html,
656                 href : this.href || '#'
657             }];
658             if (this.menu) {
659                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
660                 cfg.cls += ' dropdown';
661             }   
662             
663             delete cfg.html;
664             
665         }
666         
667        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
668         
669         if (this.glyphicon) {
670             cfg.html = ' ' + cfg.html;
671             
672             cfg.cn = [
673                 {
674                     tag: 'span',
675                     cls: 'glyphicon glyphicon-' + this.glyphicon
676                 }
677             ];
678         }
679         
680         if (this.badge) {
681             cfg.html += ' ';
682             
683             cfg.tag = 'a';
684             
685 //            cfg.cls='btn roo-button';
686             
687             cfg.href=this.href;
688             
689             var value = cfg.html;
690             
691             if(this.glyphicon){
692                 value = {
693                             tag: 'span',
694                             cls: 'glyphicon glyphicon-' + this.glyphicon,
695                             html: this.html
696                         };
697                 
698             }
699             
700             cfg.cn = [
701                 value,
702                 {
703                     tag: 'span',
704                     cls: 'badge',
705                     html: this.badge
706                 }
707             ];
708             
709             cfg.html='';
710         }
711         
712         if (this.menu) {
713             cfg.cls += ' dropdown';
714             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
715         }
716         
717         if (cfg.tag !== 'a' && this.href !== '') {
718             throw "Tag must be a to set href.";
719         } else if (this.href.length > 0) {
720             cfg.href = this.href;
721         }
722         
723         if(this.removeClass){
724             cfg.cls = '';
725         }
726         
727         if(this.target){
728             cfg.target = this.target;
729         }
730         
731         return cfg;
732     },
733     initEvents: function() {
734        // Roo.log('init events?');
735 //        Roo.log(this.el.dom);
736         // add the menu...
737         
738         if (typeof (this.menu) != 'undefined') {
739             this.menu.parentType = this.xtype;
740             this.menu.triggerEl = this.el;
741             this.addxtype(Roo.apply({}, this.menu));
742         }
743
744
745        if (this.el.hasClass('roo-button')) {
746             this.el.on('click', this.onClick, this);
747        } else {
748             this.el.select('.roo-button').on('click', this.onClick, this);
749        }
750        
751        if(this.removeClass){
752            this.el.on('click', this.onClick, this);
753        }
754        
755        this.el.enableDisplayMode();
756         
757     },
758     onClick : function(e)
759     {
760         if (this.disabled) {
761             return;
762         }
763         
764         
765         Roo.log('button on click ');
766         if(this.preventDefault){
767             e.preventDefault();
768         }
769         if (this.pressed === true || this.pressed === false) {
770             this.pressed = !this.pressed;
771             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
772             this.fireEvent('toggle', this, e, this.pressed);
773         }
774         
775         
776         this.fireEvent('click', this, e);
777     },
778     
779     /**
780      * Enables this button
781      */
782     enable : function()
783     {
784         this.disabled = false;
785         this.el.removeClass('disabled');
786     },
787     
788     /**
789      * Disable this button
790      */
791     disable : function()
792     {
793         this.disabled = true;
794         this.el.addClass('disabled');
795     },
796      /**
797      * sets the active state on/off, 
798      * @param {Boolean} state (optional) Force a particular state
799      */
800     setActive : function(v) {
801         
802         this.el[v ? 'addClass' : 'removeClass']('active');
803     },
804      /**
805      * toggles the current active state 
806      */
807     toggleActive : function()
808     {
809        var active = this.el.hasClass('active');
810        this.setActive(!active);
811        
812         
813     },
814     setText : function(str)
815     {
816         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
817     },
818     getText : function()
819     {
820         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
821     },
822     hide: function() {
823        
824      
825         this.el.hide();   
826     },
827     show: function() {
828        
829         this.el.show();   
830     }
831     
832     
833 });
834
835  /*
836  * - LGPL
837  *
838  * column
839  * 
840  */
841
842 /**
843  * @class Roo.bootstrap.Column
844  * @extends Roo.bootstrap.Component
845  * Bootstrap Column class
846  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
847  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
848  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
849  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
850  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
851  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
852  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
853  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
854  *
855  * 
856  * @cfg {Boolean} hidden (true|false) hide the element
857  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
858  * @cfg {String} fa (ban|check|...) font awesome icon
859  * @cfg {Number} fasize (1|2|....) font awsome size
860
861  * @cfg {String} icon (info-sign|check|...) glyphicon name
862
863  * @cfg {String} html content of column.
864  * 
865  * @constructor
866  * Create a new Column
867  * @param {Object} config The config object
868  */
869
870 Roo.bootstrap.Column = function(config){
871     Roo.bootstrap.Column.superclass.constructor.call(this, config);
872 };
873
874 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
875     
876     xs: false,
877     sm: false,
878     md: false,
879     lg: false,
880     xsoff: false,
881     smoff: false,
882     mdoff: false,
883     lgoff: false,
884     html: '',
885     offset: 0,
886     alert: false,
887     fa: false,
888     icon : false,
889     hidden : false,
890     fasize : 1,
891     
892     getAutoCreate : function(){
893         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
894         
895         cfg = {
896             tag: 'div',
897             cls: 'column'
898         };
899         
900         var settings=this;
901         ['xs','sm','md','lg'].map(function(size){
902             //Roo.log( size + ':' + settings[size]);
903             
904             if (settings[size+'off'] !== false) {
905                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
906             }
907             
908             if (settings[size] === false) {
909                 return;
910             }
911             Roo.log(settings[size]);
912             if (!settings[size]) { // 0 = hidden
913                 cfg.cls += ' hidden-' + size;
914                 return;
915             }
916             cfg.cls += ' col-' + size + '-' + settings[size];
917             
918         });
919         
920         if (this.hidden) {
921             cfg.cls += ' hidden';
922         }
923         
924         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
925             cfg.cls +=' alert alert-' + this.alert;
926         }
927         
928         
929         if (this.html.length) {
930             cfg.html = this.html;
931         }
932         if (this.fa) {
933             var fasize = '';
934             if (this.fasize > 1) {
935                 fasize = ' fa-' + this.fasize + 'x';
936             }
937             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
938             
939             
940         }
941         if (this.icon) {
942             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
943         }
944         
945         return cfg;
946     }
947    
948 });
949
950  
951
952  /*
953  * - LGPL
954  *
955  * page container.
956  * 
957  */
958
959
960 /**
961  * @class Roo.bootstrap.Container
962  * @extends Roo.bootstrap.Component
963  * Bootstrap Container class
964  * @cfg {Boolean} jumbotron is it a jumbotron element
965  * @cfg {String} html content of element
966  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
967  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
968  * @cfg {String} header content of header (for panel)
969  * @cfg {String} footer content of footer (for panel)
970  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
971  * @cfg {String} tag (header|aside|section) type of HTML tag.
972  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
973  * @cfg {String} fa (ban|check|...) font awesome icon
974  * @cfg {String} icon (info-sign|check|...) glyphicon name
975  * @cfg {Boolean} hidden (true|false) hide the element
976
977  *     
978  * @constructor
979  * Create a new Container
980  * @param {Object} config The config object
981  */
982
983 Roo.bootstrap.Container = function(config){
984     Roo.bootstrap.Container.superclass.constructor.call(this, config);
985 };
986
987 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
988     
989     jumbotron : false,
990     well: '',
991     panel : '',
992     header: '',
993     footer : '',
994     sticky: '',
995     tag : false,
996     alert : false,
997     fa: false,
998     icon : false,
999   
1000      
1001     getChildContainer : function() {
1002         
1003         if(!this.el){
1004             return false;
1005         }
1006         
1007         if (this.panel.length) {
1008             return this.el.select('.panel-body',true).first();
1009         }
1010         
1011         return this.el;
1012     },
1013     
1014     
1015     getAutoCreate : function(){
1016         
1017         var cfg = {
1018             tag : this.tag || 'div',
1019             html : '',
1020             cls : ''
1021         };
1022         if (this.jumbotron) {
1023             cfg.cls = 'jumbotron';
1024         }
1025         
1026         
1027         
1028         // - this is applied by the parent..
1029         //if (this.cls) {
1030         //    cfg.cls = this.cls + '';
1031         //}
1032         
1033         if (this.sticky.length) {
1034             
1035             var bd = Roo.get(document.body);
1036             if (!bd.hasClass('bootstrap-sticky')) {
1037                 bd.addClass('bootstrap-sticky');
1038                 Roo.select('html',true).setStyle('height', '100%');
1039             }
1040              
1041             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1042         }
1043         
1044         
1045         if (this.well.length) {
1046             switch (this.well) {
1047                 case 'lg':
1048                 case 'sm':
1049                     cfg.cls +=' well well-' +this.well;
1050                     break;
1051                 default:
1052                     cfg.cls +=' well';
1053                     break;
1054             }
1055         }
1056         
1057         if (this.hidden) {
1058             cfg.cls += ' hidden';
1059         }
1060         
1061         
1062         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1063             cfg.cls +=' alert alert-' + this.alert;
1064         }
1065         
1066         var body = cfg;
1067         
1068         if (this.panel.length) {
1069             cfg.cls += ' panel panel-' + this.panel;
1070             cfg.cn = [];
1071             if (this.header.length) {
1072                 cfg.cn.push({
1073                     
1074                     cls : 'panel-heading',
1075                     cn : [{
1076                         tag: 'h3',
1077                         cls : 'panel-title',
1078                         html : this.header
1079                     }]
1080                     
1081                 });
1082             }
1083             body = false;
1084             cfg.cn.push({
1085                 cls : 'panel-body',
1086                 html : this.html
1087             });
1088             
1089             
1090             if (this.footer.length) {
1091                 cfg.cn.push({
1092                     cls : 'panel-footer',
1093                     html : this.footer
1094                     
1095                 });
1096             }
1097             
1098         }
1099         
1100         if (body) {
1101             body.html = this.html || cfg.html;
1102             // prefix with the icons..
1103             if (this.fa) {
1104                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1105             }
1106             if (this.icon) {
1107                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1108             }
1109             
1110             
1111         }
1112         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1113             cfg.cls =  'container';
1114         }
1115         
1116         return cfg;
1117     },
1118     
1119     titleEl : function()
1120     {
1121         if(!this.el || !this.panel.length || !this.header.length){
1122             return;
1123         }
1124         
1125         return this.el.select('.panel-title',true).first();
1126     },
1127     
1128     setTitle : function(v)
1129     {
1130         var titleEl = this.titleEl();
1131         
1132         if(!titleEl){
1133             return;
1134         }
1135         
1136         titleEl.dom.innerHTML = v;
1137     },
1138     
1139     getTitle : function()
1140     {
1141         
1142         var titleEl = this.titleEl();
1143         
1144         if(!titleEl){
1145             return '';
1146         }
1147         
1148         return titleEl.dom.innerHTML;
1149     },
1150     
1151     show : function() {
1152         this.el.removeClass('hidden');
1153     },
1154     hide: function() {
1155         if (!this.el.hasClass('hidden')) {
1156             this.el.addClass('hidden');
1157         }
1158         
1159     }
1160    
1161 });
1162
1163  /*
1164  * - LGPL
1165  *
1166  * image
1167  * 
1168  */
1169
1170
1171 /**
1172  * @class Roo.bootstrap.Img
1173  * @extends Roo.bootstrap.Component
1174  * Bootstrap Img class
1175  * @cfg {Boolean} imgResponsive false | true
1176  * @cfg {String} border rounded | circle | thumbnail
1177  * @cfg {String} src image source
1178  * @cfg {String} alt image alternative text
1179  * @cfg {String} href a tag href
1180  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1181  * 
1182  * @constructor
1183  * Create a new Input
1184  * @param {Object} config The config object
1185  */
1186
1187 Roo.bootstrap.Img = function(config){
1188     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1189     
1190     this.addEvents({
1191         // img events
1192         /**
1193          * @event click
1194          * The img click event for the img.
1195          * @param {Roo.EventObject} e
1196          */
1197         "click" : true
1198     });
1199 };
1200
1201 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1202     
1203     imgResponsive: true,
1204     border: '',
1205     src: '',
1206     href: false,
1207     target: false,
1208
1209     getAutoCreate : function(){
1210         
1211         var cfg = {
1212             tag: 'img',
1213             cls: (this.imgResponsive) ? 'img-responsive' : '',
1214             html : null
1215         }
1216         
1217         cfg.html = this.html || cfg.html;
1218         
1219         cfg.src = this.src || cfg.src;
1220         
1221         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1222             cfg.cls += ' img-' + this.border;
1223         }
1224         
1225         if(this.alt){
1226             cfg.alt = this.alt;
1227         }
1228         
1229         if(this.href){
1230             var a = {
1231                 tag: 'a',
1232                 href: this.href,
1233                 cn: [
1234                     cfg
1235                 ]
1236             }
1237             
1238             if(this.target){
1239                 a.target = this.target;
1240             }
1241             
1242         }
1243         
1244         
1245         return (this.href) ? a : cfg;
1246     },
1247     
1248     initEvents: function() {
1249         
1250         if(!this.href){
1251             this.el.on('click', this.onClick, this);
1252         }
1253     },
1254     
1255     onClick : function(e)
1256     {
1257         Roo.log('img onclick');
1258         this.fireEvent('click', this, e);
1259     }
1260    
1261 });
1262
1263  /*
1264  * - LGPL
1265  *
1266  * image
1267  * 
1268  */
1269
1270
1271 /**
1272  * @class Roo.bootstrap.Link
1273  * @extends Roo.bootstrap.Component
1274  * Bootstrap Link Class
1275  * @cfg {String} alt image alternative text
1276  * @cfg {String} href a tag href
1277  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1278  * @cfg {String} html the content of the link.
1279  * @cfg {String} anchor name for the anchor link
1280
1281  * @cfg {Boolean} preventDefault (true | false) default false
1282
1283  * 
1284  * @constructor
1285  * Create a new Input
1286  * @param {Object} config The config object
1287  */
1288
1289 Roo.bootstrap.Link = function(config){
1290     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1291     
1292     this.addEvents({
1293         // img events
1294         /**
1295          * @event click
1296          * The img click event for the img.
1297          * @param {Roo.EventObject} e
1298          */
1299         "click" : true
1300     });
1301 };
1302
1303 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1304     
1305     href: false,
1306     target: false,
1307     preventDefault: false,
1308     anchor : false,
1309     alt : false,
1310
1311     getAutoCreate : function()
1312     {
1313         
1314         var cfg = {
1315             tag: 'a'
1316         };
1317         // anchor's do not require html/href...
1318         if (this.anchor === false) {
1319             cfg.html = this.html || 'html-missing';
1320             cfg.href = this.href || '#';
1321         } else {
1322             cfg.name = this.anchor;
1323             if (this.html !== false) {
1324                 cfg.html = this.html;
1325             }
1326             if (this.href !== false) {
1327                 cfg.href = this.href;
1328             }
1329         }
1330         
1331         if(this.alt !== false){
1332             cfg.alt = this.alt;
1333         }
1334         
1335         
1336         if(this.target !== false) {
1337             cfg.target = this.target;
1338         }
1339         
1340         return cfg;
1341     },
1342     
1343     initEvents: function() {
1344         
1345         if(!this.href || this.preventDefault){
1346             this.el.on('click', this.onClick, this);
1347         }
1348     },
1349     
1350     onClick : function(e)
1351     {
1352         if(this.preventDefault){
1353             e.preventDefault();
1354         }
1355         //Roo.log('img onclick');
1356         this.fireEvent('click', this, e);
1357     }
1358    
1359 });
1360
1361  /*
1362  * - LGPL
1363  *
1364  * header
1365  * 
1366  */
1367
1368 /**
1369  * @class Roo.bootstrap.Header
1370  * @extends Roo.bootstrap.Component
1371  * Bootstrap Header class
1372  * @cfg {String} html content of header
1373  * @cfg {Number} level (1|2|3|4|5|6) default 1
1374  * 
1375  * @constructor
1376  * Create a new Header
1377  * @param {Object} config The config object
1378  */
1379
1380
1381 Roo.bootstrap.Header  = function(config){
1382     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1383 };
1384
1385 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1386     
1387     //href : false,
1388     html : false,
1389     level : 1,
1390     
1391     
1392     
1393     getAutoCreate : function(){
1394         
1395         
1396         
1397         var cfg = {
1398             tag: 'h' + (1 *this.level),
1399             html: this.html || ''
1400         } ;
1401         
1402         return cfg;
1403     }
1404    
1405 });
1406
1407  
1408
1409  /*
1410  * Based on:
1411  * Ext JS Library 1.1.1
1412  * Copyright(c) 2006-2007, Ext JS, LLC.
1413  *
1414  * Originally Released Under LGPL - original licence link has changed is not relivant.
1415  *
1416  * Fork - LGPL
1417  * <script type="text/javascript">
1418  */
1419  
1420 /**
1421  * @class Roo.bootstrap.MenuMgr
1422  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1423  * @singleton
1424  */
1425 Roo.bootstrap.MenuMgr = function(){
1426    var menus, active, groups = {}, attached = false, lastShow = new Date();
1427
1428    // private - called when first menu is created
1429    function init(){
1430        menus = {};
1431        active = new Roo.util.MixedCollection();
1432        Roo.get(document).addKeyListener(27, function(){
1433            if(active.length > 0){
1434                hideAll();
1435            }
1436        });
1437    }
1438
1439    // private
1440    function hideAll(){
1441        if(active && active.length > 0){
1442            var c = active.clone();
1443            c.each(function(m){
1444                m.hide();
1445            });
1446        }
1447    }
1448
1449    // private
1450    function onHide(m){
1451        active.remove(m);
1452        if(active.length < 1){
1453            Roo.get(document).un("mouseup", onMouseDown);
1454             
1455            attached = false;
1456        }
1457    }
1458
1459    // private
1460    function onShow(m){
1461        var last = active.last();
1462        lastShow = new Date();
1463        active.add(m);
1464        if(!attached){
1465           Roo.get(document).on("mouseup", onMouseDown);
1466            
1467            attached = true;
1468        }
1469        if(m.parentMenu){
1470           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1471           m.parentMenu.activeChild = m;
1472        }else if(last && last.isVisible()){
1473           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1474        }
1475    }
1476
1477    // private
1478    function onBeforeHide(m){
1479        if(m.activeChild){
1480            m.activeChild.hide();
1481        }
1482        if(m.autoHideTimer){
1483            clearTimeout(m.autoHideTimer);
1484            delete m.autoHideTimer;
1485        }
1486    }
1487
1488    // private
1489    function onBeforeShow(m){
1490        var pm = m.parentMenu;
1491        if(!pm && !m.allowOtherMenus){
1492            hideAll();
1493        }else if(pm && pm.activeChild && active != m){
1494            pm.activeChild.hide();
1495        }
1496    }
1497
1498    // private
1499    function onMouseDown(e){
1500         Roo.log("on MouseDown");
1501         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1502            hideAll();
1503         }
1504         
1505         
1506    }
1507
1508    // private
1509    function onBeforeCheck(mi, state){
1510        if(state){
1511            var g = groups[mi.group];
1512            for(var i = 0, l = g.length; i < l; i++){
1513                if(g[i] != mi){
1514                    g[i].setChecked(false);
1515                }
1516            }
1517        }
1518    }
1519
1520    return {
1521
1522        /**
1523         * Hides all menus that are currently visible
1524         */
1525        hideAll : function(){
1526             hideAll();  
1527        },
1528
1529        // private
1530        register : function(menu){
1531            if(!menus){
1532                init();
1533            }
1534            menus[menu.id] = menu;
1535            menu.on("beforehide", onBeforeHide);
1536            menu.on("hide", onHide);
1537            menu.on("beforeshow", onBeforeShow);
1538            menu.on("show", onShow);
1539            var g = menu.group;
1540            if(g && menu.events["checkchange"]){
1541                if(!groups[g]){
1542                    groups[g] = [];
1543                }
1544                groups[g].push(menu);
1545                menu.on("checkchange", onCheck);
1546            }
1547        },
1548
1549         /**
1550          * Returns a {@link Roo.menu.Menu} object
1551          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1552          * be used to generate and return a new Menu instance.
1553          */
1554        get : function(menu){
1555            if(typeof menu == "string"){ // menu id
1556                return menus[menu];
1557            }else if(menu.events){  // menu instance
1558                return menu;
1559            }
1560            /*else if(typeof menu.length == 'number'){ // array of menu items?
1561                return new Roo.bootstrap.Menu({items:menu});
1562            }else{ // otherwise, must be a config
1563                return new Roo.bootstrap.Menu(menu);
1564            }
1565            */
1566            return false;
1567        },
1568
1569        // private
1570        unregister : function(menu){
1571            delete menus[menu.id];
1572            menu.un("beforehide", onBeforeHide);
1573            menu.un("hide", onHide);
1574            menu.un("beforeshow", onBeforeShow);
1575            menu.un("show", onShow);
1576            var g = menu.group;
1577            if(g && menu.events["checkchange"]){
1578                groups[g].remove(menu);
1579                menu.un("checkchange", onCheck);
1580            }
1581        },
1582
1583        // private
1584        registerCheckable : function(menuItem){
1585            var g = menuItem.group;
1586            if(g){
1587                if(!groups[g]){
1588                    groups[g] = [];
1589                }
1590                groups[g].push(menuItem);
1591                menuItem.on("beforecheckchange", onBeforeCheck);
1592            }
1593        },
1594
1595        // private
1596        unregisterCheckable : function(menuItem){
1597            var g = menuItem.group;
1598            if(g){
1599                groups[g].remove(menuItem);
1600                menuItem.un("beforecheckchange", onBeforeCheck);
1601            }
1602        }
1603    };
1604 }();/*
1605  * - LGPL
1606  *
1607  * menu
1608  * 
1609  */
1610
1611 /**
1612  * @class Roo.bootstrap.Menu
1613  * @extends Roo.bootstrap.Component
1614  * Bootstrap Menu class - container for MenuItems
1615  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1616  * 
1617  * @constructor
1618  * Create a new Menu
1619  * @param {Object} config The config object
1620  */
1621
1622
1623 Roo.bootstrap.Menu = function(config){
1624     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1625     if (this.registerMenu) {
1626         Roo.bootstrap.MenuMgr.register(this);
1627     }
1628     this.addEvents({
1629         /**
1630          * @event beforeshow
1631          * Fires before this menu is displayed
1632          * @param {Roo.menu.Menu} this
1633          */
1634         beforeshow : true,
1635         /**
1636          * @event beforehide
1637          * Fires before this menu is hidden
1638          * @param {Roo.menu.Menu} this
1639          */
1640         beforehide : true,
1641         /**
1642          * @event show
1643          * Fires after this menu is displayed
1644          * @param {Roo.menu.Menu} this
1645          */
1646         show : true,
1647         /**
1648          * @event hide
1649          * Fires after this menu is hidden
1650          * @param {Roo.menu.Menu} this
1651          */
1652         hide : true,
1653         /**
1654          * @event click
1655          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1656          * @param {Roo.menu.Menu} this
1657          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1658          * @param {Roo.EventObject} e
1659          */
1660         click : true,
1661         /**
1662          * @event mouseover
1663          * Fires when the mouse is hovering over this menu
1664          * @param {Roo.menu.Menu} this
1665          * @param {Roo.EventObject} e
1666          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1667          */
1668         mouseover : true,
1669         /**
1670          * @event mouseout
1671          * Fires when the mouse exits this menu
1672          * @param {Roo.menu.Menu} this
1673          * @param {Roo.EventObject} e
1674          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1675          */
1676         mouseout : true,
1677         /**
1678          * @event itemclick
1679          * Fires when a menu item contained in this menu is clicked
1680          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1681          * @param {Roo.EventObject} e
1682          */
1683         itemclick: true
1684     });
1685     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1686 };
1687
1688 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1689     
1690    /// html : false,
1691     //align : '',
1692     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1693     type: false,
1694     /**
1695      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1696      */
1697     registerMenu : true,
1698     
1699     menuItems :false, // stores the menu items..
1700     
1701     hidden:true,
1702     
1703     parentMenu : false,
1704     
1705     getChildContainer : function() {
1706         return this.el;  
1707     },
1708     
1709     getAutoCreate : function(){
1710          
1711         //if (['right'].indexOf(this.align)!==-1) {
1712         //    cfg.cn[1].cls += ' pull-right'
1713         //}
1714         
1715         
1716         var cfg = {
1717             tag : 'ul',
1718             cls : 'dropdown-menu' ,
1719             style : 'z-index:1000'
1720             
1721         }
1722         
1723         if (this.type === 'submenu') {
1724             cfg.cls = 'submenu active';
1725         }
1726         if (this.type === 'treeview') {
1727             cfg.cls = 'treeview-menu';
1728         }
1729         
1730         return cfg;
1731     },
1732     initEvents : function() {
1733         
1734        // Roo.log("ADD event");
1735        // Roo.log(this.triggerEl.dom);
1736         this.triggerEl.on('click', this.onTriggerPress, this);
1737         this.triggerEl.addClass('dropdown-toggle');
1738         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1739
1740         this.el.on("mouseover", this.onMouseOver, this);
1741         this.el.on("mouseout", this.onMouseOut, this);
1742         
1743         
1744     },
1745     findTargetItem : function(e){
1746         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1747         if(!t){
1748             return false;
1749         }
1750         //Roo.log(t);         Roo.log(t.id);
1751         if(t && t.id){
1752             //Roo.log(this.menuitems);
1753             return this.menuitems.get(t.id);
1754             
1755             //return this.items.get(t.menuItemId);
1756         }
1757         
1758         return false;
1759     },
1760     onClick : function(e){
1761         Roo.log("menu.onClick");
1762         var t = this.findTargetItem(e);
1763         if(!t || t.isContainer){
1764             return;
1765         }
1766         Roo.log(e);
1767         /*
1768         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1769             if(t == this.activeItem && t.shouldDeactivate(e)){
1770                 this.activeItem.deactivate();
1771                 delete this.activeItem;
1772                 return;
1773             }
1774             if(t.canActivate){
1775                 this.setActiveItem(t, true);
1776             }
1777             return;
1778             
1779             
1780         }
1781         */
1782        
1783         Roo.log('pass click event');
1784         
1785         t.onClick(e);
1786         
1787         this.fireEvent("click", this, t, e);
1788         
1789         this.hide();
1790     },
1791      onMouseOver : function(e){
1792         var t  = this.findTargetItem(e);
1793         //Roo.log(t);
1794         //if(t){
1795         //    if(t.canActivate && !t.disabled){
1796         //        this.setActiveItem(t, true);
1797         //    }
1798         //}
1799         
1800         this.fireEvent("mouseover", this, e, t);
1801     },
1802     isVisible : function(){
1803         return !this.hidden;
1804     },
1805      onMouseOut : function(e){
1806         var t  = this.findTargetItem(e);
1807         
1808         //if(t ){
1809         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1810         //        this.activeItem.deactivate();
1811         //        delete this.activeItem;
1812         //    }
1813         //}
1814         this.fireEvent("mouseout", this, e, t);
1815     },
1816     
1817     
1818     /**
1819      * Displays this menu relative to another element
1820      * @param {String/HTMLElement/Roo.Element} element The element to align to
1821      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1822      * the element (defaults to this.defaultAlign)
1823      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1824      */
1825     show : function(el, pos, parentMenu){
1826         this.parentMenu = parentMenu;
1827         if(!this.el){
1828             this.render();
1829         }
1830         this.fireEvent("beforeshow", this);
1831         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1832     },
1833      /**
1834      * Displays this menu at a specific xy position
1835      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1836      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1837      */
1838     showAt : function(xy, parentMenu, /* private: */_e){
1839         this.parentMenu = parentMenu;
1840         if(!this.el){
1841             this.render();
1842         }
1843         if(_e !== false){
1844             this.fireEvent("beforeshow", this);
1845             //xy = this.el.adjustForConstraints(xy);
1846         }
1847         
1848         //this.el.show();
1849         this.hideMenuItems();
1850         this.hidden = false;
1851         this.triggerEl.addClass('open');
1852         
1853         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
1854             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
1855         }
1856         
1857         this.el.setXY(xy);
1858         this.focus();
1859         this.fireEvent("show", this);
1860     },
1861     
1862     focus : function(){
1863         return;
1864         if(!this.hidden){
1865             this.doFocus.defer(50, this);
1866         }
1867     },
1868
1869     doFocus : function(){
1870         if(!this.hidden){
1871             this.focusEl.focus();
1872         }
1873     },
1874
1875     /**
1876      * Hides this menu and optionally all parent menus
1877      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1878      */
1879     hide : function(deep){
1880         
1881         this.hideMenuItems();
1882         if(this.el && this.isVisible()){
1883             this.fireEvent("beforehide", this);
1884             if(this.activeItem){
1885                 this.activeItem.deactivate();
1886                 this.activeItem = null;
1887             }
1888             this.triggerEl.removeClass('open');;
1889             this.hidden = true;
1890             this.fireEvent("hide", this);
1891         }
1892         if(deep === true && this.parentMenu){
1893             this.parentMenu.hide(true);
1894         }
1895     },
1896     
1897     onTriggerPress  : function(e)
1898     {
1899         
1900         Roo.log('trigger press');
1901         //Roo.log(e.getTarget());
1902        // Roo.log(this.triggerEl.dom);
1903         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1904             return;
1905         }
1906         if (this.isVisible()) {
1907             Roo.log('hide');
1908             this.hide();
1909         } else {
1910             this.show(this.triggerEl, false, false);
1911         }
1912         
1913         
1914     },
1915     
1916          
1917        
1918     
1919     hideMenuItems : function()
1920     {
1921         //$(backdrop).remove()
1922         Roo.select('.open',true).each(function(aa) {
1923             
1924             aa.removeClass('open');
1925           //var parent = getParent($(this))
1926           //var relatedTarget = { relatedTarget: this }
1927           
1928            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1929           //if (e.isDefaultPrevented()) return
1930            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1931         })
1932     },
1933     addxtypeChild : function (tree, cntr) {
1934         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1935           
1936         this.menuitems.add(comp);
1937         return comp;
1938
1939     },
1940     getEl : function()
1941     {
1942         Roo.log(this.el);
1943         return this.el;
1944     }
1945 });
1946
1947  
1948  /*
1949  * - LGPL
1950  *
1951  * menu item
1952  * 
1953  */
1954
1955
1956 /**
1957  * @class Roo.bootstrap.MenuItem
1958  * @extends Roo.bootstrap.Component
1959  * Bootstrap MenuItem class
1960  * @cfg {String} html the menu label
1961  * @cfg {String} href the link
1962  * @cfg {Boolean} preventDefault (true | false) default true
1963  * @cfg {Boolean} isContainer (true | false) default false
1964  * 
1965  * 
1966  * @constructor
1967  * Create a new MenuItem
1968  * @param {Object} config The config object
1969  */
1970
1971
1972 Roo.bootstrap.MenuItem = function(config){
1973     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1974     this.addEvents({
1975         // raw events
1976         /**
1977          * @event click
1978          * The raw click event for the entire grid.
1979          * @param {Roo.bootstrap.MenuItem} this
1980          * @param {Roo.EventObject} e
1981          */
1982         "click" : true
1983     });
1984 };
1985
1986 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1987     
1988     href : false,
1989     html : false,
1990     preventDefault: true,
1991     isContainer : false,
1992     
1993     getAutoCreate : function(){
1994         
1995         if(this.isContainer){
1996             return {
1997                 tag: 'li',
1998                 cls: 'dropdown-menu-item'
1999             };
2000         }
2001         
2002         var cfg= {
2003             tag: 'li',
2004             cls: 'dropdown-menu-item',
2005             cn: [
2006                     {
2007                         tag : 'a',
2008                         href : '#',
2009                         html : 'Link'
2010                     }
2011                 ]
2012         };
2013         if (this.parent().type == 'treeview') {
2014             cfg.cls = 'treeview-menu';
2015         }
2016         
2017         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2018         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2019         return cfg;
2020     },
2021     
2022     initEvents: function() {
2023         
2024         //this.el.select('a').on('click', this.onClick, this);
2025         
2026     },
2027     onClick : function(e)
2028     {
2029         Roo.log('item on click ');
2030         //if(this.preventDefault){
2031         //    e.preventDefault();
2032         //}
2033         //this.parent().hideMenuItems();
2034         
2035         this.fireEvent('click', this, e);
2036     },
2037     getEl : function()
2038     {
2039         return this.el;
2040     }
2041 });
2042
2043  
2044
2045  /*
2046  * - LGPL
2047  *
2048  * menu separator
2049  * 
2050  */
2051
2052
2053 /**
2054  * @class Roo.bootstrap.MenuSeparator
2055  * @extends Roo.bootstrap.Component
2056  * Bootstrap MenuSeparator class
2057  * 
2058  * @constructor
2059  * Create a new MenuItem
2060  * @param {Object} config The config object
2061  */
2062
2063
2064 Roo.bootstrap.MenuSeparator = function(config){
2065     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2066 };
2067
2068 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2069     
2070     getAutoCreate : function(){
2071         var cfg = {
2072             cls: 'divider',
2073             tag : 'li'
2074         };
2075         
2076         return cfg;
2077     }
2078    
2079 });
2080
2081  
2082
2083  
2084 /*
2085 * Licence: LGPL
2086 */
2087
2088 /**
2089  * @class Roo.bootstrap.Modal
2090  * @extends Roo.bootstrap.Component
2091  * Bootstrap Modal class
2092  * @cfg {String} title Title of dialog
2093  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2094  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2095  * @cfg {Boolean} specificTitle default false
2096  * @cfg {Array} buttons Array of buttons or standard button set..
2097  * @cfg {String} buttonPosition (left|right|center) default right
2098  * @cfg {Boolean} animate default true
2099  * @cfg {Boolean} allow_close default true
2100  * 
2101  * @constructor
2102  * Create a new Modal Dialog
2103  * @param {Object} config The config object
2104  */
2105
2106 Roo.bootstrap.Modal = function(config){
2107     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2108     this.addEvents({
2109         // raw events
2110         /**
2111          * @event btnclick
2112          * The raw btnclick event for the button
2113          * @param {Roo.EventObject} e
2114          */
2115         "btnclick" : true
2116     });
2117     this.buttons = this.buttons || [];
2118      
2119     if (this.tmpl) {
2120         this.tmpl = Roo.factory(this.tmpl);
2121     }
2122     
2123 };
2124
2125 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2126     
2127     title : 'test dialog',
2128    
2129     buttons : false,
2130     
2131     // set on load...
2132      
2133     html: false,
2134     
2135     tmp: false,
2136     
2137     specificTitle: false,
2138     
2139     buttonPosition: 'right',
2140     
2141     allow_close : true,
2142     
2143     animate : true,
2144     
2145     
2146      // private
2147     bodyEl:  false,
2148     footerEl:  false,
2149     titleEl:  false,
2150     closeEl:  false,
2151     
2152     
2153     onRender : function(ct, position)
2154     {
2155         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2156      
2157         if(!this.el){
2158             var cfg = Roo.apply({},  this.getAutoCreate());
2159             cfg.id = Roo.id();
2160             //if(!cfg.name){
2161             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2162             //}
2163             //if (!cfg.name.length) {
2164             //    delete cfg.name;
2165            // }
2166             if (this.cls) {
2167                 cfg.cls += ' ' + this.cls;
2168             }
2169             if (this.style) {
2170                 cfg.style = this.style;
2171             }
2172             this.el = Roo.get(document.body).createChild(cfg, position);
2173         }
2174         //var type = this.el.dom.type;
2175         
2176         
2177         
2178         
2179         if(this.tabIndex !== undefined){
2180             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2181         }
2182         
2183         
2184         this.bodyEl = this.el.select('.modal-body',true).first();
2185         this.closeEl = this.el.select('.modal-header .close', true).first();
2186         this.footerEl = this.el.select('.modal-footer',true).first();
2187         this.titleEl = this.el.select('.modal-title',true).first();
2188         
2189         
2190          
2191         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2192         this.maskEl.enableDisplayMode("block");
2193         this.maskEl.hide();
2194         //this.el.addClass("x-dlg-modal");
2195     
2196         if (this.buttons.length) {
2197             Roo.each(this.buttons, function(bb) {
2198                 b = Roo.apply({}, bb);
2199                 b.xns = b.xns || Roo.bootstrap;
2200                 b.xtype = b.xtype || 'Button';
2201                 if (typeof(b.listeners) == 'undefined') {
2202                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2203                 }
2204                 
2205                 var btn = Roo.factory(b);
2206                 
2207                 btn.onRender(this.el.select('.modal-footer div').first());
2208                 
2209             },this);
2210         }
2211         // render the children.
2212         var nitems = [];
2213         
2214         if(typeof(this.items) != 'undefined'){
2215             var items = this.items;
2216             delete this.items;
2217
2218             for(var i =0;i < items.length;i++) {
2219                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2220             }
2221         }
2222         
2223         this.items = nitems;
2224         
2225         // where are these used - they used to be body/close/footer
2226         
2227        
2228         this.initEvents();
2229         //this.el.addClass([this.fieldClass, this.cls]);
2230         
2231     },
2232     getAutoCreate : function(){
2233         
2234         
2235         var bdy = {
2236                 cls : 'modal-body',
2237                 html : this.html || ''
2238         };
2239         
2240         var title = {
2241             tag: 'h4',
2242             cls : 'modal-title',
2243             html : this.title
2244         };
2245         
2246         if(this.specificTitle){
2247             title = this.title;
2248             
2249         };
2250         
2251         var header = [];
2252         if (this.allow_close) {
2253             header.push({
2254                 tag: 'button',
2255                 cls : 'close',
2256                 html : '&times'
2257             });
2258         }
2259         header.push(title);
2260         
2261         var modal = {
2262             cls: "modal",
2263             style : 'display: none',
2264             cn : [
2265                 {
2266                     cls: "modal-dialog",
2267                     cn : [
2268                         {
2269                             cls : "modal-content",
2270                             cn : [
2271                                 {
2272                                     cls : 'modal-header',
2273                                     cn : header
2274                                 },
2275                                 bdy,
2276                                 {
2277                                     cls : 'modal-footer',
2278                                     cn : [
2279                                         {
2280                                             tag: 'div',
2281                                             cls: 'btn-' + this.buttonPosition
2282                                         }
2283                                     ]
2284                                     
2285                                 }
2286                                 
2287                                 
2288                             ]
2289                             
2290                         }
2291                     ]
2292                         
2293                 }
2294             ]
2295         };
2296         
2297         if(this.animate){
2298             modal.cls += ' fade';
2299         }
2300         
2301         return modal;
2302           
2303     },
2304     getChildContainer : function() {
2305          
2306          return this.bodyEl;
2307         
2308     },
2309     getButtonContainer : function() {
2310          return this.el.select('.modal-footer div',true).first();
2311         
2312     },
2313     initEvents : function()
2314     {
2315         if (this.allow_close) {
2316             this.closeEl.on('click', this.hide, this);
2317         }
2318
2319     },
2320     show : function() {
2321         
2322         if (!this.rendered) {
2323             this.render();
2324         }
2325         
2326         this.el.setStyle('display', 'block');
2327         
2328         if(this.animate){
2329             var _this = this;
2330             (function(){ _this.el.addClass('in'); }).defer(50);
2331         }else{
2332             this.el.addClass('in');
2333         }
2334         
2335         // not sure how we can show data in here.. 
2336         //if (this.tmpl) {
2337         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2338         //}
2339         
2340         Roo.get(document.body).addClass("x-body-masked");
2341         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2342         this.maskEl.show();
2343         this.el.setStyle('zIndex', '10001');
2344        
2345         this.fireEvent('show', this);
2346         
2347         
2348     },
2349     hide : function()
2350     {
2351         this.maskEl.hide();
2352         Roo.get(document.body).removeClass("x-body-masked");
2353         this.el.removeClass('in');
2354         
2355         if(this.animate){
2356             var _this = this;
2357             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2358         }else{
2359             this.el.setStyle('display', 'none');
2360         }
2361         
2362         this.fireEvent('hide', this);
2363     },
2364     
2365     addButton : function(str, cb)
2366     {
2367          
2368         
2369         var b = Roo.apply({}, { html : str } );
2370         b.xns = b.xns || Roo.bootstrap;
2371         b.xtype = b.xtype || 'Button';
2372         if (typeof(b.listeners) == 'undefined') {
2373             b.listeners = { click : cb.createDelegate(this)  };
2374         }
2375         
2376         var btn = Roo.factory(b);
2377            
2378         btn.onRender(this.el.select('.modal-footer div').first());
2379         
2380         return btn;   
2381        
2382     },
2383     
2384     setDefaultButton : function(btn)
2385     {
2386         //this.el.select('.modal-footer').()
2387     },
2388     resizeTo: function(w,h)
2389     {
2390         // skip..
2391     },
2392     setContentSize  : function(w, h)
2393     {
2394         
2395     },
2396     onButtonClick: function(btn,e)
2397     {
2398         //Roo.log([a,b,c]);
2399         this.fireEvent('btnclick', btn.name, e);
2400     },
2401      /**
2402      * Set the title of the Dialog
2403      * @param {String} str new Title
2404      */
2405     setTitle: function(str) {
2406         this.titleEl.dom.innerHTML = str;    
2407     },
2408     /**
2409      * Set the body of the Dialog
2410      * @param {String} str new Title
2411      */
2412     setBody: function(str) {
2413         this.bodyEl.dom.innerHTML = str;    
2414     },
2415     /**
2416      * Set the body of the Dialog using the template
2417      * @param {Obj} data - apply this data to the template and replace the body contents.
2418      */
2419     applyBody: function(obj)
2420     {
2421         if (!this.tmpl) {
2422             Roo.log("Error - using apply Body without a template");
2423             //code
2424         }
2425         this.tmpl.overwrite(this.bodyEl, obj);
2426     }
2427     
2428 });
2429
2430
2431 Roo.apply(Roo.bootstrap.Modal,  {
2432     /**
2433          * Button config that displays a single OK button
2434          * @type Object
2435          */
2436         OK :  [{
2437             name : 'ok',
2438             weight : 'primary',
2439             html : 'OK'
2440         }], 
2441         /**
2442          * Button config that displays Yes and No buttons
2443          * @type Object
2444          */
2445         YESNO : [
2446             {
2447                 name  : 'no',
2448                 html : 'No'
2449             },
2450             {
2451                 name  :'yes',
2452                 weight : 'primary',
2453                 html : 'Yes'
2454             }
2455         ],
2456         
2457         /**
2458          * Button config that displays OK and Cancel buttons
2459          * @type Object
2460          */
2461         OKCANCEL : [
2462             {
2463                name : 'cancel',
2464                 html : 'Cancel'
2465             },
2466             {
2467                 name : 'ok',
2468                 weight : 'primary',
2469                 html : 'OK'
2470             }
2471         ],
2472         /**
2473          * Button config that displays Yes, No and Cancel buttons
2474          * @type Object
2475          */
2476         YESNOCANCEL : [
2477             {
2478                 name : 'yes',
2479                 weight : 'primary',
2480                 html : 'Yes'
2481             },
2482             {
2483                 name : 'no',
2484                 html : 'No'
2485             },
2486             {
2487                 name : 'cancel',
2488                 html : 'Cancel'
2489             }
2490         ]
2491 });
2492  
2493  /*
2494  * - LGPL
2495  *
2496  * messagebox - can be used as a replace
2497  * 
2498  */
2499 /**
2500  * @class Roo.MessageBox
2501  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2502  * Example usage:
2503  *<pre><code>
2504 // Basic alert:
2505 Roo.Msg.alert('Status', 'Changes saved successfully.');
2506
2507 // Prompt for user data:
2508 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2509     if (btn == 'ok'){
2510         // process text value...
2511     }
2512 });
2513
2514 // Show a dialog using config options:
2515 Roo.Msg.show({
2516    title:'Save Changes?',
2517    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2518    buttons: Roo.Msg.YESNOCANCEL,
2519    fn: processResult,
2520    animEl: 'elId'
2521 });
2522 </code></pre>
2523  * @singleton
2524  */
2525 Roo.bootstrap.MessageBox = function(){
2526     var dlg, opt, mask, waitTimer;
2527     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2528     var buttons, activeTextEl, bwidth;
2529
2530     
2531     // private
2532     var handleButton = function(button){
2533         dlg.hide();
2534         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2535     };
2536
2537     // private
2538     var handleHide = function(){
2539         if(opt && opt.cls){
2540             dlg.el.removeClass(opt.cls);
2541         }
2542         //if(waitTimer){
2543         //    Roo.TaskMgr.stop(waitTimer);
2544         //    waitTimer = null;
2545         //}
2546     };
2547
2548     // private
2549     var updateButtons = function(b){
2550         var width = 0;
2551         if(!b){
2552             buttons["ok"].hide();
2553             buttons["cancel"].hide();
2554             buttons["yes"].hide();
2555             buttons["no"].hide();
2556             //dlg.footer.dom.style.display = 'none';
2557             return width;
2558         }
2559         dlg.footerEl.dom.style.display = '';
2560         for(var k in buttons){
2561             if(typeof buttons[k] != "function"){
2562                 if(b[k]){
2563                     buttons[k].show();
2564                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2565                     width += buttons[k].el.getWidth()+15;
2566                 }else{
2567                     buttons[k].hide();
2568                 }
2569             }
2570         }
2571         return width;
2572     };
2573
2574     // private
2575     var handleEsc = function(d, k, e){
2576         if(opt && opt.closable !== false){
2577             dlg.hide();
2578         }
2579         if(e){
2580             e.stopEvent();
2581         }
2582     };
2583
2584     return {
2585         /**
2586          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2587          * @return {Roo.BasicDialog} The BasicDialog element
2588          */
2589         getDialog : function(){
2590            if(!dlg){
2591                 dlg = new Roo.bootstrap.Modal( {
2592                     //draggable: true,
2593                     //resizable:false,
2594                     //constraintoviewport:false,
2595                     //fixedcenter:true,
2596                     //collapsible : false,
2597                     //shim:true,
2598                     //modal: true,
2599                   //  width:400,
2600                   //  height:100,
2601                     //buttonAlign:"center",
2602                     closeClick : function(){
2603                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2604                             handleButton("no");
2605                         }else{
2606                             handleButton("cancel");
2607                         }
2608                     }
2609                 });
2610                 dlg.render();
2611                 dlg.on("hide", handleHide);
2612                 mask = dlg.mask;
2613                 //dlg.addKeyListener(27, handleEsc);
2614                 buttons = {};
2615                 this.buttons = buttons;
2616                 var bt = this.buttonText;
2617                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2618                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2619                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2620                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2621                 Roo.log(buttons)
2622                 bodyEl = dlg.bodyEl.createChild({
2623
2624                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2625                         '<textarea class="roo-mb-textarea"></textarea>' +
2626                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2627                 });
2628                 msgEl = bodyEl.dom.firstChild;
2629                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2630                 textboxEl.enableDisplayMode();
2631                 textboxEl.addKeyListener([10,13], function(){
2632                     if(dlg.isVisible() && opt && opt.buttons){
2633                         if(opt.buttons.ok){
2634                             handleButton("ok");
2635                         }else if(opt.buttons.yes){
2636                             handleButton("yes");
2637                         }
2638                     }
2639                 });
2640                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2641                 textareaEl.enableDisplayMode();
2642                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2643                 progressEl.enableDisplayMode();
2644                 var pf = progressEl.dom.firstChild;
2645                 if (pf) {
2646                     pp = Roo.get(pf.firstChild);
2647                     pp.setHeight(pf.offsetHeight);
2648                 }
2649                 
2650             }
2651             return dlg;
2652         },
2653
2654         /**
2655          * Updates the message box body text
2656          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2657          * the XHTML-compliant non-breaking space character '&amp;#160;')
2658          * @return {Roo.MessageBox} This message box
2659          */
2660         updateText : function(text){
2661             if(!dlg.isVisible() && !opt.width){
2662                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2663             }
2664             msgEl.innerHTML = text || '&#160;';
2665       
2666             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2667             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2668             var w = Math.max(
2669                     Math.min(opt.width || cw , this.maxWidth), 
2670                     Math.max(opt.minWidth || this.minWidth, bwidth)
2671             );
2672             if(opt.prompt){
2673                 activeTextEl.setWidth(w);
2674             }
2675             if(dlg.isVisible()){
2676                 dlg.fixedcenter = false;
2677             }
2678             // to big, make it scroll. = But as usual stupid IE does not support
2679             // !important..
2680             
2681             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2682                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2683                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2684             } else {
2685                 bodyEl.dom.style.height = '';
2686                 bodyEl.dom.style.overflowY = '';
2687             }
2688             if (cw > w) {
2689                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2690             } else {
2691                 bodyEl.dom.style.overflowX = '';
2692             }
2693             
2694             dlg.setContentSize(w, bodyEl.getHeight());
2695             if(dlg.isVisible()){
2696                 dlg.fixedcenter = true;
2697             }
2698             return this;
2699         },
2700
2701         /**
2702          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2703          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2704          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2705          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2706          * @return {Roo.MessageBox} This message box
2707          */
2708         updateProgress : function(value, text){
2709             if(text){
2710                 this.updateText(text);
2711             }
2712             if (pp) { // weird bug on my firefox - for some reason this is not defined
2713                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2714             }
2715             return this;
2716         },        
2717
2718         /**
2719          * Returns true if the message box is currently displayed
2720          * @return {Boolean} True if the message box is visible, else false
2721          */
2722         isVisible : function(){
2723             return dlg && dlg.isVisible();  
2724         },
2725
2726         /**
2727          * Hides the message box if it is displayed
2728          */
2729         hide : function(){
2730             if(this.isVisible()){
2731                 dlg.hide();
2732             }  
2733         },
2734
2735         /**
2736          * Displays a new message box, or reinitializes an existing message box, based on the config options
2737          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2738          * The following config object properties are supported:
2739          * <pre>
2740 Property    Type             Description
2741 ----------  ---------------  ------------------------------------------------------------------------------------
2742 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2743                                    closes (defaults to undefined)
2744 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2745                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2746 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2747                                    progress and wait dialogs will ignore this property and always hide the
2748                                    close button as they can only be closed programmatically.
2749 cls               String           A custom CSS class to apply to the message box element
2750 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2751                                    displayed (defaults to 75)
2752 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2753                                    function will be btn (the name of the button that was clicked, if applicable,
2754                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2755                                    Progress and wait dialogs will ignore this option since they do not respond to
2756                                    user actions and can only be closed programmatically, so any required function
2757                                    should be called by the same code after it closes the dialog.
2758 icon              String           A CSS class that provides a background image to be used as an icon for
2759                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2760 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2761 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2762 modal             Boolean          False to allow user interaction with the page while the message box is
2763                                    displayed (defaults to true)
2764 msg               String           A string that will replace the existing message box body text (defaults
2765                                    to the XHTML-compliant non-breaking space character '&#160;')
2766 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2767 progress          Boolean          True to display a progress bar (defaults to false)
2768 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2769 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2770 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2771 title             String           The title text
2772 value             String           The string value to set into the active textbox element if displayed
2773 wait              Boolean          True to display a progress bar (defaults to false)
2774 width             Number           The width of the dialog in pixels
2775 </pre>
2776          *
2777          * Example usage:
2778          * <pre><code>
2779 Roo.Msg.show({
2780    title: 'Address',
2781    msg: 'Please enter your address:',
2782    width: 300,
2783    buttons: Roo.MessageBox.OKCANCEL,
2784    multiline: true,
2785    fn: saveAddress,
2786    animEl: 'addAddressBtn'
2787 });
2788 </code></pre>
2789          * @param {Object} config Configuration options
2790          * @return {Roo.MessageBox} This message box
2791          */
2792         show : function(options)
2793         {
2794             
2795             // this causes nightmares if you show one dialog after another
2796             // especially on callbacks..
2797              
2798             if(this.isVisible()){
2799                 
2800                 this.hide();
2801                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2802                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2803                 Roo.log("New Dialog Message:" +  options.msg )
2804                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2805                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2806                 
2807             }
2808             var d = this.getDialog();
2809             opt = options;
2810             d.setTitle(opt.title || "&#160;");
2811             d.closeEl.setDisplayed(opt.closable !== false);
2812             activeTextEl = textboxEl;
2813             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2814             if(opt.prompt){
2815                 if(opt.multiline){
2816                     textboxEl.hide();
2817                     textareaEl.show();
2818                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2819                         opt.multiline : this.defaultTextHeight);
2820                     activeTextEl = textareaEl;
2821                 }else{
2822                     textboxEl.show();
2823                     textareaEl.hide();
2824                 }
2825             }else{
2826                 textboxEl.hide();
2827                 textareaEl.hide();
2828             }
2829             progressEl.setDisplayed(opt.progress === true);
2830             this.updateProgress(0);
2831             activeTextEl.dom.value = opt.value || "";
2832             if(opt.prompt){
2833                 dlg.setDefaultButton(activeTextEl);
2834             }else{
2835                 var bs = opt.buttons;
2836                 var db = null;
2837                 if(bs && bs.ok){
2838                     db = buttons["ok"];
2839                 }else if(bs && bs.yes){
2840                     db = buttons["yes"];
2841                 }
2842                 dlg.setDefaultButton(db);
2843             }
2844             bwidth = updateButtons(opt.buttons);
2845             this.updateText(opt.msg);
2846             if(opt.cls){
2847                 d.el.addClass(opt.cls);
2848             }
2849             d.proxyDrag = opt.proxyDrag === true;
2850             d.modal = opt.modal !== false;
2851             d.mask = opt.modal !== false ? mask : false;
2852             if(!d.isVisible()){
2853                 // force it to the end of the z-index stack so it gets a cursor in FF
2854                 document.body.appendChild(dlg.el.dom);
2855                 d.animateTarget = null;
2856                 d.show(options.animEl);
2857             }
2858             return this;
2859         },
2860
2861         /**
2862          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2863          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2864          * and closing the message box when the process is complete.
2865          * @param {String} title The title bar text
2866          * @param {String} msg The message box body text
2867          * @return {Roo.MessageBox} This message box
2868          */
2869         progress : function(title, msg){
2870             this.show({
2871                 title : title,
2872                 msg : msg,
2873                 buttons: false,
2874                 progress:true,
2875                 closable:false,
2876                 minWidth: this.minProgressWidth,
2877                 modal : true
2878             });
2879             return this;
2880         },
2881
2882         /**
2883          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2884          * If a callback function is passed it will be called after the user clicks the button, and the
2885          * id of the button that was clicked will be passed as the only parameter to the callback
2886          * (could also be the top-right close button).
2887          * @param {String} title The title bar text
2888          * @param {String} msg The message box body text
2889          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2890          * @param {Object} scope (optional) The scope of the callback function
2891          * @return {Roo.MessageBox} This message box
2892          */
2893         alert : function(title, msg, fn, scope){
2894             this.show({
2895                 title : title,
2896                 msg : msg,
2897                 buttons: this.OK,
2898                 fn: fn,
2899                 scope : scope,
2900                 modal : true
2901             });
2902             return this;
2903         },
2904
2905         /**
2906          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2907          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2908          * You are responsible for closing the message box when the process is complete.
2909          * @param {String} msg The message box body text
2910          * @param {String} title (optional) The title bar text
2911          * @return {Roo.MessageBox} This message box
2912          */
2913         wait : function(msg, title){
2914             this.show({
2915                 title : title,
2916                 msg : msg,
2917                 buttons: false,
2918                 closable:false,
2919                 progress:true,
2920                 modal:true,
2921                 width:300,
2922                 wait:true
2923             });
2924             waitTimer = Roo.TaskMgr.start({
2925                 run: function(i){
2926                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2927                 },
2928                 interval: 1000
2929             });
2930             return this;
2931         },
2932
2933         /**
2934          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2935          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2936          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2937          * @param {String} title The title bar text
2938          * @param {String} msg The message box body text
2939          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2940          * @param {Object} scope (optional) The scope of the callback function
2941          * @return {Roo.MessageBox} This message box
2942          */
2943         confirm : function(title, msg, fn, scope){
2944             this.show({
2945                 title : title,
2946                 msg : msg,
2947                 buttons: this.YESNO,
2948                 fn: fn,
2949                 scope : scope,
2950                 modal : true
2951             });
2952             return this;
2953         },
2954
2955         /**
2956          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2957          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2958          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2959          * (could also be the top-right close button) and the text that was entered will be passed as the two
2960          * parameters to the callback.
2961          * @param {String} title The title bar text
2962          * @param {String} msg The message box body text
2963          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2964          * @param {Object} scope (optional) The scope of the callback function
2965          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2966          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2967          * @return {Roo.MessageBox} This message box
2968          */
2969         prompt : function(title, msg, fn, scope, multiline){
2970             this.show({
2971                 title : title,
2972                 msg : msg,
2973                 buttons: this.OKCANCEL,
2974                 fn: fn,
2975                 minWidth:250,
2976                 scope : scope,
2977                 prompt:true,
2978                 multiline: multiline,
2979                 modal : true
2980             });
2981             return this;
2982         },
2983
2984         /**
2985          * Button config that displays a single OK button
2986          * @type Object
2987          */
2988         OK : {ok:true},
2989         /**
2990          * Button config that displays Yes and No buttons
2991          * @type Object
2992          */
2993         YESNO : {yes:true, no:true},
2994         /**
2995          * Button config that displays OK and Cancel buttons
2996          * @type Object
2997          */
2998         OKCANCEL : {ok:true, cancel:true},
2999         /**
3000          * Button config that displays Yes, No and Cancel buttons
3001          * @type Object
3002          */
3003         YESNOCANCEL : {yes:true, no:true, cancel:true},
3004
3005         /**
3006          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3007          * @type Number
3008          */
3009         defaultTextHeight : 75,
3010         /**
3011          * The maximum width in pixels of the message box (defaults to 600)
3012          * @type Number
3013          */
3014         maxWidth : 600,
3015         /**
3016          * The minimum width in pixels of the message box (defaults to 100)
3017          * @type Number
3018          */
3019         minWidth : 100,
3020         /**
3021          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3022          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3023          * @type Number
3024          */
3025         minProgressWidth : 250,
3026         /**
3027          * An object containing the default button text strings that can be overriden for localized language support.
3028          * Supported properties are: ok, cancel, yes and no.
3029          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3030          * @type Object
3031          */
3032         buttonText : {
3033             ok : "OK",
3034             cancel : "Cancel",
3035             yes : "Yes",
3036             no : "No"
3037         }
3038     };
3039 }();
3040
3041 /**
3042  * Shorthand for {@link Roo.MessageBox}
3043  */
3044 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3045 Roo.Msg = Roo.Msg || Roo.MessageBox;
3046 /*
3047  * - LGPL
3048  *
3049  * navbar
3050  * 
3051  */
3052
3053 /**
3054  * @class Roo.bootstrap.Navbar
3055  * @extends Roo.bootstrap.Component
3056  * Bootstrap Navbar class
3057
3058  * @constructor
3059  * Create a new Navbar
3060  * @param {Object} config The config object
3061  */
3062
3063
3064 Roo.bootstrap.Navbar = function(config){
3065     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3066     
3067 };
3068
3069 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3070     
3071     
3072    
3073     // private
3074     navItems : false,
3075     loadMask : false,
3076     
3077     
3078     getAutoCreate : function(){
3079         
3080         
3081         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3082         
3083     },
3084     
3085     initEvents :function ()
3086     {
3087         //Roo.log(this.el.select('.navbar-toggle',true));
3088         this.el.select('.navbar-toggle',true).on('click', function() {
3089            // Roo.log('click');
3090             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3091         }, this);
3092         
3093         var mark = {
3094             tag: "div",
3095             cls:"x-dlg-mask"
3096         }
3097         
3098         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3099         
3100         var size = this.el.getSize();
3101         this.maskEl.setSize(size.width, size.height);
3102         this.maskEl.enableDisplayMode("block");
3103         this.maskEl.hide();
3104         
3105         if(this.loadMask){
3106             this.maskEl.show();
3107         }
3108     },
3109     
3110     
3111     getChildContainer : function()
3112     {
3113         if (this.el.select('.collapse').getCount()) {
3114             return this.el.select('.collapse',true).first();
3115         }
3116         
3117         return this.el;
3118     },
3119     
3120     mask : function()
3121     {
3122         this.maskEl.show();
3123     },
3124     
3125     unmask : function()
3126     {
3127         this.maskEl.hide();
3128     } 
3129     
3130     
3131     
3132     
3133 });
3134
3135
3136
3137  
3138
3139  /*
3140  * - LGPL
3141  *
3142  * navbar
3143  * 
3144  */
3145
3146 /**
3147  * @class Roo.bootstrap.NavSimplebar
3148  * @extends Roo.bootstrap.Navbar
3149  * Bootstrap Sidebar class
3150  *
3151  * @cfg {Boolean} inverse is inverted color
3152  * 
3153  * @cfg {String} type (nav | pills | tabs)
3154  * @cfg {Boolean} arrangement stacked | justified
3155  * @cfg {String} align (left | right) alignment
3156  * 
3157  * @cfg {Boolean} main (true|false) main nav bar? default false
3158  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3159  * 
3160  * @cfg {String} tag (header|footer|nav|div) default is nav 
3161
3162  * 
3163  * 
3164  * 
3165  * @constructor
3166  * Create a new Sidebar
3167  * @param {Object} config The config object
3168  */
3169
3170
3171 Roo.bootstrap.NavSimplebar = function(config){
3172     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3173 };
3174
3175 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3176     
3177     inverse: false,
3178     
3179     type: false,
3180     arrangement: '',
3181     align : false,
3182     
3183     
3184     
3185     main : false,
3186     
3187     
3188     tag : false,
3189     
3190     
3191     getAutoCreate : function(){
3192         
3193         
3194         var cfg = {
3195             tag : this.tag || 'div',
3196             cls : 'navbar'
3197         };
3198           
3199         
3200         cfg.cn = [
3201             {
3202                 cls: 'nav',
3203                 tag : 'ul'
3204             }
3205         ];
3206         
3207          
3208         this.type = this.type || 'nav';
3209         if (['tabs','pills'].indexOf(this.type)!==-1) {
3210             cfg.cn[0].cls += ' nav-' + this.type
3211         
3212         
3213         } else {
3214             if (this.type!=='nav') {
3215                 Roo.log('nav type must be nav/tabs/pills')
3216             }
3217             cfg.cn[0].cls += ' navbar-nav'
3218         }
3219         
3220         
3221         
3222         
3223         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3224             cfg.cn[0].cls += ' nav-' + this.arrangement;
3225         }
3226         
3227         
3228         if (this.align === 'right') {
3229             cfg.cn[0].cls += ' navbar-right';
3230         }
3231         
3232         if (this.inverse) {
3233             cfg.cls += ' navbar-inverse';
3234             
3235         }
3236         
3237         
3238         return cfg;
3239     
3240         
3241     }
3242     
3243     
3244     
3245 });
3246
3247
3248
3249  
3250
3251  
3252        /*
3253  * - LGPL
3254  *
3255  * navbar
3256  * 
3257  */
3258
3259 /**
3260  * @class Roo.bootstrap.NavHeaderbar
3261  * @extends Roo.bootstrap.NavSimplebar
3262  * Bootstrap Sidebar class
3263  *
3264  * @cfg {String} brand what is brand
3265  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3266  * @cfg {String} brand_href href of the brand
3267  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3268  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3269  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3270  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3271  * 
3272  * @constructor
3273  * Create a new Sidebar
3274  * @param {Object} config The config object
3275  */
3276
3277
3278 Roo.bootstrap.NavHeaderbar = function(config){
3279     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3280       
3281 };
3282
3283 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3284     
3285     position: '',
3286     brand: '',
3287     brand_href: false,
3288     srButton : true,
3289     autohide : false,
3290     desktopCenter : false,
3291    
3292     
3293     getAutoCreate : function(){
3294         
3295         var   cfg = {
3296             tag: this.nav || 'nav',
3297             cls: 'navbar',
3298             role: 'navigation',
3299             cn: []
3300         };
3301         
3302         var cn = cfg.cn;
3303         if (this.desktopCenter) {
3304             cn.push({cls : 'container', cn : []});
3305             cn = cn[0].cn;
3306         }
3307         
3308         if(this.srButton){
3309             cn.push({
3310                 tag: 'div',
3311                 cls: 'navbar-header',
3312                 cn: [
3313                     {
3314                         tag: 'button',
3315                         type: 'button',
3316                         cls: 'navbar-toggle',
3317                         'data-toggle': 'collapse',
3318                         cn: [
3319                             {
3320                                 tag: 'span',
3321                                 cls: 'sr-only',
3322                                 html: 'Toggle navigation'
3323                             },
3324                             {
3325                                 tag: 'span',
3326                                 cls: 'icon-bar'
3327                             },
3328                             {
3329                                 tag: 'span',
3330                                 cls: 'icon-bar'
3331                             },
3332                             {
3333                                 tag: 'span',
3334                                 cls: 'icon-bar'
3335                             }
3336                         ]
3337                     }
3338                 ]
3339             });
3340         }
3341         
3342         cn.push({
3343             tag: 'div',
3344             cls: 'collapse navbar-collapse',
3345             cn : []
3346         });
3347         
3348         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3349         
3350         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3351             cfg.cls += ' navbar-' + this.position;
3352             
3353             // tag can override this..
3354             
3355             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3356         }
3357         
3358         if (this.brand !== '') {
3359             cn[0].cn.push({
3360                 tag: 'a',
3361                 href: this.brand_href ? this.brand_href : '#',
3362                 cls: 'navbar-brand',
3363                 cn: [
3364                 this.brand
3365                 ]
3366             });
3367         }
3368         
3369         if(this.main){
3370             cfg.cls += ' main-nav';
3371         }
3372         
3373         
3374         return cfg;
3375
3376         
3377     },
3378     getHeaderChildContainer : function()
3379     {
3380         if (this.el.select('.navbar-header').getCount()) {
3381             return this.el.select('.navbar-header',true).first();
3382         }
3383         
3384         return this.getChildContainer();
3385     },
3386     
3387     
3388     initEvents : function()
3389     {
3390         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3391         
3392         if (this.autohide) {
3393             
3394             var prevScroll = 0;
3395             var ft = this.el;
3396             
3397             Roo.get(document).on('scroll',function(e) {
3398                 var ns = Roo.get(document).getScroll().top;
3399                 var os = prevScroll;
3400                 prevScroll = ns;
3401                 
3402                 if(ns > os){
3403                     ft.removeClass('slideDown');
3404                     ft.addClass('slideUp');
3405                     return;
3406                 }
3407                 ft.removeClass('slideUp');
3408                 ft.addClass('slideDown');
3409                  
3410               
3411           },this);
3412         }
3413     }    
3414           
3415       
3416     
3417     
3418 });
3419
3420
3421
3422  
3423
3424  /*
3425  * - LGPL
3426  *
3427  * navbar
3428  * 
3429  */
3430
3431 /**
3432  * @class Roo.bootstrap.NavSidebar
3433  * @extends Roo.bootstrap.Navbar
3434  * Bootstrap Sidebar class
3435  * 
3436  * @constructor
3437  * Create a new Sidebar
3438  * @param {Object} config The config object
3439  */
3440
3441
3442 Roo.bootstrap.NavSidebar = function(config){
3443     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3444 };
3445
3446 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3447     
3448     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3449     
3450     getAutoCreate : function(){
3451         
3452         
3453         return  {
3454             tag: 'div',
3455             cls: 'sidebar sidebar-nav'
3456         };
3457     
3458         
3459     }
3460     
3461     
3462     
3463 });
3464
3465
3466
3467  
3468
3469  /*
3470  * - LGPL
3471  *
3472  * nav group
3473  * 
3474  */
3475
3476 /**
3477  * @class Roo.bootstrap.NavGroup
3478  * @extends Roo.bootstrap.Component
3479  * Bootstrap NavGroup class
3480  * @cfg {String} align left | right
3481  * @cfg {Boolean} inverse false | true
3482  * @cfg {String} type (nav|pills|tab) default nav
3483  * @cfg {String} navId - reference Id for navbar.
3484
3485  * 
3486  * @constructor
3487  * Create a new nav group
3488  * @param {Object} config The config object
3489  */
3490
3491 Roo.bootstrap.NavGroup = function(config){
3492     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3493     this.navItems = [];
3494    
3495     Roo.bootstrap.NavGroup.register(this);
3496      this.addEvents({
3497         /**
3498              * @event changed
3499              * Fires when the active item changes
3500              * @param {Roo.bootstrap.NavGroup} this
3501              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3502              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3503          */
3504         'changed': true
3505      });
3506     
3507 };
3508
3509 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3510     
3511     align: '',
3512     inverse: false,
3513     form: false,
3514     type: 'nav',
3515     navId : '',
3516     // private
3517     
3518     navItems : false, 
3519     
3520     getAutoCreate : function()
3521     {
3522         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3523         
3524         cfg = {
3525             tag : 'ul',
3526             cls: 'nav' 
3527         }
3528         
3529         if (['tabs','pills'].indexOf(this.type)!==-1) {
3530             cfg.cls += ' nav-' + this.type
3531         } else {
3532             if (this.type!=='nav') {
3533                 Roo.log('nav type must be nav/tabs/pills')
3534             }
3535             cfg.cls += ' navbar-nav'
3536         }
3537         
3538         if (this.parent().sidebar) {
3539             cfg = {
3540                 tag: 'ul',
3541                 cls: 'dashboard-menu sidebar-menu'
3542             }
3543             
3544             return cfg;
3545         }
3546         
3547         if (this.form === true) {
3548             cfg = {
3549                 tag: 'form',
3550                 cls: 'navbar-form'
3551             }
3552             
3553             if (this.align === 'right') {
3554                 cfg.cls += ' navbar-right';
3555             } else {
3556                 cfg.cls += ' navbar-left';
3557             }
3558         }
3559         
3560         if (this.align === 'right') {
3561             cfg.cls += ' navbar-right';
3562         }
3563         
3564         if (this.inverse) {
3565             cfg.cls += ' navbar-inverse';
3566             
3567         }
3568         
3569         
3570         return cfg;
3571     },
3572     /**
3573     * sets the active Navigation item
3574     * @param {Roo.bootstrap.NavItem} the new current navitem
3575     */
3576     setActiveItem : function(item)
3577     {
3578         var prev = false;
3579         Roo.each(this.navItems, function(v){
3580             if (v == item) {
3581                 return ;
3582             }
3583             if (v.isActive()) {
3584                 v.setActive(false, true);
3585                 prev = v;
3586                 
3587             }
3588             
3589         });
3590
3591         item.setActive(true, true);
3592         this.fireEvent('changed', this, item, prev);
3593         
3594         
3595     },
3596     /**
3597     * gets the active Navigation item
3598     * @return {Roo.bootstrap.NavItem} the current navitem
3599     */
3600     getActive : function()
3601     {
3602         
3603         var prev = false;
3604         Roo.each(this.navItems, function(v){
3605             
3606             if (v.isActive()) {
3607                 prev = v;
3608                 
3609             }
3610             
3611         });
3612         return prev;
3613     },
3614     
3615     indexOfNav : function()
3616     {
3617         
3618         var prev = false;
3619         Roo.each(this.navItems, function(v,i){
3620             
3621             if (v.isActive()) {
3622                 prev = i;
3623                 
3624             }
3625             
3626         });
3627         return prev;
3628     },
3629     /**
3630     * adds a Navigation item
3631     * @param {Roo.bootstrap.NavItem} the navitem to add
3632     */
3633     addItem : function(cfg)
3634     {
3635         var cn = new Roo.bootstrap.NavItem(cfg);
3636         this.register(cn);
3637         cn.parentId = this.id;
3638         cn.onRender(this.el, null);
3639         return cn;
3640     },
3641     /**
3642     * register a Navigation item
3643     * @param {Roo.bootstrap.NavItem} the navitem to add
3644     */
3645     register : function(item)
3646     {
3647         this.navItems.push( item);
3648         item.navId = this.navId;
3649     
3650     },
3651     
3652     /**
3653     * clear all the Navigation item
3654     */
3655    
3656     clearAll : function()
3657     {
3658         this.navItems = [];
3659         this.el.dom.innerHTML = '';
3660     },
3661     
3662     getNavItem: function(tabId)
3663     {
3664         var ret = false;
3665         Roo.each(this.navItems, function(e) {
3666             if (e.tabId == tabId) {
3667                ret =  e;
3668                return false;
3669             }
3670             return true;
3671             
3672         });
3673         return ret;
3674     },
3675     
3676     setActiveNext : function()
3677     {
3678         var i = this.indexOfNav(this.getActive());
3679         if (i > this.navItems.length) {
3680             return;
3681         }
3682         this.setActiveItem(this.navItems[i+1]);
3683     },
3684     setActivePrev : function()
3685     {
3686         var i = this.indexOfNav(this.getActive());
3687         if (i  < 1) {
3688             return;
3689         }
3690         this.setActiveItem(this.navItems[i-1]);
3691     },
3692     clearWasActive : function(except) {
3693         Roo.each(this.navItems, function(e) {
3694             if (e.tabId != except.tabId && e.was_active) {
3695                e.was_active = false;
3696                return false;
3697             }
3698             return true;
3699             
3700         });
3701     },
3702     getWasActive : function ()
3703     {
3704         var r = false;
3705         Roo.each(this.navItems, function(e) {
3706             if (e.was_active) {
3707                r = e;
3708                return false;
3709             }
3710             return true;
3711             
3712         });
3713         return r;
3714     }
3715     
3716     
3717 });
3718
3719  
3720 Roo.apply(Roo.bootstrap.NavGroup, {
3721     
3722     groups: {},
3723      /**
3724     * register a Navigation Group
3725     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3726     */
3727     register : function(navgrp)
3728     {
3729         this.groups[navgrp.navId] = navgrp;
3730         
3731     },
3732     /**
3733     * fetch a Navigation Group based on the navigation ID
3734     * @param {string} the navgroup to add
3735     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3736     */
3737     get: function(navId) {
3738         if (typeof(this.groups[navId]) == 'undefined') {
3739             return false;
3740             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3741         }
3742         return this.groups[navId] ;
3743     }
3744     
3745     
3746     
3747 });
3748
3749  /*
3750  * - LGPL
3751  *
3752  * row
3753  * 
3754  */
3755
3756 /**
3757  * @class Roo.bootstrap.NavItem
3758  * @extends Roo.bootstrap.Component
3759  * Bootstrap Navbar.NavItem class
3760  * @cfg {String} href  link to
3761  * @cfg {String} html content of button
3762  * @cfg {String} badge text inside badge
3763  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3764  * @cfg {String} glyphicon name of glyphicon
3765  * @cfg {String} icon name of font awesome icon
3766  * @cfg {Boolean} active Is item active
3767  * @cfg {Boolean} disabled Is item disabled
3768  
3769  * @cfg {Boolean} preventDefault (true | false) default false
3770  * @cfg {String} tabId the tab that this item activates.
3771  * @cfg {String} tagtype (a|span) render as a href or span?
3772  * @cfg {Boolean} animateRef (true|false) link to element default false  
3773   
3774  * @constructor
3775  * Create a new Navbar Item
3776  * @param {Object} config The config object
3777  */
3778 Roo.bootstrap.NavItem = function(config){
3779     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3780     this.addEvents({
3781         // raw events
3782         /**
3783          * @event click
3784          * The raw click event for the entire grid.
3785          * @param {Roo.EventObject} e
3786          */
3787         "click" : true,
3788          /**
3789             * @event changed
3790             * Fires when the active item active state changes
3791             * @param {Roo.bootstrap.NavItem} this
3792             * @param {boolean} state the new state
3793              
3794          */
3795         'changed': true,
3796         /**
3797             * @event scrollto
3798             * Fires when scroll to element
3799             * @param {Roo.bootstrap.NavItem} this
3800             * @param {Object} options
3801             * @param {Roo.EventObject} e
3802              
3803          */
3804         'scrollto': true
3805     });
3806    
3807 };
3808
3809 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3810     
3811     href: false,
3812     html: '',
3813     badge: '',
3814     icon: false,
3815     glyphicon: false,
3816     active: false,
3817     preventDefault : false,
3818     tabId : false,
3819     tagtype : 'a',
3820     disabled : false,
3821     animateRef : false,
3822     was_active : false,
3823     
3824     getAutoCreate : function(){
3825          
3826         var cfg = {
3827             tag: 'li',
3828             cls: 'nav-item'
3829             
3830         }
3831         if (this.active) {
3832             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3833         }
3834         if (this.disabled) {
3835             cfg.cls += ' disabled';
3836         }
3837         
3838         if (this.href || this.html || this.glyphicon || this.icon) {
3839             cfg.cn = [
3840                 {
3841                     tag: this.tagtype,
3842                     href : this.href || "#",
3843                     html: this.html || ''
3844                 }
3845             ];
3846             
3847             if (this.icon) {
3848                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3849             }
3850
3851             if(this.glyphicon) {
3852                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3853             }
3854             
3855             if (this.menu) {
3856                 
3857                 cfg.cn[0].html += " <span class='caret'></span>";
3858              
3859             }
3860             
3861             if (this.badge !== '') {
3862                  
3863                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3864             }
3865         }
3866         
3867         
3868         
3869         return cfg;
3870     },
3871     initEvents: function() 
3872     {
3873         if (typeof (this.menu) != 'undefined') {
3874             this.menu.parentType = this.xtype;
3875             this.menu.triggerEl = this.el;
3876             this.menu = this.addxtype(Roo.apply({}, this.menu));
3877         }
3878         
3879         this.el.select('a',true).on('click', this.onClick, this);
3880         
3881         if(this.tagtype == 'span'){
3882             this.el.select('span',true).on('click', this.onClick, this);
3883         }
3884        
3885         // at this point parent should be available..
3886         this.parent().register(this);
3887     },
3888     
3889     onClick : function(e)
3890     {
3891         if(
3892                 this.preventDefault || 
3893                 this.href == '#' 
3894         ){
3895             
3896             e.preventDefault();
3897         }
3898         
3899         if (this.disabled) {
3900             return;
3901         }
3902         
3903         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3904         if (tg && tg.transition) {
3905             Roo.log("waiting for the transitionend");
3906             return;
3907         }
3908         
3909         
3910         
3911         //Roo.log("fire event clicked");
3912         if(this.fireEvent('click', this, e) === false){
3913             return;
3914         };
3915         
3916         if(this.tagtype == 'span'){
3917             return;
3918         }
3919         
3920         //Roo.log(this.href);
3921         var ael = this.el.select('a',true).first();
3922         //Roo.log(ael);
3923         
3924         if(ael && this.animateRef && this.href.indexOf('#') > -1){
3925             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
3926             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
3927                 return; // ignore... - it's a 'hash' to another page.
3928             }
3929             
3930             e.preventDefault();
3931             this.scrollToElement(e);
3932         }
3933         
3934         
3935         var p =  this.parent();
3936    
3937         if (['tabs','pills'].indexOf(p.type)!==-1) {
3938             if (typeof(p.setActiveItem) !== 'undefined') {
3939                 p.setActiveItem(this);
3940             }
3941         }
3942     },
3943     
3944     isActive: function () {
3945         return this.active
3946     },
3947     setActive : function(state, fire, is_was_active)
3948     {
3949         if (this.active && !state & this.navId) {
3950             this.was_active = true;
3951             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3952             if (nv) {
3953                 nv.clearWasActive(this);
3954             }
3955             
3956         }
3957         this.active = state;
3958         
3959         if (!state ) {
3960             this.el.removeClass('active');
3961         } else if (!this.el.hasClass('active')) {
3962             this.el.addClass('active');
3963         }
3964         if (fire) {
3965             this.fireEvent('changed', this, state);
3966         }
3967         
3968         // show a panel if it's registered and related..
3969         
3970         if (!this.navId || !this.tabId || !state || is_was_active) {
3971             return;
3972         }
3973         
3974         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3975         if (!tg) {
3976             return;
3977         }
3978         var pan = tg.getPanelByName(this.tabId);
3979         if (!pan) {
3980             return;
3981         }
3982         // if we can not flip to new panel - go back to old nav highlight..
3983         if (false == tg.showPanel(pan)) {
3984             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3985             if (nv) {
3986                 var onav = nv.getWasActive();
3987                 if (onav) {
3988                     onav.setActive(true, false, true);
3989                 }
3990             }
3991             
3992         }
3993         
3994         
3995         
3996     },
3997      // this should not be here...
3998     setDisabled : function(state)
3999     {
4000         this.disabled = state;
4001         if (!state ) {
4002             this.el.removeClass('disabled');
4003         } else if (!this.el.hasClass('disabled')) {
4004             this.el.addClass('disabled');
4005         }
4006         
4007     },
4008     
4009     /**
4010      * Fetch the element to display the tooltip on.
4011      * @return {Roo.Element} defaults to this.el
4012      */
4013     tooltipEl : function()
4014     {
4015         return this.el.select('' + this.tagtype + '', true).first();
4016     },
4017     
4018     scrollToElement : function(e)
4019     {
4020         var c = document.body;
4021         
4022         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4023         
4024         if(!target){
4025             return;
4026         }
4027
4028         var o = target.calcOffsetsTo(c);
4029         
4030         var options = {
4031             target : target,
4032             value : o[1]
4033         }
4034         
4035         this.fireEvent('scrollto', this, options, e);
4036         
4037         Roo.get(c).scrollTo('top', options.value, true);
4038         
4039         return;
4040     }
4041 });
4042  
4043
4044  /*
4045  * - LGPL
4046  *
4047  * sidebar item
4048  *
4049  *  li
4050  *    <span> icon </span>
4051  *    <span> text </span>
4052  *    <span>badge </span>
4053  */
4054
4055 /**
4056  * @class Roo.bootstrap.NavSidebarItem
4057  * @extends Roo.bootstrap.NavItem
4058  * Bootstrap Navbar.NavSidebarItem class
4059  * @constructor
4060  * Create a new Navbar Button
4061  * @param {Object} config The config object
4062  */
4063 Roo.bootstrap.NavSidebarItem = function(config){
4064     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4065     this.addEvents({
4066         // raw events
4067         /**
4068          * @event click
4069          * The raw click event for the entire grid.
4070          * @param {Roo.EventObject} e
4071          */
4072         "click" : true,
4073          /**
4074             * @event changed
4075             * Fires when the active item active state changes
4076             * @param {Roo.bootstrap.NavSidebarItem} this
4077             * @param {boolean} state the new state
4078              
4079          */
4080         'changed': true
4081     });
4082    
4083 };
4084
4085 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4086     
4087     
4088     getAutoCreate : function(){
4089         
4090         
4091         var a = {
4092                 tag: 'a',
4093                 href : this.href || '#',
4094                 cls: '',
4095                 html : '',
4096                 cn : []
4097         };
4098         var cfg = {
4099             tag: 'li',
4100             cls: '',
4101             cn: [ a ]
4102         }
4103         var span = {
4104             tag: 'span',
4105             html : this.html || ''
4106         }
4107         
4108         
4109         if (this.active) {
4110             cfg.cls += ' active';
4111         }
4112         
4113         // left icon..
4114         if (this.glyphicon || this.icon) {
4115             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4116             a.cn.push({ tag : 'i', cls : c }) ;
4117         }
4118         // html..
4119         a.cn.push(span);
4120         // then badge..
4121         if (this.badge !== '') {
4122             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4123         }
4124         // fi
4125         if (this.menu) {
4126             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4127             a.cls += 'dropdown-toggle treeview' ;
4128             
4129         }
4130         
4131         
4132         
4133         return cfg;
4134          
4135            
4136     }
4137    
4138      
4139  
4140 });
4141  
4142
4143  /*
4144  * - LGPL
4145  *
4146  * row
4147  * 
4148  */
4149
4150 /**
4151  * @class Roo.bootstrap.Row
4152  * @extends Roo.bootstrap.Component
4153  * Bootstrap Row class (contains columns...)
4154  * 
4155  * @constructor
4156  * Create a new Row
4157  * @param {Object} config The config object
4158  */
4159
4160 Roo.bootstrap.Row = function(config){
4161     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4162 };
4163
4164 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4165     
4166     getAutoCreate : function(){
4167        return {
4168             cls: 'row clearfix'
4169        };
4170     }
4171     
4172     
4173 });
4174
4175  
4176
4177  /*
4178  * - LGPL
4179  *
4180  * element
4181  * 
4182  */
4183
4184 /**
4185  * @class Roo.bootstrap.Element
4186  * @extends Roo.bootstrap.Component
4187  * Bootstrap Element class
4188  * @cfg {String} html contents of the element
4189  * @cfg {String} tag tag of the element
4190  * @cfg {String} cls class of the element
4191  * @cfg {Boolean} preventDefault (true|false) default false
4192  * @cfg {Boolean} clickable (true|false) default false
4193  * 
4194  * @constructor
4195  * Create a new Element
4196  * @param {Object} config The config object
4197  */
4198
4199 Roo.bootstrap.Element = function(config){
4200     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4201     
4202     this.addEvents({
4203         // raw events
4204         /**
4205          * @event click
4206          * When a element is chick
4207          * @param {Roo.bootstrap.Element} this
4208          * @param {Roo.EventObject} e
4209          */
4210         "click" : true
4211     });
4212 };
4213
4214 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4215     
4216     tag: 'div',
4217     cls: '',
4218     html: '',
4219     preventDefault: false, 
4220     clickable: false,
4221     
4222     getAutoCreate : function(){
4223         
4224         var cfg = {
4225             tag: this.tag,
4226             cls: this.cls,
4227             html: this.html
4228         }
4229         
4230         return cfg;
4231     },
4232     
4233     initEvents: function() 
4234     {
4235         Roo.bootstrap.Element.superclass.initEvents.call(this);
4236         
4237         if(this.clickable){
4238             this.el.on('click', this.onClick, this);
4239         }
4240         
4241     },
4242     
4243     onClick : function(e)
4244     {
4245         if(this.preventDefault){
4246             e.preventDefault();
4247         }
4248         
4249         this.fireEvent('click', this, e);
4250     },
4251     
4252     getValue : function()
4253     {
4254         return this.el.dom.innerHTML;
4255     },
4256     
4257     setValue : function(value)
4258     {
4259         this.el.dom.innerHTML = value;
4260     }
4261    
4262 });
4263
4264  
4265
4266  /*
4267  * - LGPL
4268  *
4269  * pagination
4270  * 
4271  */
4272
4273 /**
4274  * @class Roo.bootstrap.Pagination
4275  * @extends Roo.bootstrap.Component
4276  * Bootstrap Pagination class
4277  * @cfg {String} size xs | sm | md | lg
4278  * @cfg {Boolean} inverse false | true
4279  * 
4280  * @constructor
4281  * Create a new Pagination
4282  * @param {Object} config The config object
4283  */
4284
4285 Roo.bootstrap.Pagination = function(config){
4286     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4287 };
4288
4289 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4290     
4291     cls: false,
4292     size: false,
4293     inverse: false,
4294     
4295     getAutoCreate : function(){
4296         var cfg = {
4297             tag: 'ul',
4298                 cls: 'pagination'
4299         };
4300         if (this.inverse) {
4301             cfg.cls += ' inverse';
4302         }
4303         if (this.html) {
4304             cfg.html=this.html;
4305         }
4306         if (this.cls) {
4307             cfg.cls += " " + this.cls;
4308         }
4309         return cfg;
4310     }
4311    
4312 });
4313
4314  
4315
4316  /*
4317  * - LGPL
4318  *
4319  * Pagination item
4320  * 
4321  */
4322
4323
4324 /**
4325  * @class Roo.bootstrap.PaginationItem
4326  * @extends Roo.bootstrap.Component
4327  * Bootstrap PaginationItem class
4328  * @cfg {String} html text
4329  * @cfg {String} href the link
4330  * @cfg {Boolean} preventDefault (true | false) default true
4331  * @cfg {Boolean} active (true | false) default false
4332  * @cfg {Boolean} disabled default false
4333  * 
4334  * 
4335  * @constructor
4336  * Create a new PaginationItem
4337  * @param {Object} config The config object
4338  */
4339
4340
4341 Roo.bootstrap.PaginationItem = function(config){
4342     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4343     this.addEvents({
4344         // raw events
4345         /**
4346          * @event click
4347          * The raw click event for the entire grid.
4348          * @param {Roo.EventObject} e
4349          */
4350         "click" : true
4351     });
4352 };
4353
4354 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4355     
4356     href : false,
4357     html : false,
4358     preventDefault: true,
4359     active : false,
4360     cls : false,
4361     disabled: false,
4362     
4363     getAutoCreate : function(){
4364         var cfg= {
4365             tag: 'li',
4366             cn: [
4367                 {
4368                     tag : 'a',
4369                     href : this.href ? this.href : '#',
4370                     html : this.html ? this.html : ''
4371                 }
4372             ]
4373         };
4374         
4375         if(this.cls){
4376             cfg.cls = this.cls;
4377         }
4378         
4379         if(this.disabled){
4380             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4381         }
4382         
4383         if(this.active){
4384             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4385         }
4386         
4387         return cfg;
4388     },
4389     
4390     initEvents: function() {
4391         
4392         this.el.on('click', this.onClick, this);
4393         
4394     },
4395     onClick : function(e)
4396     {
4397         Roo.log('PaginationItem on click ');
4398         if(this.preventDefault){
4399             e.preventDefault();
4400         }
4401         
4402         if(this.disabled){
4403             return;
4404         }
4405         
4406         this.fireEvent('click', this, e);
4407     }
4408    
4409 });
4410
4411  
4412
4413  /*
4414  * - LGPL
4415  *
4416  * slider
4417  * 
4418  */
4419
4420
4421 /**
4422  * @class Roo.bootstrap.Slider
4423  * @extends Roo.bootstrap.Component
4424  * Bootstrap Slider class
4425  *    
4426  * @constructor
4427  * Create a new Slider
4428  * @param {Object} config The config object
4429  */
4430
4431 Roo.bootstrap.Slider = function(config){
4432     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4433 };
4434
4435 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4436     
4437     getAutoCreate : function(){
4438         
4439         var cfg = {
4440             tag: 'div',
4441             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4442             cn: [
4443                 {
4444                     tag: 'a',
4445                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4446                 }
4447             ]
4448         }
4449         
4450         return cfg;
4451     }
4452    
4453 });
4454
4455  /*
4456  * Based on:
4457  * Ext JS Library 1.1.1
4458  * Copyright(c) 2006-2007, Ext JS, LLC.
4459  *
4460  * Originally Released Under LGPL - original licence link has changed is not relivant.
4461  *
4462  * Fork - LGPL
4463  * <script type="text/javascript">
4464  */
4465  
4466
4467 /**
4468  * @class Roo.grid.ColumnModel
4469  * @extends Roo.util.Observable
4470  * This is the default implementation of a ColumnModel used by the Grid. It defines
4471  * the columns in the grid.
4472  * <br>Usage:<br>
4473  <pre><code>
4474  var colModel = new Roo.grid.ColumnModel([
4475         {header: "Ticker", width: 60, sortable: true, locked: true},
4476         {header: "Company Name", width: 150, sortable: true},
4477         {header: "Market Cap.", width: 100, sortable: true},
4478         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4479         {header: "Employees", width: 100, sortable: true, resizable: false}
4480  ]);
4481  </code></pre>
4482  * <p>
4483  
4484  * The config options listed for this class are options which may appear in each
4485  * individual column definition.
4486  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4487  * @constructor
4488  * @param {Object} config An Array of column config objects. See this class's
4489  * config objects for details.
4490 */
4491 Roo.grid.ColumnModel = function(config){
4492         /**
4493      * The config passed into the constructor
4494      */
4495     this.config = config;
4496     this.lookup = {};
4497
4498     // if no id, create one
4499     // if the column does not have a dataIndex mapping,
4500     // map it to the order it is in the config
4501     for(var i = 0, len = config.length; i < len; i++){
4502         var c = config[i];
4503         if(typeof c.dataIndex == "undefined"){
4504             c.dataIndex = i;
4505         }
4506         if(typeof c.renderer == "string"){
4507             c.renderer = Roo.util.Format[c.renderer];
4508         }
4509         if(typeof c.id == "undefined"){
4510             c.id = Roo.id();
4511         }
4512         if(c.editor && c.editor.xtype){
4513             c.editor  = Roo.factory(c.editor, Roo.grid);
4514         }
4515         if(c.editor && c.editor.isFormField){
4516             c.editor = new Roo.grid.GridEditor(c.editor);
4517         }
4518         this.lookup[c.id] = c;
4519     }
4520
4521     /**
4522      * The width of columns which have no width specified (defaults to 100)
4523      * @type Number
4524      */
4525     this.defaultWidth = 100;
4526
4527     /**
4528      * Default sortable of columns which have no sortable specified (defaults to false)
4529      * @type Boolean
4530      */
4531     this.defaultSortable = false;
4532
4533     this.addEvents({
4534         /**
4535              * @event widthchange
4536              * Fires when the width of a column changes.
4537              * @param {ColumnModel} this
4538              * @param {Number} columnIndex The column index
4539              * @param {Number} newWidth The new width
4540              */
4541             "widthchange": true,
4542         /**
4543              * @event headerchange
4544              * Fires when the text of a header changes.
4545              * @param {ColumnModel} this
4546              * @param {Number} columnIndex The column index
4547              * @param {Number} newText The new header text
4548              */
4549             "headerchange": true,
4550         /**
4551              * @event hiddenchange
4552              * Fires when a column is hidden or "unhidden".
4553              * @param {ColumnModel} this
4554              * @param {Number} columnIndex The column index
4555              * @param {Boolean} hidden true if hidden, false otherwise
4556              */
4557             "hiddenchange": true,
4558             /**
4559          * @event columnmoved
4560          * Fires when a column is moved.
4561          * @param {ColumnModel} this
4562          * @param {Number} oldIndex
4563          * @param {Number} newIndex
4564          */
4565         "columnmoved" : true,
4566         /**
4567          * @event columlockchange
4568          * Fires when a column's locked state is changed
4569          * @param {ColumnModel} this
4570          * @param {Number} colIndex
4571          * @param {Boolean} locked true if locked
4572          */
4573         "columnlockchange" : true
4574     });
4575     Roo.grid.ColumnModel.superclass.constructor.call(this);
4576 };
4577 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4578     /**
4579      * @cfg {String} header The header text to display in the Grid view.
4580      */
4581     /**
4582      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4583      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4584      * specified, the column's index is used as an index into the Record's data Array.
4585      */
4586     /**
4587      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4588      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4589      */
4590     /**
4591      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4592      * Defaults to the value of the {@link #defaultSortable} property.
4593      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4594      */
4595     /**
4596      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4597      */
4598     /**
4599      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4600      */
4601     /**
4602      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4603      */
4604     /**
4605      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4606      */
4607     /**
4608      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4609      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4610      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4611      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4612      */
4613        /**
4614      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4615      */
4616     /**
4617      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4618      */
4619     /**
4620      * @cfg {String} cursor (Optional)
4621      */
4622     /**
4623      * @cfg {String} tooltip (Optional)
4624      */
4625     /**
4626      * Returns the id of the column at the specified index.
4627      * @param {Number} index The column index
4628      * @return {String} the id
4629      */
4630     getColumnId : function(index){
4631         return this.config[index].id;
4632     },
4633
4634     /**
4635      * Returns the column for a specified id.
4636      * @param {String} id The column id
4637      * @return {Object} the column
4638      */
4639     getColumnById : function(id){
4640         return this.lookup[id];
4641     },
4642
4643     
4644     /**
4645      * Returns the column for a specified dataIndex.
4646      * @param {String} dataIndex The column dataIndex
4647      * @return {Object|Boolean} the column or false if not found
4648      */
4649     getColumnByDataIndex: function(dataIndex){
4650         var index = this.findColumnIndex(dataIndex);
4651         return index > -1 ? this.config[index] : false;
4652     },
4653     
4654     /**
4655      * Returns the index for a specified column id.
4656      * @param {String} id The column id
4657      * @return {Number} the index, or -1 if not found
4658      */
4659     getIndexById : function(id){
4660         for(var i = 0, len = this.config.length; i < len; i++){
4661             if(this.config[i].id == id){
4662                 return i;
4663             }
4664         }
4665         return -1;
4666     },
4667     
4668     /**
4669      * Returns the index for a specified column dataIndex.
4670      * @param {String} dataIndex The column dataIndex
4671      * @return {Number} the index, or -1 if not found
4672      */
4673     
4674     findColumnIndex : function(dataIndex){
4675         for(var i = 0, len = this.config.length; i < len; i++){
4676             if(this.config[i].dataIndex == dataIndex){
4677                 return i;
4678             }
4679         }
4680         return -1;
4681     },
4682     
4683     
4684     moveColumn : function(oldIndex, newIndex){
4685         var c = this.config[oldIndex];
4686         this.config.splice(oldIndex, 1);
4687         this.config.splice(newIndex, 0, c);
4688         this.dataMap = null;
4689         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4690     },
4691
4692     isLocked : function(colIndex){
4693         return this.config[colIndex].locked === true;
4694     },
4695
4696     setLocked : function(colIndex, value, suppressEvent){
4697         if(this.isLocked(colIndex) == value){
4698             return;
4699         }
4700         this.config[colIndex].locked = value;
4701         if(!suppressEvent){
4702             this.fireEvent("columnlockchange", this, colIndex, value);
4703         }
4704     },
4705
4706     getTotalLockedWidth : function(){
4707         var totalWidth = 0;
4708         for(var i = 0; i < this.config.length; i++){
4709             if(this.isLocked(i) && !this.isHidden(i)){
4710                 this.totalWidth += this.getColumnWidth(i);
4711             }
4712         }
4713         return totalWidth;
4714     },
4715
4716     getLockedCount : function(){
4717         for(var i = 0, len = this.config.length; i < len; i++){
4718             if(!this.isLocked(i)){
4719                 return i;
4720             }
4721         }
4722     },
4723
4724     /**
4725      * Returns the number of columns.
4726      * @return {Number}
4727      */
4728     getColumnCount : function(visibleOnly){
4729         if(visibleOnly === true){
4730             var c = 0;
4731             for(var i = 0, len = this.config.length; i < len; i++){
4732                 if(!this.isHidden(i)){
4733                     c++;
4734                 }
4735             }
4736             return c;
4737         }
4738         return this.config.length;
4739     },
4740
4741     /**
4742      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4743      * @param {Function} fn
4744      * @param {Object} scope (optional)
4745      * @return {Array} result
4746      */
4747     getColumnsBy : function(fn, scope){
4748         var r = [];
4749         for(var i = 0, len = this.config.length; i < len; i++){
4750             var c = this.config[i];
4751             if(fn.call(scope||this, c, i) === true){
4752                 r[r.length] = c;
4753             }
4754         }
4755         return r;
4756     },
4757
4758     /**
4759      * Returns true if the specified column is sortable.
4760      * @param {Number} col The column index
4761      * @return {Boolean}
4762      */
4763     isSortable : function(col){
4764         if(typeof this.config[col].sortable == "undefined"){
4765             return this.defaultSortable;
4766         }
4767         return this.config[col].sortable;
4768     },
4769
4770     /**
4771      * Returns the rendering (formatting) function defined for the column.
4772      * @param {Number} col The column index.
4773      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4774      */
4775     getRenderer : function(col){
4776         if(!this.config[col].renderer){
4777             return Roo.grid.ColumnModel.defaultRenderer;
4778         }
4779         return this.config[col].renderer;
4780     },
4781
4782     /**
4783      * Sets the rendering (formatting) function for a column.
4784      * @param {Number} col The column index
4785      * @param {Function} fn The function to use to process the cell's raw data
4786      * to return HTML markup for the grid view. The render function is called with
4787      * the following parameters:<ul>
4788      * <li>Data value.</li>
4789      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4790      * <li>css A CSS style string to apply to the table cell.</li>
4791      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4792      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4793      * <li>Row index</li>
4794      * <li>Column index</li>
4795      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4796      */
4797     setRenderer : function(col, fn){
4798         this.config[col].renderer = fn;
4799     },
4800
4801     /**
4802      * Returns the width for the specified column.
4803      * @param {Number} col The column index
4804      * @return {Number}
4805      */
4806     getColumnWidth : function(col){
4807         return this.config[col].width * 1 || this.defaultWidth;
4808     },
4809
4810     /**
4811      * Sets the width for a column.
4812      * @param {Number} col The column index
4813      * @param {Number} width The new width
4814      */
4815     setColumnWidth : function(col, width, suppressEvent){
4816         this.config[col].width = width;
4817         this.totalWidth = null;
4818         if(!suppressEvent){
4819              this.fireEvent("widthchange", this, col, width);
4820         }
4821     },
4822
4823     /**
4824      * Returns the total width of all columns.
4825      * @param {Boolean} includeHidden True to include hidden column widths
4826      * @return {Number}
4827      */
4828     getTotalWidth : function(includeHidden){
4829         if(!this.totalWidth){
4830             this.totalWidth = 0;
4831             for(var i = 0, len = this.config.length; i < len; i++){
4832                 if(includeHidden || !this.isHidden(i)){
4833                     this.totalWidth += this.getColumnWidth(i);
4834                 }
4835             }
4836         }
4837         return this.totalWidth;
4838     },
4839
4840     /**
4841      * Returns the header for the specified column.
4842      * @param {Number} col The column index
4843      * @return {String}
4844      */
4845     getColumnHeader : function(col){
4846         return this.config[col].header;
4847     },
4848
4849     /**
4850      * Sets the header for a column.
4851      * @param {Number} col The column index
4852      * @param {String} header The new header
4853      */
4854     setColumnHeader : function(col, header){
4855         this.config[col].header = header;
4856         this.fireEvent("headerchange", this, col, header);
4857     },
4858
4859     /**
4860      * Returns the tooltip for the specified column.
4861      * @param {Number} col The column index
4862      * @return {String}
4863      */
4864     getColumnTooltip : function(col){
4865             return this.config[col].tooltip;
4866     },
4867     /**
4868      * Sets the tooltip for a column.
4869      * @param {Number} col The column index
4870      * @param {String} tooltip The new tooltip
4871      */
4872     setColumnTooltip : function(col, tooltip){
4873             this.config[col].tooltip = tooltip;
4874     },
4875
4876     /**
4877      * Returns the dataIndex for the specified column.
4878      * @param {Number} col The column index
4879      * @return {Number}
4880      */
4881     getDataIndex : function(col){
4882         return this.config[col].dataIndex;
4883     },
4884
4885     /**
4886      * Sets the dataIndex for a column.
4887      * @param {Number} col The column index
4888      * @param {Number} dataIndex The new dataIndex
4889      */
4890     setDataIndex : function(col, dataIndex){
4891         this.config[col].dataIndex = dataIndex;
4892     },
4893
4894     
4895     
4896     /**
4897      * Returns true if the cell is editable.
4898      * @param {Number} colIndex The column index
4899      * @param {Number} rowIndex The row index
4900      * @return {Boolean}
4901      */
4902     isCellEditable : function(colIndex, rowIndex){
4903         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4904     },
4905
4906     /**
4907      * Returns the editor defined for the cell/column.
4908      * return false or null to disable editing.
4909      * @param {Number} colIndex The column index
4910      * @param {Number} rowIndex The row index
4911      * @return {Object}
4912      */
4913     getCellEditor : function(colIndex, rowIndex){
4914         return this.config[colIndex].editor;
4915     },
4916
4917     /**
4918      * Sets if a column is editable.
4919      * @param {Number} col The column index
4920      * @param {Boolean} editable True if the column is editable
4921      */
4922     setEditable : function(col, editable){
4923         this.config[col].editable = editable;
4924     },
4925
4926
4927     /**
4928      * Returns true if the column is hidden.
4929      * @param {Number} colIndex The column index
4930      * @return {Boolean}
4931      */
4932     isHidden : function(colIndex){
4933         return this.config[colIndex].hidden;
4934     },
4935
4936
4937     /**
4938      * Returns true if the column width cannot be changed
4939      */
4940     isFixed : function(colIndex){
4941         return this.config[colIndex].fixed;
4942     },
4943
4944     /**
4945      * Returns true if the column can be resized
4946      * @return {Boolean}
4947      */
4948     isResizable : function(colIndex){
4949         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4950     },
4951     /**
4952      * Sets if a column is hidden.
4953      * @param {Number} colIndex The column index
4954      * @param {Boolean} hidden True if the column is hidden
4955      */
4956     setHidden : function(colIndex, hidden){
4957         this.config[colIndex].hidden = hidden;
4958         this.totalWidth = null;
4959         this.fireEvent("hiddenchange", this, colIndex, hidden);
4960     },
4961
4962     /**
4963      * Sets the editor for a column.
4964      * @param {Number} col The column index
4965      * @param {Object} editor The editor object
4966      */
4967     setEditor : function(col, editor){
4968         this.config[col].editor = editor;
4969     }
4970 });
4971
4972 Roo.grid.ColumnModel.defaultRenderer = function(value){
4973         if(typeof value == "string" && value.length < 1){
4974             return "&#160;";
4975         }
4976         return value;
4977 };
4978
4979 // Alias for backwards compatibility
4980 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4981 /*
4982  * Based on:
4983  * Ext JS Library 1.1.1
4984  * Copyright(c) 2006-2007, Ext JS, LLC.
4985  *
4986  * Originally Released Under LGPL - original licence link has changed is not relivant.
4987  *
4988  * Fork - LGPL
4989  * <script type="text/javascript">
4990  */
4991  
4992 /**
4993  * @class Roo.LoadMask
4994  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4995  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4996  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4997  * element's UpdateManager load indicator and will be destroyed after the initial load.
4998  * @constructor
4999  * Create a new LoadMask
5000  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5001  * @param {Object} config The config object
5002  */
5003 Roo.LoadMask = function(el, config){
5004     this.el = Roo.get(el);
5005     Roo.apply(this, config);
5006     if(this.store){
5007         this.store.on('beforeload', this.onBeforeLoad, this);
5008         this.store.on('load', this.onLoad, this);
5009         this.store.on('loadexception', this.onLoadException, this);
5010         this.removeMask = false;
5011     }else{
5012         var um = this.el.getUpdateManager();
5013         um.showLoadIndicator = false; // disable the default indicator
5014         um.on('beforeupdate', this.onBeforeLoad, this);
5015         um.on('update', this.onLoad, this);
5016         um.on('failure', this.onLoad, this);
5017         this.removeMask = true;
5018     }
5019 };
5020
5021 Roo.LoadMask.prototype = {
5022     /**
5023      * @cfg {Boolean} removeMask
5024      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5025      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5026      */
5027     /**
5028      * @cfg {String} msg
5029      * The text to display in a centered loading message box (defaults to 'Loading...')
5030      */
5031     msg : 'Loading...',
5032     /**
5033      * @cfg {String} msgCls
5034      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5035      */
5036     msgCls : 'x-mask-loading',
5037
5038     /**
5039      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5040      * @type Boolean
5041      */
5042     disabled: false,
5043
5044     /**
5045      * Disables the mask to prevent it from being displayed
5046      */
5047     disable : function(){
5048        this.disabled = true;
5049     },
5050
5051     /**
5052      * Enables the mask so that it can be displayed
5053      */
5054     enable : function(){
5055         this.disabled = false;
5056     },
5057     
5058     onLoadException : function()
5059     {
5060         Roo.log(arguments);
5061         
5062         if (typeof(arguments[3]) != 'undefined') {
5063             Roo.MessageBox.alert("Error loading",arguments[3]);
5064         } 
5065         /*
5066         try {
5067             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5068                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5069             }   
5070         } catch(e) {
5071             
5072         }
5073         */
5074     
5075         
5076         
5077         this.el.unmask(this.removeMask);
5078     },
5079     // private
5080     onLoad : function()
5081     {
5082         this.el.unmask(this.removeMask);
5083     },
5084
5085     // private
5086     onBeforeLoad : function(){
5087         if(!this.disabled){
5088             this.el.mask(this.msg, this.msgCls);
5089         }
5090     },
5091
5092     // private
5093     destroy : function(){
5094         if(this.store){
5095             this.store.un('beforeload', this.onBeforeLoad, this);
5096             this.store.un('load', this.onLoad, this);
5097             this.store.un('loadexception', this.onLoadException, this);
5098         }else{
5099             var um = this.el.getUpdateManager();
5100             um.un('beforeupdate', this.onBeforeLoad, this);
5101             um.un('update', this.onLoad, this);
5102             um.un('failure', this.onLoad, this);
5103         }
5104     }
5105 };/*
5106  * - LGPL
5107  *
5108  * table
5109  * 
5110  */
5111
5112 /**
5113  * @class Roo.bootstrap.Table
5114  * @extends Roo.bootstrap.Component
5115  * Bootstrap Table class
5116  * @cfg {String} cls table class
5117  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5118  * @cfg {String} bgcolor Specifies the background color for a table
5119  * @cfg {Number} border Specifies whether the table cells should have borders or not
5120  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5121  * @cfg {Number} cellspacing Specifies the space between cells
5122  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5123  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5124  * @cfg {String} sortable Specifies that the table should be sortable
5125  * @cfg {String} summary Specifies a summary of the content of a table
5126  * @cfg {Number} width Specifies the width of a table
5127  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5128  * 
5129  * @cfg {boolean} striped Should the rows be alternative striped
5130  * @cfg {boolean} bordered Add borders to the table
5131  * @cfg {boolean} hover Add hover highlighting
5132  * @cfg {boolean} condensed Format condensed
5133  * @cfg {boolean} responsive Format condensed
5134  * @cfg {Boolean} loadMask (true|false) default false
5135  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5136  * @cfg {Boolean} thead (true|false) generate thead, default true
5137  * @cfg {Boolean} RowSelection (true|false) default false
5138  * @cfg {Boolean} CellSelection (true|false) default false
5139  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5140  
5141  * 
5142  * @constructor
5143  * Create a new Table
5144  * @param {Object} config The config object
5145  */
5146
5147 Roo.bootstrap.Table = function(config){
5148     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5149     
5150     if (this.sm) {
5151         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5152         this.sm = this.selModel;
5153         this.sm.xmodule = this.xmodule || false;
5154     }
5155     if (this.cm && typeof(this.cm.config) == 'undefined') {
5156         this.colModel = new Roo.grid.ColumnModel(this.cm);
5157         this.cm = this.colModel;
5158         this.cm.xmodule = this.xmodule || false;
5159     }
5160     if (this.store) {
5161         this.store= Roo.factory(this.store, Roo.data);
5162         this.ds = this.store;
5163         this.ds.xmodule = this.xmodule || false;
5164          
5165     }
5166     if (this.footer && this.store) {
5167         this.footer.dataSource = this.ds;
5168         this.footer = Roo.factory(this.footer);
5169     }
5170     
5171     /** @private */
5172     this.addEvents({
5173         /**
5174          * @event cellclick
5175          * Fires when a cell is clicked
5176          * @param {Roo.bootstrap.Table} this
5177          * @param {Roo.Element} el
5178          * @param {Number} rowIndex
5179          * @param {Number} columnIndex
5180          * @param {Roo.EventObject} e
5181          */
5182         "cellclick" : true,
5183         /**
5184          * @event celldblclick
5185          * Fires when a cell is double clicked
5186          * @param {Roo.bootstrap.Table} this
5187          * @param {Roo.Element} el
5188          * @param {Number} rowIndex
5189          * @param {Number} columnIndex
5190          * @param {Roo.EventObject} e
5191          */
5192         "celldblclick" : true,
5193         /**
5194          * @event rowclick
5195          * Fires when a row is clicked
5196          * @param {Roo.bootstrap.Table} this
5197          * @param {Roo.Element} el
5198          * @param {Number} rowIndex
5199          * @param {Roo.EventObject} e
5200          */
5201         "rowclick" : true,
5202         /**
5203          * @event rowdblclick
5204          * Fires when a row is double clicked
5205          * @param {Roo.bootstrap.Table} this
5206          * @param {Roo.Element} el
5207          * @param {Number} rowIndex
5208          * @param {Roo.EventObject} e
5209          */
5210         "rowdblclick" : true,
5211         /**
5212          * @event mouseover
5213          * Fires when a mouseover occur
5214          * @param {Roo.bootstrap.Table} this
5215          * @param {Roo.Element} el
5216          * @param {Number} rowIndex
5217          * @param {Number} columnIndex
5218          * @param {Roo.EventObject} e
5219          */
5220         "mouseover" : true,
5221         /**
5222          * @event mouseout
5223          * Fires when a mouseout occur
5224          * @param {Roo.bootstrap.Table} this
5225          * @param {Roo.Element} el
5226          * @param {Number} rowIndex
5227          * @param {Number} columnIndex
5228          * @param {Roo.EventObject} e
5229          */
5230         "mouseout" : true,
5231         /**
5232          * @event rowclass
5233          * Fires when a row is rendered, so you can change add a style to it.
5234          * @param {Roo.bootstrap.Table} this
5235          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5236          */
5237         'rowclass' : true,
5238           /**
5239          * @event rowsrendered
5240          * Fires when all the  rows have been rendered
5241          * @param {Roo.bootstrap.Table} this
5242          */
5243         'rowsrendered' : true
5244         
5245     });
5246 };
5247
5248 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5249     
5250     cls: false,
5251     align: false,
5252     bgcolor: false,
5253     border: false,
5254     cellpadding: false,
5255     cellspacing: false,
5256     frame: false,
5257     rules: false,
5258     sortable: false,
5259     summary: false,
5260     width: false,
5261     striped : false,
5262     bordered: false,
5263     hover:  false,
5264     condensed : false,
5265     responsive : false,
5266     sm : false,
5267     cm : false,
5268     store : false,
5269     loadMask : false,
5270     tfoot : true,
5271     thead : true,
5272     RowSelection : false,
5273     CellSelection : false,
5274     layout : false,
5275     
5276     // Roo.Element - the tbody
5277     mainBody: false, 
5278     
5279     getAutoCreate : function(){
5280         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5281         
5282         cfg = {
5283             tag: 'table',
5284             cls : 'table',
5285             cn : []
5286         }
5287             
5288         if (this.striped) {
5289             cfg.cls += ' table-striped';
5290         }
5291         
5292         if (this.hover) {
5293             cfg.cls += ' table-hover';
5294         }
5295         if (this.bordered) {
5296             cfg.cls += ' table-bordered';
5297         }
5298         if (this.condensed) {
5299             cfg.cls += ' table-condensed';
5300         }
5301         if (this.responsive) {
5302             cfg.cls += ' table-responsive';
5303         }
5304         
5305         if (this.cls) {
5306             cfg.cls+=  ' ' +this.cls;
5307         }
5308         
5309         // this lot should be simplifed...
5310         
5311         if (this.align) {
5312             cfg.align=this.align;
5313         }
5314         if (this.bgcolor) {
5315             cfg.bgcolor=this.bgcolor;
5316         }
5317         if (this.border) {
5318             cfg.border=this.border;
5319         }
5320         if (this.cellpadding) {
5321             cfg.cellpadding=this.cellpadding;
5322         }
5323         if (this.cellspacing) {
5324             cfg.cellspacing=this.cellspacing;
5325         }
5326         if (this.frame) {
5327             cfg.frame=this.frame;
5328         }
5329         if (this.rules) {
5330             cfg.rules=this.rules;
5331         }
5332         if (this.sortable) {
5333             cfg.sortable=this.sortable;
5334         }
5335         if (this.summary) {
5336             cfg.summary=this.summary;
5337         }
5338         if (this.width) {
5339             cfg.width=this.width;
5340         }
5341         if (this.layout) {
5342             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5343         }
5344         
5345         if(this.store || this.cm){
5346             if(this.thead){
5347                 cfg.cn.push(this.renderHeader());
5348             }
5349             
5350             cfg.cn.push(this.renderBody());
5351             
5352             if(this.tfoot){
5353                 cfg.cn.push(this.renderFooter());
5354             }
5355             
5356             cfg.cls+=  ' TableGrid';
5357         }
5358         
5359         return { cn : [ cfg ] };
5360     },
5361     
5362     initEvents : function()
5363     {   
5364         if(!this.store || !this.cm){
5365             return;
5366         }
5367         
5368         //Roo.log('initEvents with ds!!!!');
5369         
5370         this.mainBody = this.el.select('tbody', true).first();
5371         
5372         
5373         var _this = this;
5374         
5375         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5376             e.on('click', _this.sort, _this);
5377         });
5378         
5379         this.el.on("click", this.onClick, this);
5380         this.el.on("dblclick", this.onDblClick, this);
5381         
5382         // why is this done????? = it breaks dialogs??
5383         //this.parent().el.setStyle('position', 'relative');
5384         
5385         
5386         if (this.footer) {
5387             this.footer.parentId = this.id;
5388             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5389         }
5390         
5391         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5392         
5393         this.store.on('load', this.onLoad, this);
5394         this.store.on('beforeload', this.onBeforeLoad, this);
5395         this.store.on('update', this.onUpdate, this);
5396         this.store.on('add', this.onAdd, this);
5397         
5398     },
5399     
5400     onMouseover : function(e, el)
5401     {
5402         var cell = Roo.get(el);
5403         
5404         if(!cell){
5405             return;
5406         }
5407         
5408         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5409             cell = cell.findParent('td', false, true);
5410         }
5411         
5412         var row = cell.findParent('tr', false, true);
5413         var cellIndex = cell.dom.cellIndex;
5414         var rowIndex = row.dom.rowIndex - 1; // start from 0
5415         
5416         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5417         
5418     },
5419     
5420     onMouseout : function(e, el)
5421     {
5422         var cell = Roo.get(el);
5423         
5424         if(!cell){
5425             return;
5426         }
5427         
5428         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5429             cell = cell.findParent('td', false, true);
5430         }
5431         
5432         var row = cell.findParent('tr', false, true);
5433         var cellIndex = cell.dom.cellIndex;
5434         var rowIndex = row.dom.rowIndex - 1; // start from 0
5435         
5436         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5437         
5438     },
5439     
5440     onClick : function(e, el)
5441     {
5442         var cell = Roo.get(el);
5443         
5444         if(!cell || (!this.CellSelection && !this.RowSelection)){
5445             return;
5446         }
5447         
5448         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5449             cell = cell.findParent('td', false, true);
5450         }
5451         
5452         if(!cell || typeof(cell) == 'undefined'){
5453             return;
5454         }
5455         
5456         var row = cell.findParent('tr', false, true);
5457         
5458         if(!row || typeof(row) == 'undefined'){
5459             return;
5460         }
5461         
5462         var cellIndex = cell.dom.cellIndex;
5463         var rowIndex = this.getRowIndex(row);
5464         
5465         if(this.CellSelection){
5466             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5467         }
5468         
5469         if(this.RowSelection){
5470             this.fireEvent('rowclick', this, row, rowIndex, e);
5471         }
5472         
5473         
5474     },
5475     
5476     onDblClick : function(e,el)
5477     {
5478         var cell = Roo.get(el);
5479         
5480         if(!cell || (!this.CellSelection && !this.RowSelection)){
5481             return;
5482         }
5483         
5484         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5485             cell = cell.findParent('td', false, true);
5486         }
5487         
5488         if(!cell || typeof(cell) == 'undefined'){
5489             return;
5490         }
5491         
5492         var row = cell.findParent('tr', false, true);
5493         
5494         if(!row || typeof(row) == 'undefined'){
5495             return;
5496         }
5497         
5498         var cellIndex = cell.dom.cellIndex;
5499         var rowIndex = this.getRowIndex(row);
5500         
5501         if(this.CellSelection){
5502             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5503         }
5504         
5505         if(this.RowSelection){
5506             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5507         }
5508     },
5509     
5510     sort : function(e,el)
5511     {
5512         var col = Roo.get(el);
5513         
5514         if(!col.hasClass('sortable')){
5515             return;
5516         }
5517         
5518         var sort = col.attr('sort');
5519         var dir = 'ASC';
5520         
5521         if(col.hasClass('glyphicon-arrow-up')){
5522             dir = 'DESC';
5523         }
5524         
5525         this.store.sortInfo = {field : sort, direction : dir};
5526         
5527         if (this.footer) {
5528             Roo.log("calling footer first");
5529             this.footer.onClick('first');
5530         } else {
5531         
5532             this.store.load({ params : { start : 0 } });
5533         }
5534     },
5535     
5536     renderHeader : function()
5537     {
5538         var header = {
5539             tag: 'thead',
5540             cn : []
5541         };
5542         
5543         var cm = this.cm;
5544         
5545         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5546             
5547             var config = cm.config[i];
5548                     
5549             var c = {
5550                 tag: 'th',
5551                 style : '',
5552                 html: cm.getColumnHeader(i)
5553             };
5554             
5555             if(typeof(config.tooltip) != 'undefined'){
5556                 c.tooltip = config.tooltip;
5557             }
5558             
5559             if(typeof(config.colspan) != 'undefined'){
5560                 c.colspan = config.colspan;
5561             }
5562             
5563             if(typeof(config.hidden) != 'undefined' && config.hidden){
5564                 c.style += ' display:none;';
5565             }
5566             
5567             if(typeof(config.dataIndex) != 'undefined'){
5568                 c.sort = config.dataIndex;
5569             }
5570             
5571             if(typeof(config.sortable) != 'undefined' && config.sortable){
5572                 c.cls = 'sortable';
5573             }
5574             
5575             if(typeof(config.align) != 'undefined' && config.align.length){
5576                 c.style += ' text-align:' + config.align + ';';
5577             }
5578             
5579             if(typeof(config.width) != 'undefined'){
5580                 c.style += ' width:' + config.width + 'px;';
5581             }
5582             
5583             if(typeof(config.cls) != 'undefined'){
5584                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5585             }
5586             
5587             header.cn.push(c)
5588         }
5589         
5590         return header;
5591     },
5592     
5593     renderBody : function()
5594     {
5595         var body = {
5596             tag: 'tbody',
5597             cn : [
5598                 {
5599                     tag: 'tr',
5600                     cn : [
5601                         {
5602                             tag : 'td',
5603                             colspan :  this.cm.getColumnCount()
5604                         }
5605                     ]
5606                 }
5607             ]
5608         };
5609         
5610         return body;
5611     },
5612     
5613     renderFooter : function()
5614     {
5615         var footer = {
5616             tag: 'tfoot',
5617             cn : [
5618                 {
5619                     tag: 'tr',
5620                     cn : [
5621                         {
5622                             tag : 'td',
5623                             colspan :  this.cm.getColumnCount()
5624                         }
5625                     ]
5626                 }
5627             ]
5628         };
5629         
5630         return footer;
5631     },
5632     
5633     
5634     
5635     onLoad : function()
5636     {
5637         Roo.log('ds onload');
5638         this.clear();
5639         
5640         var _this = this;
5641         var cm = this.cm;
5642         var ds = this.store;
5643         
5644         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5645             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5646             
5647             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5648                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5649             }
5650             
5651             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5652                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5653             }
5654         });
5655         
5656         var tbody =  this.mainBody;
5657               
5658         if(ds.getCount() > 0){
5659             ds.data.each(function(d,rowIndex){
5660                 var row =  this.renderRow(cm, ds, rowIndex);
5661                 
5662                 tbody.createChild(row);
5663                 
5664                 var _this = this;
5665                 
5666                 if(row.cellObjects.length){
5667                     Roo.each(row.cellObjects, function(r){
5668                         _this.renderCellObject(r);
5669                     })
5670                 }
5671                 
5672             }, this);
5673         }
5674         
5675         Roo.each(this.el.select('tbody td', true).elements, function(e){
5676             e.on('mouseover', _this.onMouseover, _this);
5677         });
5678         
5679         Roo.each(this.el.select('tbody td', true).elements, function(e){
5680             e.on('mouseout', _this.onMouseout, _this);
5681         });
5682         this.fireEvent('rowsrendered', this);
5683         //if(this.loadMask){
5684         //    this.maskEl.hide();
5685         //}
5686     },
5687     
5688     
5689     onUpdate : function(ds,record)
5690     {
5691         this.refreshRow(record);
5692     },
5693     
5694     onRemove : function(ds, record, index, isUpdate){
5695         if(isUpdate !== true){
5696             this.fireEvent("beforerowremoved", this, index, record);
5697         }
5698         var bt = this.mainBody.dom;
5699         
5700         var rows = this.el.select('tbody > tr', true).elements;
5701         
5702         if(typeof(rows[index]) != 'undefined'){
5703             bt.removeChild(rows[index].dom);
5704         }
5705         
5706 //        if(bt.rows[index]){
5707 //            bt.removeChild(bt.rows[index]);
5708 //        }
5709         
5710         if(isUpdate !== true){
5711             //this.stripeRows(index);
5712             //this.syncRowHeights(index, index);
5713             //this.layout();
5714             this.fireEvent("rowremoved", this, index, record);
5715         }
5716     },
5717     
5718     onAdd : function(ds, records, rowIndex)
5719     {
5720         //Roo.log('on Add called');
5721         // - note this does not handle multiple adding very well..
5722         var bt = this.mainBody.dom;
5723         for (var i =0 ; i < records.length;i++) {
5724             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5725             //Roo.log(records[i]);
5726             //Roo.log(this.store.getAt(rowIndex+i));
5727             this.insertRow(this.store, rowIndex + i, false);
5728             return;
5729         }
5730         
5731     },
5732     
5733     
5734     refreshRow : function(record){
5735         var ds = this.store, index;
5736         if(typeof record == 'number'){
5737             index = record;
5738             record = ds.getAt(index);
5739         }else{
5740             index = ds.indexOf(record);
5741         }
5742         this.insertRow(ds, index, true);
5743         this.onRemove(ds, record, index+1, true);
5744         //this.syncRowHeights(index, index);
5745         //this.layout();
5746         this.fireEvent("rowupdated", this, index, record);
5747     },
5748     
5749     insertRow : function(dm, rowIndex, isUpdate){
5750         
5751         if(!isUpdate){
5752             this.fireEvent("beforerowsinserted", this, rowIndex);
5753         }
5754             //var s = this.getScrollState();
5755         var row = this.renderRow(this.cm, this.store, rowIndex);
5756         // insert before rowIndex..
5757         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5758         
5759         var _this = this;
5760                 
5761         if(row.cellObjects.length){
5762             Roo.each(row.cellObjects, function(r){
5763                 _this.renderCellObject(r);
5764             })
5765         }
5766             
5767         if(!isUpdate){
5768             this.fireEvent("rowsinserted", this, rowIndex);
5769             //this.syncRowHeights(firstRow, lastRow);
5770             //this.stripeRows(firstRow);
5771             //this.layout();
5772         }
5773         
5774     },
5775     
5776     
5777     getRowDom : function(rowIndex)
5778     {
5779         var rows = this.el.select('tbody > tr', true).elements;
5780         
5781         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
5782         
5783     },
5784     // returns the object tree for a tr..
5785   
5786     
5787     renderRow : function(cm, ds, rowIndex) 
5788     {
5789         
5790         var d = ds.getAt(rowIndex);
5791         
5792         var row = {
5793             tag : 'tr',
5794             cn : []
5795         };
5796             
5797         var cellObjects = [];
5798         
5799         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5800             var config = cm.config[i];
5801             
5802             var renderer = cm.getRenderer(i);
5803             var value = '';
5804             var id = false;
5805             
5806             if(typeof(renderer) !== 'undefined'){
5807                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5808             }
5809             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5810             // and are rendered into the cells after the row is rendered - using the id for the element.
5811             
5812             if(typeof(value) === 'object'){
5813                 id = Roo.id();
5814                 cellObjects.push({
5815                     container : id,
5816                     cfg : value 
5817                 })
5818             }
5819             
5820             var rowcfg = {
5821                 record: d,
5822                 rowIndex : rowIndex,
5823                 colIndex : i,
5824                 rowClass : ''
5825             }
5826
5827             this.fireEvent('rowclass', this, rowcfg);
5828             
5829             var td = {
5830                 tag: 'td',
5831                 cls : rowcfg.rowClass,
5832                 style: '',
5833                 html: (typeof(value) === 'object') ? '' : value
5834             };
5835             
5836             if (id) {
5837                 td.id = id;
5838             }
5839             
5840             if(typeof(config.colspan) != 'undefined'){
5841                 td.colspan = config.colspan;
5842             }
5843             
5844             if(typeof(config.hidden) != 'undefined' && config.hidden){
5845                 td.style += ' display:none;';
5846             }
5847             
5848             if(typeof(config.align) != 'undefined' && config.align.length){
5849                 td.style += ' text-align:' + config.align + ';';
5850             }
5851             
5852             if(typeof(config.width) != 'undefined'){
5853                 td.style += ' width:' +  config.width + 'px;';
5854             }
5855             
5856             if(typeof(config.cursor) != 'undefined'){
5857                 td.style += ' cursor:' +  config.cursor + ';';
5858             }
5859             
5860             if(typeof(config.cls) != 'undefined'){
5861                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
5862             }
5863              
5864             row.cn.push(td);
5865            
5866         }
5867         
5868         row.cellObjects = cellObjects;
5869         
5870         return row;
5871           
5872     },
5873     
5874     
5875     
5876     onBeforeLoad : function()
5877     {
5878         //Roo.log('ds onBeforeLoad');
5879         
5880         //this.clear();
5881         
5882         //if(this.loadMask){
5883         //    this.maskEl.show();
5884         //}
5885     },
5886      /**
5887      * Remove all rows
5888      */
5889     clear : function()
5890     {
5891         this.el.select('tbody', true).first().dom.innerHTML = '';
5892     },
5893     /**
5894      * Show or hide a row.
5895      * @param {Number} rowIndex to show or hide
5896      * @param {Boolean} state hide
5897      */
5898     setRowVisibility : function(rowIndex, state)
5899     {
5900         var bt = this.mainBody.dom;
5901         
5902         var rows = this.el.select('tbody > tr', true).elements;
5903         
5904         if(typeof(rows[rowIndex]) == 'undefined'){
5905             return;
5906         }
5907         rows[rowIndex].dom.style.display = state ? '' : 'none';
5908     },
5909     
5910     
5911     getSelectionModel : function(){
5912         if(!this.selModel){
5913             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5914         }
5915         return this.selModel;
5916     },
5917     /*
5918      * Render the Roo.bootstrap object from renderder
5919      */
5920     renderCellObject : function(r)
5921     {
5922         var _this = this;
5923         
5924         var t = r.cfg.render(r.container);
5925         
5926         if(r.cfg.cn){
5927             Roo.each(r.cfg.cn, function(c){
5928                 var child = {
5929                     container: t.getChildContainer(),
5930                     cfg: c
5931                 }
5932                 _this.renderCellObject(child);
5933             })
5934         }
5935     },
5936     
5937     getRowIndex : function(row)
5938     {
5939         var rowIndex = -1;
5940         
5941         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
5942             if(el != row){
5943                 return;
5944             }
5945             
5946             rowIndex = index;
5947         });
5948         
5949         return rowIndex;
5950     }
5951    
5952 });
5953
5954  
5955
5956  /*
5957  * - LGPL
5958  *
5959  * table cell
5960  * 
5961  */
5962
5963 /**
5964  * @class Roo.bootstrap.TableCell
5965  * @extends Roo.bootstrap.Component
5966  * Bootstrap TableCell class
5967  * @cfg {String} html cell contain text
5968  * @cfg {String} cls cell class
5969  * @cfg {String} tag cell tag (td|th) default td
5970  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5971  * @cfg {String} align Aligns the content in a cell
5972  * @cfg {String} axis Categorizes cells
5973  * @cfg {String} bgcolor Specifies the background color of a cell
5974  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5975  * @cfg {Number} colspan Specifies the number of columns a cell should span
5976  * @cfg {String} headers Specifies one or more header cells a cell is related to
5977  * @cfg {Number} height Sets the height of a cell
5978  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5979  * @cfg {Number} rowspan Sets the number of rows a cell should span
5980  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5981  * @cfg {String} valign Vertical aligns the content in a cell
5982  * @cfg {Number} width Specifies the width of a cell
5983  * 
5984  * @constructor
5985  * Create a new TableCell
5986  * @param {Object} config The config object
5987  */
5988
5989 Roo.bootstrap.TableCell = function(config){
5990     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5991 };
5992
5993 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5994     
5995     html: false,
5996     cls: false,
5997     tag: false,
5998     abbr: false,
5999     align: false,
6000     axis: false,
6001     bgcolor: false,
6002     charoff: false,
6003     colspan: false,
6004     headers: false,
6005     height: false,
6006     nowrap: false,
6007     rowspan: false,
6008     scope: false,
6009     valign: false,
6010     width: false,
6011     
6012     
6013     getAutoCreate : function(){
6014         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6015         
6016         cfg = {
6017             tag: 'td'
6018         }
6019         
6020         if(this.tag){
6021             cfg.tag = this.tag;
6022         }
6023         
6024         if (this.html) {
6025             cfg.html=this.html
6026         }
6027         if (this.cls) {
6028             cfg.cls=this.cls
6029         }
6030         if (this.abbr) {
6031             cfg.abbr=this.abbr
6032         }
6033         if (this.align) {
6034             cfg.align=this.align
6035         }
6036         if (this.axis) {
6037             cfg.axis=this.axis
6038         }
6039         if (this.bgcolor) {
6040             cfg.bgcolor=this.bgcolor
6041         }
6042         if (this.charoff) {
6043             cfg.charoff=this.charoff
6044         }
6045         if (this.colspan) {
6046             cfg.colspan=this.colspan
6047         }
6048         if (this.headers) {
6049             cfg.headers=this.headers
6050         }
6051         if (this.height) {
6052             cfg.height=this.height
6053         }
6054         if (this.nowrap) {
6055             cfg.nowrap=this.nowrap
6056         }
6057         if (this.rowspan) {
6058             cfg.rowspan=this.rowspan
6059         }
6060         if (this.scope) {
6061             cfg.scope=this.scope
6062         }
6063         if (this.valign) {
6064             cfg.valign=this.valign
6065         }
6066         if (this.width) {
6067             cfg.width=this.width
6068         }
6069         
6070         
6071         return cfg;
6072     }
6073    
6074 });
6075
6076  
6077
6078  /*
6079  * - LGPL
6080  *
6081  * table row
6082  * 
6083  */
6084
6085 /**
6086  * @class Roo.bootstrap.TableRow
6087  * @extends Roo.bootstrap.Component
6088  * Bootstrap TableRow class
6089  * @cfg {String} cls row class
6090  * @cfg {String} align Aligns the content in a table row
6091  * @cfg {String} bgcolor Specifies a background color for a table row
6092  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6093  * @cfg {String} valign Vertical aligns the content in a table row
6094  * 
6095  * @constructor
6096  * Create a new TableRow
6097  * @param {Object} config The config object
6098  */
6099
6100 Roo.bootstrap.TableRow = function(config){
6101     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6102 };
6103
6104 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6105     
6106     cls: false,
6107     align: false,
6108     bgcolor: false,
6109     charoff: false,
6110     valign: false,
6111     
6112     getAutoCreate : function(){
6113         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6114         
6115         cfg = {
6116             tag: 'tr'
6117         }
6118             
6119         if(this.cls){
6120             cfg.cls = this.cls;
6121         }
6122         if(this.align){
6123             cfg.align = this.align;
6124         }
6125         if(this.bgcolor){
6126             cfg.bgcolor = this.bgcolor;
6127         }
6128         if(this.charoff){
6129             cfg.charoff = this.charoff;
6130         }
6131         if(this.valign){
6132             cfg.valign = this.valign;
6133         }
6134         
6135         return cfg;
6136     }
6137    
6138 });
6139
6140  
6141
6142  /*
6143  * - LGPL
6144  *
6145  * table body
6146  * 
6147  */
6148
6149 /**
6150  * @class Roo.bootstrap.TableBody
6151  * @extends Roo.bootstrap.Component
6152  * Bootstrap TableBody class
6153  * @cfg {String} cls element class
6154  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6155  * @cfg {String} align Aligns the content inside the element
6156  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6157  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6158  * 
6159  * @constructor
6160  * Create a new TableBody
6161  * @param {Object} config The config object
6162  */
6163
6164 Roo.bootstrap.TableBody = function(config){
6165     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6166 };
6167
6168 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6169     
6170     cls: false,
6171     tag: false,
6172     align: false,
6173     charoff: false,
6174     valign: false,
6175     
6176     getAutoCreate : function(){
6177         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6178         
6179         cfg = {
6180             tag: 'tbody'
6181         }
6182             
6183         if (this.cls) {
6184             cfg.cls=this.cls
6185         }
6186         if(this.tag){
6187             cfg.tag = this.tag;
6188         }
6189         
6190         if(this.align){
6191             cfg.align = this.align;
6192         }
6193         if(this.charoff){
6194             cfg.charoff = this.charoff;
6195         }
6196         if(this.valign){
6197             cfg.valign = this.valign;
6198         }
6199         
6200         return cfg;
6201     }
6202     
6203     
6204 //    initEvents : function()
6205 //    {
6206 //        
6207 //        if(!this.store){
6208 //            return;
6209 //        }
6210 //        
6211 //        this.store = Roo.factory(this.store, Roo.data);
6212 //        this.store.on('load', this.onLoad, this);
6213 //        
6214 //        this.store.load();
6215 //        
6216 //    },
6217 //    
6218 //    onLoad: function () 
6219 //    {   
6220 //        this.fireEvent('load', this);
6221 //    }
6222 //    
6223 //   
6224 });
6225
6226  
6227
6228  /*
6229  * Based on:
6230  * Ext JS Library 1.1.1
6231  * Copyright(c) 2006-2007, Ext JS, LLC.
6232  *
6233  * Originally Released Under LGPL - original licence link has changed is not relivant.
6234  *
6235  * Fork - LGPL
6236  * <script type="text/javascript">
6237  */
6238
6239 // as we use this in bootstrap.
6240 Roo.namespace('Roo.form');
6241  /**
6242  * @class Roo.form.Action
6243  * Internal Class used to handle form actions
6244  * @constructor
6245  * @param {Roo.form.BasicForm} el The form element or its id
6246  * @param {Object} config Configuration options
6247  */
6248
6249  
6250  
6251 // define the action interface
6252 Roo.form.Action = function(form, options){
6253     this.form = form;
6254     this.options = options || {};
6255 };
6256 /**
6257  * Client Validation Failed
6258  * @const 
6259  */
6260 Roo.form.Action.CLIENT_INVALID = 'client';
6261 /**
6262  * Server Validation Failed
6263  * @const 
6264  */
6265 Roo.form.Action.SERVER_INVALID = 'server';
6266  /**
6267  * Connect to Server Failed
6268  * @const 
6269  */
6270 Roo.form.Action.CONNECT_FAILURE = 'connect';
6271 /**
6272  * Reading Data from Server Failed
6273  * @const 
6274  */
6275 Roo.form.Action.LOAD_FAILURE = 'load';
6276
6277 Roo.form.Action.prototype = {
6278     type : 'default',
6279     failureType : undefined,
6280     response : undefined,
6281     result : undefined,
6282
6283     // interface method
6284     run : function(options){
6285
6286     },
6287
6288     // interface method
6289     success : function(response){
6290
6291     },
6292
6293     // interface method
6294     handleResponse : function(response){
6295
6296     },
6297
6298     // default connection failure
6299     failure : function(response){
6300         
6301         this.response = response;
6302         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6303         this.form.afterAction(this, false);
6304     },
6305
6306     processResponse : function(response){
6307         this.response = response;
6308         if(!response.responseText){
6309             return true;
6310         }
6311         this.result = this.handleResponse(response);
6312         return this.result;
6313     },
6314
6315     // utility functions used internally
6316     getUrl : function(appendParams){
6317         var url = this.options.url || this.form.url || this.form.el.dom.action;
6318         if(appendParams){
6319             var p = this.getParams();
6320             if(p){
6321                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6322             }
6323         }
6324         return url;
6325     },
6326
6327     getMethod : function(){
6328         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6329     },
6330
6331     getParams : function(){
6332         var bp = this.form.baseParams;
6333         var p = this.options.params;
6334         if(p){
6335             if(typeof p == "object"){
6336                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6337             }else if(typeof p == 'string' && bp){
6338                 p += '&' + Roo.urlEncode(bp);
6339             }
6340         }else if(bp){
6341             p = Roo.urlEncode(bp);
6342         }
6343         return p;
6344     },
6345
6346     createCallback : function(){
6347         return {
6348             success: this.success,
6349             failure: this.failure,
6350             scope: this,
6351             timeout: (this.form.timeout*1000),
6352             upload: this.form.fileUpload ? this.success : undefined
6353         };
6354     }
6355 };
6356
6357 Roo.form.Action.Submit = function(form, options){
6358     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6359 };
6360
6361 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6362     type : 'submit',
6363
6364     haveProgress : false,
6365     uploadComplete : false,
6366     
6367     // uploadProgress indicator.
6368     uploadProgress : function()
6369     {
6370         if (!this.form.progressUrl) {
6371             return;
6372         }
6373         
6374         if (!this.haveProgress) {
6375             Roo.MessageBox.progress("Uploading", "Uploading");
6376         }
6377         if (this.uploadComplete) {
6378            Roo.MessageBox.hide();
6379            return;
6380         }
6381         
6382         this.haveProgress = true;
6383    
6384         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6385         
6386         var c = new Roo.data.Connection();
6387         c.request({
6388             url : this.form.progressUrl,
6389             params: {
6390                 id : uid
6391             },
6392             method: 'GET',
6393             success : function(req){
6394                //console.log(data);
6395                 var rdata = false;
6396                 var edata;
6397                 try  {
6398                    rdata = Roo.decode(req.responseText)
6399                 } catch (e) {
6400                     Roo.log("Invalid data from server..");
6401                     Roo.log(edata);
6402                     return;
6403                 }
6404                 if (!rdata || !rdata.success) {
6405                     Roo.log(rdata);
6406                     Roo.MessageBox.alert(Roo.encode(rdata));
6407                     return;
6408                 }
6409                 var data = rdata.data;
6410                 
6411                 if (this.uploadComplete) {
6412                    Roo.MessageBox.hide();
6413                    return;
6414                 }
6415                    
6416                 if (data){
6417                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6418                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6419                     );
6420                 }
6421                 this.uploadProgress.defer(2000,this);
6422             },
6423        
6424             failure: function(data) {
6425                 Roo.log('progress url failed ');
6426                 Roo.log(data);
6427             },
6428             scope : this
6429         });
6430            
6431     },
6432     
6433     
6434     run : function()
6435     {
6436         // run get Values on the form, so it syncs any secondary forms.
6437         this.form.getValues();
6438         
6439         var o = this.options;
6440         var method = this.getMethod();
6441         var isPost = method == 'POST';
6442         if(o.clientValidation === false || this.form.isValid()){
6443             
6444             if (this.form.progressUrl) {
6445                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6446                     (new Date() * 1) + '' + Math.random());
6447                     
6448             } 
6449             
6450             
6451             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6452                 form:this.form.el.dom,
6453                 url:this.getUrl(!isPost),
6454                 method: method,
6455                 params:isPost ? this.getParams() : null,
6456                 isUpload: this.form.fileUpload
6457             }));
6458             
6459             this.uploadProgress();
6460
6461         }else if (o.clientValidation !== false){ // client validation failed
6462             this.failureType = Roo.form.Action.CLIENT_INVALID;
6463             this.form.afterAction(this, false);
6464         }
6465     },
6466
6467     success : function(response)
6468     {
6469         this.uploadComplete= true;
6470         if (this.haveProgress) {
6471             Roo.MessageBox.hide();
6472         }
6473         
6474         
6475         var result = this.processResponse(response);
6476         if(result === true || result.success){
6477             this.form.afterAction(this, true);
6478             return;
6479         }
6480         if(result.errors){
6481             this.form.markInvalid(result.errors);
6482             this.failureType = Roo.form.Action.SERVER_INVALID;
6483         }
6484         this.form.afterAction(this, false);
6485     },
6486     failure : function(response)
6487     {
6488         this.uploadComplete= true;
6489         if (this.haveProgress) {
6490             Roo.MessageBox.hide();
6491         }
6492         
6493         this.response = response;
6494         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6495         this.form.afterAction(this, false);
6496     },
6497     
6498     handleResponse : function(response){
6499         if(this.form.errorReader){
6500             var rs = this.form.errorReader.read(response);
6501             var errors = [];
6502             if(rs.records){
6503                 for(var i = 0, len = rs.records.length; i < len; i++) {
6504                     var r = rs.records[i];
6505                     errors[i] = r.data;
6506                 }
6507             }
6508             if(errors.length < 1){
6509                 errors = null;
6510             }
6511             return {
6512                 success : rs.success,
6513                 errors : errors
6514             };
6515         }
6516         var ret = false;
6517         try {
6518             ret = Roo.decode(response.responseText);
6519         } catch (e) {
6520             ret = {
6521                 success: false,
6522                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6523                 errors : []
6524             };
6525         }
6526         return ret;
6527         
6528     }
6529 });
6530
6531
6532 Roo.form.Action.Load = function(form, options){
6533     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6534     this.reader = this.form.reader;
6535 };
6536
6537 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6538     type : 'load',
6539
6540     run : function(){
6541         
6542         Roo.Ajax.request(Roo.apply(
6543                 this.createCallback(), {
6544                     method:this.getMethod(),
6545                     url:this.getUrl(false),
6546                     params:this.getParams()
6547         }));
6548     },
6549
6550     success : function(response){
6551         
6552         var result = this.processResponse(response);
6553         if(result === true || !result.success || !result.data){
6554             this.failureType = Roo.form.Action.LOAD_FAILURE;
6555             this.form.afterAction(this, false);
6556             return;
6557         }
6558         this.form.clearInvalid();
6559         this.form.setValues(result.data);
6560         this.form.afterAction(this, true);
6561     },
6562
6563     handleResponse : function(response){
6564         if(this.form.reader){
6565             var rs = this.form.reader.read(response);
6566             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6567             return {
6568                 success : rs.success,
6569                 data : data
6570             };
6571         }
6572         return Roo.decode(response.responseText);
6573     }
6574 });
6575
6576 Roo.form.Action.ACTION_TYPES = {
6577     'load' : Roo.form.Action.Load,
6578     'submit' : Roo.form.Action.Submit
6579 };/*
6580  * - LGPL
6581  *
6582  * form
6583  * 
6584  */
6585
6586 /**
6587  * @class Roo.bootstrap.Form
6588  * @extends Roo.bootstrap.Component
6589  * Bootstrap Form class
6590  * @cfg {String} method  GET | POST (default POST)
6591  * @cfg {String} labelAlign top | left (default top)
6592  * @cfg {String} align left  | right - for navbars
6593  * @cfg {Boolean} loadMask load mask when submit (default true)
6594
6595  * 
6596  * @constructor
6597  * Create a new Form
6598  * @param {Object} config The config object
6599  */
6600
6601
6602 Roo.bootstrap.Form = function(config){
6603     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6604     this.addEvents({
6605         /**
6606          * @event clientvalidation
6607          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6608          * @param {Form} this
6609          * @param {Boolean} valid true if the form has passed client-side validation
6610          */
6611         clientvalidation: true,
6612         /**
6613          * @event beforeaction
6614          * Fires before any action is performed. Return false to cancel the action.
6615          * @param {Form} this
6616          * @param {Action} action The action to be performed
6617          */
6618         beforeaction: true,
6619         /**
6620          * @event actionfailed
6621          * Fires when an action fails.
6622          * @param {Form} this
6623          * @param {Action} action The action that failed
6624          */
6625         actionfailed : true,
6626         /**
6627          * @event actioncomplete
6628          * Fires when an action is completed.
6629          * @param {Form} this
6630          * @param {Action} action The action that completed
6631          */
6632         actioncomplete : true
6633     });
6634     
6635 };
6636
6637 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6638       
6639      /**
6640      * @cfg {String} method
6641      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6642      */
6643     method : 'POST',
6644     /**
6645      * @cfg {String} url
6646      * The URL to use for form actions if one isn't supplied in the action options.
6647      */
6648     /**
6649      * @cfg {Boolean} fileUpload
6650      * Set to true if this form is a file upload.
6651      */
6652      
6653     /**
6654      * @cfg {Object} baseParams
6655      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6656      */
6657       
6658     /**
6659      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6660      */
6661     timeout: 30,
6662     /**
6663      * @cfg {Sting} align (left|right) for navbar forms
6664      */
6665     align : 'left',
6666
6667     // private
6668     activeAction : null,
6669  
6670     /**
6671      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6672      * element by passing it or its id or mask the form itself by passing in true.
6673      * @type Mixed
6674      */
6675     waitMsgTarget : false,
6676     
6677     loadMask : true,
6678     
6679     getAutoCreate : function(){
6680         
6681         var cfg = {
6682             tag: 'form',
6683             method : this.method || 'POST',
6684             id : this.id || Roo.id(),
6685             cls : ''
6686         }
6687         if (this.parent().xtype.match(/^Nav/)) {
6688             cfg.cls = 'navbar-form navbar-' + this.align;
6689             
6690         }
6691         
6692         if (this.labelAlign == 'left' ) {
6693             cfg.cls += ' form-horizontal';
6694         }
6695         
6696         
6697         return cfg;
6698     },
6699     initEvents : function()
6700     {
6701         this.el.on('submit', this.onSubmit, this);
6702         // this was added as random key presses on the form where triggering form submit.
6703         this.el.on('keypress', function(e) {
6704             if (e.getCharCode() != 13) {
6705                 return true;
6706             }
6707             // we might need to allow it for textareas.. and some other items.
6708             // check e.getTarget().
6709             
6710             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6711                 return true;
6712             }
6713         
6714             Roo.log("keypress blocked");
6715             
6716             e.preventDefault();
6717             return false;
6718         });
6719         
6720     },
6721     // private
6722     onSubmit : function(e){
6723         e.stopEvent();
6724     },
6725     
6726      /**
6727      * Returns true if client-side validation on the form is successful.
6728      * @return Boolean
6729      */
6730     isValid : function(){
6731         var items = this.getItems();
6732         var valid = true;
6733         items.each(function(f){
6734            if(!f.validate()){
6735                valid = false;
6736                
6737            }
6738         });
6739         return valid;
6740     },
6741     /**
6742      * Returns true if any fields in this form have changed since their original load.
6743      * @return Boolean
6744      */
6745     isDirty : function(){
6746         var dirty = false;
6747         var items = this.getItems();
6748         items.each(function(f){
6749            if(f.isDirty()){
6750                dirty = true;
6751                return false;
6752            }
6753            return true;
6754         });
6755         return dirty;
6756     },
6757      /**
6758      * Performs a predefined action (submit or load) or custom actions you define on this form.
6759      * @param {String} actionName The name of the action type
6760      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6761      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6762      * accept other config options):
6763      * <pre>
6764 Property          Type             Description
6765 ----------------  ---------------  ----------------------------------------------------------------------------------
6766 url               String           The url for the action (defaults to the form's url)
6767 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6768 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6769 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6770                                    validate the form on the client (defaults to false)
6771      * </pre>
6772      * @return {BasicForm} this
6773      */
6774     doAction : function(action, options){
6775         if(typeof action == 'string'){
6776             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6777         }
6778         if(this.fireEvent('beforeaction', this, action) !== false){
6779             this.beforeAction(action);
6780             action.run.defer(100, action);
6781         }
6782         return this;
6783     },
6784     
6785     // private
6786     beforeAction : function(action){
6787         var o = action.options;
6788         
6789         if(this.loadMask){
6790             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6791         }
6792         // not really supported yet.. ??
6793         
6794         //if(this.waitMsgTarget === true){
6795         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6796         //}else if(this.waitMsgTarget){
6797         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6798         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6799         //}else {
6800         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6801        // }
6802          
6803     },
6804
6805     // private
6806     afterAction : function(action, success){
6807         this.activeAction = null;
6808         var o = action.options;
6809         
6810         //if(this.waitMsgTarget === true){
6811             this.el.unmask();
6812         //}else if(this.waitMsgTarget){
6813         //    this.waitMsgTarget.unmask();
6814         //}else{
6815         //    Roo.MessageBox.updateProgress(1);
6816         //    Roo.MessageBox.hide();
6817        // }
6818         // 
6819         if(success){
6820             if(o.reset){
6821                 this.reset();
6822             }
6823             Roo.callback(o.success, o.scope, [this, action]);
6824             this.fireEvent('actioncomplete', this, action);
6825             
6826         }else{
6827             
6828             // failure condition..
6829             // we have a scenario where updates need confirming.
6830             // eg. if a locking scenario exists..
6831             // we look for { errors : { needs_confirm : true }} in the response.
6832             if (
6833                 (typeof(action.result) != 'undefined')  &&
6834                 (typeof(action.result.errors) != 'undefined')  &&
6835                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6836            ){
6837                 var _t = this;
6838                 Roo.log("not supported yet");
6839                  /*
6840                 
6841                 Roo.MessageBox.confirm(
6842                     "Change requires confirmation",
6843                     action.result.errorMsg,
6844                     function(r) {
6845                         if (r != 'yes') {
6846                             return;
6847                         }
6848                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6849                     }
6850                     
6851                 );
6852                 */
6853                 
6854                 
6855                 return;
6856             }
6857             
6858             Roo.callback(o.failure, o.scope, [this, action]);
6859             // show an error message if no failed handler is set..
6860             if (!this.hasListener('actionfailed')) {
6861                 Roo.log("need to add dialog support");
6862                 /*
6863                 Roo.MessageBox.alert("Error",
6864                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6865                         action.result.errorMsg :
6866                         "Saving Failed, please check your entries or try again"
6867                 );
6868                 */
6869             }
6870             
6871             this.fireEvent('actionfailed', this, action);
6872         }
6873         
6874     },
6875     /**
6876      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6877      * @param {String} id The value to search for
6878      * @return Field
6879      */
6880     findField : function(id){
6881         var items = this.getItems();
6882         var field = items.get(id);
6883         if(!field){
6884              items.each(function(f){
6885                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6886                     field = f;
6887                     return false;
6888                 }
6889                 return true;
6890             });
6891         }
6892         return field || null;
6893     },
6894      /**
6895      * Mark fields in this form invalid in bulk.
6896      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6897      * @return {BasicForm} this
6898      */
6899     markInvalid : function(errors){
6900         if(errors instanceof Array){
6901             for(var i = 0, len = errors.length; i < len; i++){
6902                 var fieldError = errors[i];
6903                 var f = this.findField(fieldError.id);
6904                 if(f){
6905                     f.markInvalid(fieldError.msg);
6906                 }
6907             }
6908         }else{
6909             var field, id;
6910             for(id in errors){
6911                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6912                     field.markInvalid(errors[id]);
6913                 }
6914             }
6915         }
6916         //Roo.each(this.childForms || [], function (f) {
6917         //    f.markInvalid(errors);
6918         //});
6919         
6920         return this;
6921     },
6922
6923     /**
6924      * Set values for fields in this form in bulk.
6925      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6926      * @return {BasicForm} this
6927      */
6928     setValues : function(values){
6929         if(values instanceof Array){ // array of objects
6930             for(var i = 0, len = values.length; i < len; i++){
6931                 var v = values[i];
6932                 var f = this.findField(v.id);
6933                 if(f){
6934                     f.setValue(v.value);
6935                     if(this.trackResetOnLoad){
6936                         f.originalValue = f.getValue();
6937                     }
6938                 }
6939             }
6940         }else{ // object hash
6941             var field, id;
6942             for(id in values){
6943                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6944                     
6945                     if (field.setFromData && 
6946                         field.valueField && 
6947                         field.displayField &&
6948                         // combos' with local stores can 
6949                         // be queried via setValue()
6950                         // to set their value..
6951                         (field.store && !field.store.isLocal)
6952                         ) {
6953                         // it's a combo
6954                         var sd = { };
6955                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6956                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6957                         field.setFromData(sd);
6958                         
6959                     } else {
6960                         field.setValue(values[id]);
6961                     }
6962                     
6963                     
6964                     if(this.trackResetOnLoad){
6965                         field.originalValue = field.getValue();
6966                     }
6967                 }
6968             }
6969         }
6970          
6971         //Roo.each(this.childForms || [], function (f) {
6972         //    f.setValues(values);
6973         //});
6974                 
6975         return this;
6976     },
6977
6978     /**
6979      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6980      * they are returned as an array.
6981      * @param {Boolean} asString
6982      * @return {Object}
6983      */
6984     getValues : function(asString){
6985         //if (this.childForms) {
6986             // copy values from the child forms
6987         //    Roo.each(this.childForms, function (f) {
6988         //        this.setValues(f.getValues());
6989         //    }, this);
6990         //}
6991         
6992         
6993         
6994         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6995         if(asString === true){
6996             return fs;
6997         }
6998         return Roo.urlDecode(fs);
6999     },
7000     
7001     /**
7002      * Returns the fields in this form as an object with key/value pairs. 
7003      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7004      * @return {Object}
7005      */
7006     getFieldValues : function(with_hidden)
7007     {
7008         var items = this.getItems();
7009         var ret = {};
7010         items.each(function(f){
7011             if (!f.getName()) {
7012                 return;
7013             }
7014             var v = f.getValue();
7015             if (f.inputType =='radio') {
7016                 if (typeof(ret[f.getName()]) == 'undefined') {
7017                     ret[f.getName()] = ''; // empty..
7018                 }
7019                 
7020                 if (!f.el.dom.checked) {
7021                     return;
7022                     
7023                 }
7024                 v = f.el.dom.value;
7025                 
7026             }
7027             
7028             // not sure if this supported any more..
7029             if ((typeof(v) == 'object') && f.getRawValue) {
7030                 v = f.getRawValue() ; // dates..
7031             }
7032             // combo boxes where name != hiddenName...
7033             if (f.name != f.getName()) {
7034                 ret[f.name] = f.getRawValue();
7035             }
7036             ret[f.getName()] = v;
7037         });
7038         
7039         return ret;
7040     },
7041
7042     /**
7043      * Clears all invalid messages in this form.
7044      * @return {BasicForm} this
7045      */
7046     clearInvalid : function(){
7047         var items = this.getItems();
7048         
7049         items.each(function(f){
7050            f.clearInvalid();
7051         });
7052         
7053         
7054         
7055         return this;
7056     },
7057
7058     /**
7059      * Resets this form.
7060      * @return {BasicForm} this
7061      */
7062     reset : function(){
7063         var items = this.getItems();
7064         items.each(function(f){
7065             f.reset();
7066         });
7067         
7068         Roo.each(this.childForms || [], function (f) {
7069             f.reset();
7070         });
7071        
7072         
7073         return this;
7074     },
7075     getItems : function()
7076     {
7077         var r=new Roo.util.MixedCollection(false, function(o){
7078             return o.id || (o.id = Roo.id());
7079         });
7080         var iter = function(el) {
7081             if (el.inputEl) {
7082                 r.add(el);
7083             }
7084             if (!el.items) {
7085                 return;
7086             }
7087             Roo.each(el.items,function(e) {
7088                 iter(e);
7089             });
7090             
7091             
7092         };
7093         
7094         iter(this);
7095         return r;
7096         
7097         
7098         
7099         
7100     }
7101     
7102 });
7103
7104  
7105 /*
7106  * Based on:
7107  * Ext JS Library 1.1.1
7108  * Copyright(c) 2006-2007, Ext JS, LLC.
7109  *
7110  * Originally Released Under LGPL - original licence link has changed is not relivant.
7111  *
7112  * Fork - LGPL
7113  * <script type="text/javascript">
7114  */
7115 /**
7116  * @class Roo.form.VTypes
7117  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7118  * @singleton
7119  */
7120 Roo.form.VTypes = function(){
7121     // closure these in so they are only created once.
7122     var alpha = /^[a-zA-Z_]+$/;
7123     var alphanum = /^[a-zA-Z0-9_]+$/;
7124     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7125     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7126
7127     // All these messages and functions are configurable
7128     return {
7129         /**
7130          * The function used to validate email addresses
7131          * @param {String} value The email address
7132          */
7133         'email' : function(v){
7134             return email.test(v);
7135         },
7136         /**
7137          * The error text to display when the email validation function returns false
7138          * @type String
7139          */
7140         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7141         /**
7142          * The keystroke filter mask to be applied on email input
7143          * @type RegExp
7144          */
7145         'emailMask' : /[a-z0-9_\.\-@]/i,
7146
7147         /**
7148          * The function used to validate URLs
7149          * @param {String} value The URL
7150          */
7151         'url' : function(v){
7152             return url.test(v);
7153         },
7154         /**
7155          * The error text to display when the url validation function returns false
7156          * @type String
7157          */
7158         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7159         
7160         /**
7161          * The function used to validate alpha values
7162          * @param {String} value The value
7163          */
7164         'alpha' : function(v){
7165             return alpha.test(v);
7166         },
7167         /**
7168          * The error text to display when the alpha validation function returns false
7169          * @type String
7170          */
7171         'alphaText' : 'This field should only contain letters and _',
7172         /**
7173          * The keystroke filter mask to be applied on alpha input
7174          * @type RegExp
7175          */
7176         'alphaMask' : /[a-z_]/i,
7177
7178         /**
7179          * The function used to validate alphanumeric values
7180          * @param {String} value The value
7181          */
7182         'alphanum' : function(v){
7183             return alphanum.test(v);
7184         },
7185         /**
7186          * The error text to display when the alphanumeric validation function returns false
7187          * @type String
7188          */
7189         'alphanumText' : 'This field should only contain letters, numbers and _',
7190         /**
7191          * The keystroke filter mask to be applied on alphanumeric input
7192          * @type RegExp
7193          */
7194         'alphanumMask' : /[a-z0-9_]/i
7195     };
7196 }();/*
7197  * - LGPL
7198  *
7199  * Input
7200  * 
7201  */
7202
7203 /**
7204  * @class Roo.bootstrap.Input
7205  * @extends Roo.bootstrap.Component
7206  * Bootstrap Input class
7207  * @cfg {Boolean} disabled is it disabled
7208  * @cfg {String} fieldLabel - the label associated
7209  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7210  * @cfg {String} name name of the input
7211  * @cfg {string} fieldLabel - the label associated
7212  * @cfg {string}  inputType - input / file submit ...
7213  * @cfg {string} placeholder - placeholder to put in text.
7214  * @cfg {string}  before - input group add on before
7215  * @cfg {string} after - input group add on after
7216  * @cfg {string} size - (lg|sm) or leave empty..
7217  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7218  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7219  * @cfg {Number} md colspan out of 12 for computer-sized screens
7220  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7221  * @cfg {string} value default value of the input
7222  * @cfg {Number} labelWidth set the width of label (0-12)
7223  * @cfg {String} labelAlign (top|left)
7224  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7225  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7226
7227  * @cfg {String} align (left|center|right) Default left
7228  * 
7229  * 
7230  * 
7231  * @constructor
7232  * Create a new Input
7233  * @param {Object} config The config object
7234  */
7235
7236 Roo.bootstrap.Input = function(config){
7237     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7238    
7239         this.addEvents({
7240             /**
7241              * @event focus
7242              * Fires when this field receives input focus.
7243              * @param {Roo.form.Field} this
7244              */
7245             focus : true,
7246             /**
7247              * @event blur
7248              * Fires when this field loses input focus.
7249              * @param {Roo.form.Field} this
7250              */
7251             blur : true,
7252             /**
7253              * @event specialkey
7254              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7255              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7256              * @param {Roo.form.Field} this
7257              * @param {Roo.EventObject} e The event object
7258              */
7259             specialkey : true,
7260             /**
7261              * @event change
7262              * Fires just before the field blurs if the field value has changed.
7263              * @param {Roo.form.Field} this
7264              * @param {Mixed} newValue The new value
7265              * @param {Mixed} oldValue The original value
7266              */
7267             change : true,
7268             /**
7269              * @event invalid
7270              * Fires after the field has been marked as invalid.
7271              * @param {Roo.form.Field} this
7272              * @param {String} msg The validation message
7273              */
7274             invalid : true,
7275             /**
7276              * @event valid
7277              * Fires after the field has been validated with no errors.
7278              * @param {Roo.form.Field} this
7279              */
7280             valid : true,
7281              /**
7282              * @event keyup
7283              * Fires after the key up
7284              * @param {Roo.form.Field} this
7285              * @param {Roo.EventObject}  e The event Object
7286              */
7287             keyup : true
7288         });
7289 };
7290
7291 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7292      /**
7293      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7294       automatic validation (defaults to "keyup").
7295      */
7296     validationEvent : "keyup",
7297      /**
7298      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7299      */
7300     validateOnBlur : true,
7301     /**
7302      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7303      */
7304     validationDelay : 250,
7305      /**
7306      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7307      */
7308     focusClass : "x-form-focus",  // not needed???
7309     
7310        
7311     /**
7312      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7313      */
7314     invalidClass : "has-warning",
7315     
7316     /**
7317      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7318      */
7319     validClass : "has-success",
7320     
7321     /**
7322      * @cfg {Boolean} hasFeedback (true|false) default true
7323      */
7324     hasFeedback : true,
7325     
7326     /**
7327      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7328      */
7329     invalidFeedbackClass : "glyphicon-warning-sign",
7330     
7331     /**
7332      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7333      */
7334     validFeedbackClass : "glyphicon-ok",
7335     
7336     /**
7337      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7338      */
7339     selectOnFocus : false,
7340     
7341      /**
7342      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7343      */
7344     maskRe : null,
7345        /**
7346      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7347      */
7348     vtype : null,
7349     
7350       /**
7351      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7352      */
7353     disableKeyFilter : false,
7354     
7355        /**
7356      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7357      */
7358     disabled : false,
7359      /**
7360      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7361      */
7362     allowBlank : true,
7363     /**
7364      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7365      */
7366     blankText : "This field is required",
7367     
7368      /**
7369      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7370      */
7371     minLength : 0,
7372     /**
7373      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7374      */
7375     maxLength : Number.MAX_VALUE,
7376     /**
7377      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7378      */
7379     minLengthText : "The minimum length for this field is {0}",
7380     /**
7381      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7382      */
7383     maxLengthText : "The maximum length for this field is {0}",
7384   
7385     
7386     /**
7387      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7388      * If available, this function will be called only after the basic validators all return true, and will be passed the
7389      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7390      */
7391     validator : null,
7392     /**
7393      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7394      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7395      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7396      */
7397     regex : null,
7398     /**
7399      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7400      */
7401     regexText : "",
7402     
7403     autocomplete: false,
7404     
7405     
7406     fieldLabel : '',
7407     inputType : 'text',
7408     
7409     name : false,
7410     placeholder: false,
7411     before : false,
7412     after : false,
7413     size : false,
7414     hasFocus : false,
7415     preventMark: false,
7416     isFormField : true,
7417     value : '',
7418     labelWidth : 2,
7419     labelAlign : false,
7420     readOnly : false,
7421     align : false,
7422     formatedValue : false,
7423     
7424     parentLabelAlign : function()
7425     {
7426         var parent = this;
7427         while (parent.parent()) {
7428             parent = parent.parent();
7429             if (typeof(parent.labelAlign) !='undefined') {
7430                 return parent.labelAlign;
7431             }
7432         }
7433         return 'left';
7434         
7435     },
7436     
7437     getAutoCreate : function(){
7438         
7439         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7440         
7441         var id = Roo.id();
7442         
7443         var cfg = {};
7444         
7445         if(this.inputType != 'hidden'){
7446             cfg.cls = 'form-group' //input-group
7447         }
7448         
7449         var input =  {
7450             tag: 'input',
7451             id : id,
7452             type : this.inputType,
7453             value : this.value,
7454             cls : 'form-control',
7455             placeholder : this.placeholder || '',
7456             autocomplete : this.autocomplete || 'new-password'
7457         };
7458         
7459         
7460         if(this.align){
7461             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7462         }
7463         
7464         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7465             input.maxLength = this.maxLength;
7466         }
7467         
7468         if (this.disabled) {
7469             input.disabled=true;
7470         }
7471         
7472         if (this.readOnly) {
7473             input.readonly=true;
7474         }
7475         
7476         if (this.name) {
7477             input.name = this.name;
7478         }
7479         if (this.size) {
7480             input.cls += ' input-' + this.size;
7481         }
7482         var settings=this;
7483         ['xs','sm','md','lg'].map(function(size){
7484             if (settings[size]) {
7485                 cfg.cls += ' col-' + size + '-' + settings[size];
7486             }
7487         });
7488         
7489         var inputblock = input;
7490         
7491         var feedback = {
7492             tag: 'span',
7493             cls: 'glyphicon form-control-feedback'
7494         };
7495             
7496         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7497             
7498             inputblock = {
7499                 cls : 'has-feedback',
7500                 cn :  [
7501                     input,
7502                     feedback
7503                 ] 
7504             };  
7505         }
7506         
7507         if (this.before || this.after) {
7508             
7509             inputblock = {
7510                 cls : 'input-group',
7511                 cn :  [] 
7512             };
7513             
7514             if (this.before && typeof(this.before) == 'string') {
7515                 
7516                 inputblock.cn.push({
7517                     tag :'span',
7518                     cls : 'roo-input-before input-group-addon',
7519                     html : this.before
7520                 });
7521             }
7522             if (this.before && typeof(this.before) == 'object') {
7523                 this.before = Roo.factory(this.before);
7524                 Roo.log(this.before);
7525                 inputblock.cn.push({
7526                     tag :'span',
7527                     cls : 'roo-input-before input-group-' +
7528                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7529                 });
7530             }
7531             
7532             inputblock.cn.push(input);
7533             
7534             if (this.after && typeof(this.after) == 'string') {
7535                 inputblock.cn.push({
7536                     tag :'span',
7537                     cls : 'roo-input-after input-group-addon',
7538                     html : this.after
7539                 });
7540             }
7541             if (this.after && typeof(this.after) == 'object') {
7542                 this.after = Roo.factory(this.after);
7543                 Roo.log(this.after);
7544                 inputblock.cn.push({
7545                     tag :'span',
7546                     cls : 'roo-input-after input-group-' +
7547                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7548                 });
7549             }
7550             
7551             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7552                 inputblock.cls += ' has-feedback';
7553                 inputblock.cn.push(feedback);
7554             }
7555         };
7556         
7557         if (align ==='left' && this.fieldLabel.length) {
7558                 Roo.log("left and has label");
7559                 cfg.cn = [
7560                     
7561                     {
7562                         tag: 'label',
7563                         'for' :  id,
7564                         cls : 'control-label col-sm-' + this.labelWidth,
7565                         html : this.fieldLabel
7566                         
7567                     },
7568                     {
7569                         cls : "col-sm-" + (12 - this.labelWidth), 
7570                         cn: [
7571                             inputblock
7572                         ]
7573                     }
7574                     
7575                 ];
7576         } else if ( this.fieldLabel.length) {
7577                 Roo.log(" label");
7578                  cfg.cn = [
7579                    
7580                     {
7581                         tag: 'label',
7582                         //cls : 'input-group-addon',
7583                         html : this.fieldLabel
7584                         
7585                     },
7586                     
7587                     inputblock
7588                     
7589                 ];
7590
7591         } else {
7592             
7593                 Roo.log(" no label && no align");
7594                 cfg.cn = [
7595                     
7596                         inputblock
7597                     
7598                 ];
7599                 
7600                 
7601         };
7602         Roo.log('input-parentType: ' + this.parentType);
7603         
7604         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7605            cfg.cls += ' navbar-form';
7606            Roo.log(cfg);
7607         }
7608         
7609         return cfg;
7610         
7611     },
7612     /**
7613      * return the real input element.
7614      */
7615     inputEl: function ()
7616     {
7617         return this.el.select('input.form-control',true).first();
7618     },
7619     
7620     tooltipEl : function()
7621     {
7622         return this.inputEl();
7623     },
7624     
7625     setDisabled : function(v)
7626     {
7627         var i  = this.inputEl().dom;
7628         if (!v) {
7629             i.removeAttribute('disabled');
7630             return;
7631             
7632         }
7633         i.setAttribute('disabled','true');
7634     },
7635     initEvents : function()
7636     {
7637           
7638         this.inputEl().on("keydown" , this.fireKey,  this);
7639         this.inputEl().on("focus", this.onFocus,  this);
7640         this.inputEl().on("blur", this.onBlur,  this);
7641         
7642         this.inputEl().relayEvent('keyup', this);
7643
7644         // reference to original value for reset
7645         this.originalValue = this.getValue();
7646         //Roo.form.TextField.superclass.initEvents.call(this);
7647         if(this.validationEvent == 'keyup'){
7648             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7649             this.inputEl().on('keyup', this.filterValidation, this);
7650         }
7651         else if(this.validationEvent !== false){
7652             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7653         }
7654         
7655         if(this.selectOnFocus){
7656             this.on("focus", this.preFocus, this);
7657             
7658         }
7659         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7660             this.inputEl().on("keypress", this.filterKeys, this);
7661         }
7662        /* if(this.grow){
7663             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7664             this.el.on("click", this.autoSize,  this);
7665         }
7666         */
7667         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7668             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7669         }
7670         
7671         if (typeof(this.before) == 'object') {
7672             this.before.render(this.el.select('.roo-input-before',true).first());
7673         }
7674         if (typeof(this.after) == 'object') {
7675             this.after.render(this.el.select('.roo-input-after',true).first());
7676         }
7677         
7678         
7679     },
7680     filterValidation : function(e){
7681         if(!e.isNavKeyPress()){
7682             this.validationTask.delay(this.validationDelay);
7683         }
7684     },
7685      /**
7686      * Validates the field value
7687      * @return {Boolean} True if the value is valid, else false
7688      */
7689     validate : function(){
7690         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7691         if(this.disabled || this.validateValue(this.getRawValue())){
7692             this.markValid();
7693             return true;
7694         }
7695         
7696         this.markInvalid();
7697         return false;
7698     },
7699     
7700     
7701     /**
7702      * Validates a value according to the field's validation rules and marks the field as invalid
7703      * if the validation fails
7704      * @param {Mixed} value The value to validate
7705      * @return {Boolean} True if the value is valid, else false
7706      */
7707     validateValue : function(value){
7708         if(value.length < 1)  { // if it's blank
7709             if(this.allowBlank){
7710                 return true;
7711             }
7712             return false;
7713         }
7714         
7715         if(value.length < this.minLength){
7716             return false;
7717         }
7718         if(value.length > this.maxLength){
7719             return false;
7720         }
7721         if(this.vtype){
7722             var vt = Roo.form.VTypes;
7723             if(!vt[this.vtype](value, this)){
7724                 return false;
7725             }
7726         }
7727         if(typeof this.validator == "function"){
7728             var msg = this.validator(value);
7729             if(msg !== true){
7730                 return false;
7731             }
7732         }
7733         
7734         if(this.regex && !this.regex.test(value)){
7735             return false;
7736         }
7737         
7738         return true;
7739     },
7740
7741     
7742     
7743      // private
7744     fireKey : function(e){
7745         //Roo.log('field ' + e.getKey());
7746         if(e.isNavKeyPress()){
7747             this.fireEvent("specialkey", this, e);
7748         }
7749     },
7750     focus : function (selectText){
7751         if(this.rendered){
7752             this.inputEl().focus();
7753             if(selectText === true){
7754                 this.inputEl().dom.select();
7755             }
7756         }
7757         return this;
7758     } ,
7759     
7760     onFocus : function(){
7761         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7762            // this.el.addClass(this.focusClass);
7763         }
7764         if(!this.hasFocus){
7765             this.hasFocus = true;
7766             this.startValue = this.getValue();
7767             this.fireEvent("focus", this);
7768         }
7769     },
7770     
7771     beforeBlur : Roo.emptyFn,
7772
7773     
7774     // private
7775     onBlur : function(){
7776         this.beforeBlur();
7777         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7778             //this.el.removeClass(this.focusClass);
7779         }
7780         this.hasFocus = false;
7781         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7782             this.validate();
7783         }
7784         var v = this.getValue();
7785         if(String(v) !== String(this.startValue)){
7786             this.fireEvent('change', this, v, this.startValue);
7787         }
7788         this.fireEvent("blur", this);
7789     },
7790     
7791     /**
7792      * Resets the current field value to the originally loaded value and clears any validation messages
7793      */
7794     reset : function(){
7795         this.setValue(this.originalValue);
7796         this.validate();
7797     },
7798      /**
7799      * Returns the name of the field
7800      * @return {Mixed} name The name field
7801      */
7802     getName: function(){
7803         return this.name;
7804     },
7805      /**
7806      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7807      * @return {Mixed} value The field value
7808      */
7809     getValue : function(){
7810         
7811         var v = this.inputEl().getValue();
7812         
7813         return v;
7814     },
7815     /**
7816      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7817      * @return {Mixed} value The field value
7818      */
7819     getRawValue : function(){
7820         var v = this.inputEl().getValue();
7821         
7822         return v;
7823     },
7824     
7825     /**
7826      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7827      * @param {Mixed} value The value to set
7828      */
7829     setRawValue : function(v){
7830         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7831     },
7832     
7833     selectText : function(start, end){
7834         var v = this.getRawValue();
7835         if(v.length > 0){
7836             start = start === undefined ? 0 : start;
7837             end = end === undefined ? v.length : end;
7838             var d = this.inputEl().dom;
7839             if(d.setSelectionRange){
7840                 d.setSelectionRange(start, end);
7841             }else if(d.createTextRange){
7842                 var range = d.createTextRange();
7843                 range.moveStart("character", start);
7844                 range.moveEnd("character", v.length-end);
7845                 range.select();
7846             }
7847         }
7848     },
7849     
7850     /**
7851      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7852      * @param {Mixed} value The value to set
7853      */
7854     setValue : function(v){
7855         this.value = v;
7856         if(this.rendered){
7857             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7858             this.validate();
7859         }
7860     },
7861     
7862     /*
7863     processValue : function(value){
7864         if(this.stripCharsRe){
7865             var newValue = value.replace(this.stripCharsRe, '');
7866             if(newValue !== value){
7867                 this.setRawValue(newValue);
7868                 return newValue;
7869             }
7870         }
7871         return value;
7872     },
7873   */
7874     preFocus : function(){
7875         
7876         if(this.selectOnFocus){
7877             this.inputEl().dom.select();
7878         }
7879     },
7880     filterKeys : function(e){
7881         var k = e.getKey();
7882         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7883             return;
7884         }
7885         var c = e.getCharCode(), cc = String.fromCharCode(c);
7886         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7887             return;
7888         }
7889         if(!this.maskRe.test(cc)){
7890             e.stopEvent();
7891         }
7892     },
7893      /**
7894      * Clear any invalid styles/messages for this field
7895      */
7896     clearInvalid : function(){
7897         
7898         if(!this.el || this.preventMark){ // not rendered
7899             return;
7900         }
7901         this.el.removeClass(this.invalidClass);
7902         
7903         this.fireEvent('valid', this);
7904     },
7905     
7906      /**
7907      * Mark this field as valid
7908      */
7909     markValid : function(){
7910         if(!this.el  || this.preventMark){ // not rendered
7911             return;
7912         }
7913         
7914         this.el.removeClass([this.invalidClass, this.validClass]);
7915         
7916         if(this.disabled || this.allowBlank){
7917             return;
7918         }
7919         
7920         this.el.addClass(this.validClass);
7921         
7922         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
7923             
7924             var feedback = this.el.select('.form-control-feedback', true).first();
7925             
7926             if(feedback){
7927                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
7928                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
7929             }
7930             
7931         }
7932         
7933         this.fireEvent('valid', this);
7934     },
7935     
7936      /**
7937      * Mark this field as invalid
7938      * @param {String} msg The validation message
7939      */
7940     markInvalid : function(msg){
7941         if(!this.el  || this.preventMark){ // not rendered
7942             return;
7943         }
7944         
7945         this.el.removeClass([this.invalidClass, this.validClass]);
7946         
7947         if(this.disabled || this.allowBlank){
7948             return;
7949         }
7950         
7951         this.el.addClass(this.invalidClass);
7952         
7953         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7954             
7955             var feedback = this.el.select('.form-control-feedback', true).first();
7956             
7957             if(feedback){
7958                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
7959                 
7960                 if(this.getValue().length){
7961                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
7962                 }
7963                 
7964             }
7965             
7966         }
7967         
7968         this.fireEvent('invalid', this, msg);
7969     },
7970     // private
7971     SafariOnKeyDown : function(event)
7972     {
7973         // this is a workaround for a password hang bug on chrome/ webkit.
7974         
7975         var isSelectAll = false;
7976         
7977         if(this.inputEl().dom.selectionEnd > 0){
7978             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7979         }
7980         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7981             event.preventDefault();
7982             this.setValue('');
7983             return;
7984         }
7985         
7986         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
7987             
7988             event.preventDefault();
7989             // this is very hacky as keydown always get's upper case.
7990             //
7991             var cc = String.fromCharCode(event.getCharCode());
7992             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7993             
7994         }
7995     },
7996     adjustWidth : function(tag, w){
7997         tag = tag.toLowerCase();
7998         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7999             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8000                 if(tag == 'input'){
8001                     return w + 2;
8002                 }
8003                 if(tag == 'textarea'){
8004                     return w-2;
8005                 }
8006             }else if(Roo.isOpera){
8007                 if(tag == 'input'){
8008                     return w + 2;
8009                 }
8010                 if(tag == 'textarea'){
8011                     return w-2;
8012                 }
8013             }
8014         }
8015         return w;
8016     }
8017     
8018 });
8019
8020  
8021 /*
8022  * - LGPL
8023  *
8024  * Input
8025  * 
8026  */
8027
8028 /**
8029  * @class Roo.bootstrap.TextArea
8030  * @extends Roo.bootstrap.Input
8031  * Bootstrap TextArea class
8032  * @cfg {Number} cols Specifies the visible width of a text area
8033  * @cfg {Number} rows Specifies the visible number of lines in a text area
8034  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8035  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8036  * @cfg {string} html text
8037  * 
8038  * @constructor
8039  * Create a new TextArea
8040  * @param {Object} config The config object
8041  */
8042
8043 Roo.bootstrap.TextArea = function(config){
8044     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8045    
8046 };
8047
8048 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8049      
8050     cols : false,
8051     rows : 5,
8052     readOnly : false,
8053     warp : 'soft',
8054     resize : false,
8055     value: false,
8056     html: false,
8057     
8058     getAutoCreate : function(){
8059         
8060         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8061         
8062         var id = Roo.id();
8063         
8064         var cfg = {};
8065         
8066         var input =  {
8067             tag: 'textarea',
8068             id : id,
8069             warp : this.warp,
8070             rows : this.rows,
8071             value : this.value || '',
8072             html: this.html || '',
8073             cls : 'form-control',
8074             placeholder : this.placeholder || '' 
8075             
8076         };
8077         
8078         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8079             input.maxLength = this.maxLength;
8080         }
8081         
8082         if(this.resize){
8083             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8084         }
8085         
8086         if(this.cols){
8087             input.cols = this.cols;
8088         }
8089         
8090         if (this.readOnly) {
8091             input.readonly = true;
8092         }
8093         
8094         if (this.name) {
8095             input.name = this.name;
8096         }
8097         
8098         if (this.size) {
8099             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8100         }
8101         
8102         var settings=this;
8103         ['xs','sm','md','lg'].map(function(size){
8104             if (settings[size]) {
8105                 cfg.cls += ' col-' + size + '-' + settings[size];
8106             }
8107         });
8108         
8109         var inputblock = input;
8110         
8111         if(this.hasFeedback && !this.allowBlank){
8112             
8113             var feedback = {
8114                 tag: 'span',
8115                 cls: 'glyphicon form-control-feedback'
8116             };
8117
8118             inputblock = {
8119                 cls : 'has-feedback',
8120                 cn :  [
8121                     input,
8122                     feedback
8123                 ] 
8124             };  
8125         }
8126         
8127         
8128         if (this.before || this.after) {
8129             
8130             inputblock = {
8131                 cls : 'input-group',
8132                 cn :  [] 
8133             };
8134             if (this.before) {
8135                 inputblock.cn.push({
8136                     tag :'span',
8137                     cls : 'input-group-addon',
8138                     html : this.before
8139                 });
8140             }
8141             
8142             inputblock.cn.push(input);
8143             
8144             if(this.hasFeedback && !this.allowBlank){
8145                 inputblock.cls += ' has-feedback';
8146                 inputblock.cn.push(feedback);
8147             }
8148             
8149             if (this.after) {
8150                 inputblock.cn.push({
8151                     tag :'span',
8152                     cls : 'input-group-addon',
8153                     html : this.after
8154                 });
8155             }
8156             
8157         }
8158         
8159         if (align ==='left' && this.fieldLabel.length) {
8160                 Roo.log("left and has label");
8161                 cfg.cn = [
8162                     
8163                     {
8164                         tag: 'label',
8165                         'for' :  id,
8166                         cls : 'control-label col-sm-' + this.labelWidth,
8167                         html : this.fieldLabel
8168                         
8169                     },
8170                     {
8171                         cls : "col-sm-" + (12 - this.labelWidth), 
8172                         cn: [
8173                             inputblock
8174                         ]
8175                     }
8176                     
8177                 ];
8178         } else if ( this.fieldLabel.length) {
8179                 Roo.log(" label");
8180                  cfg.cn = [
8181                    
8182                     {
8183                         tag: 'label',
8184                         //cls : 'input-group-addon',
8185                         html : this.fieldLabel
8186                         
8187                     },
8188                     
8189                     inputblock
8190                     
8191                 ];
8192
8193         } else {
8194             
8195                    Roo.log(" no label && no align");
8196                 cfg.cn = [
8197                     
8198                         inputblock
8199                     
8200                 ];
8201                 
8202                 
8203         }
8204         
8205         if (this.disabled) {
8206             input.disabled=true;
8207         }
8208         
8209         return cfg;
8210         
8211     },
8212     /**
8213      * return the real textarea element.
8214      */
8215     inputEl: function ()
8216     {
8217         return this.el.select('textarea.form-control',true).first();
8218     }
8219 });
8220
8221  
8222 /*
8223  * - LGPL
8224  *
8225  * trigger field - base class for combo..
8226  * 
8227  */
8228  
8229 /**
8230  * @class Roo.bootstrap.TriggerField
8231  * @extends Roo.bootstrap.Input
8232  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8233  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8234  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8235  * for which you can provide a custom implementation.  For example:
8236  * <pre><code>
8237 var trigger = new Roo.bootstrap.TriggerField();
8238 trigger.onTriggerClick = myTriggerFn;
8239 trigger.applyTo('my-field');
8240 </code></pre>
8241  *
8242  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8243  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8244  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8245  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8246  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8247
8248  * @constructor
8249  * Create a new TriggerField.
8250  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8251  * to the base TextField)
8252  */
8253 Roo.bootstrap.TriggerField = function(config){
8254     this.mimicing = false;
8255     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8256 };
8257
8258 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8259     /**
8260      * @cfg {String} triggerClass A CSS class to apply to the trigger
8261      */
8262      /**
8263      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8264      */
8265     hideTrigger:false,
8266
8267     /** @cfg {Boolean} grow @hide */
8268     /** @cfg {Number} growMin @hide */
8269     /** @cfg {Number} growMax @hide */
8270
8271     /**
8272      * @hide 
8273      * @method
8274      */
8275     autoSize: Roo.emptyFn,
8276     // private
8277     monitorTab : true,
8278     // private
8279     deferHeight : true,
8280
8281     
8282     actionMode : 'wrap',
8283     
8284     caret : false,
8285     
8286     
8287     getAutoCreate : function(){
8288        
8289         var align = this.labelAlign || this.parentLabelAlign();
8290         
8291         var id = Roo.id();
8292         
8293         var cfg = {
8294             cls: 'form-group' //input-group
8295         };
8296         
8297         
8298         var input =  {
8299             tag: 'input',
8300             id : id,
8301             type : this.inputType,
8302             cls : 'form-control',
8303             autocomplete: 'new-password',
8304             placeholder : this.placeholder || '' 
8305             
8306         };
8307         if (this.name) {
8308             input.name = this.name;
8309         }
8310         if (this.size) {
8311             input.cls += ' input-' + this.size;
8312         }
8313         
8314         if (this.disabled) {
8315             input.disabled=true;
8316         }
8317         
8318         var inputblock = input;
8319         
8320         if(this.hasFeedback && !this.allowBlank){
8321             
8322             var feedback = {
8323                 tag: 'span',
8324                 cls: 'glyphicon form-control-feedback'
8325             };
8326
8327             inputblock = {
8328                 cls : 'has-feedback',
8329                 cn :  [
8330                     input,
8331                     feedback
8332                 ] 
8333             };  
8334         }
8335         
8336         if (this.before || this.after) {
8337             
8338             inputblock = {
8339                 cls : 'input-group',
8340                 cn :  [] 
8341             };
8342             if (this.before) {
8343                 inputblock.cn.push({
8344                     tag :'span',
8345                     cls : 'input-group-addon',
8346                     html : this.before
8347                 });
8348             }
8349             
8350             inputblock.cn.push(input);
8351             
8352             if(this.hasFeedback && !this.allowBlank){
8353                 inputblock.cls += ' has-feedback';
8354                 inputblock.cn.push(feedback);
8355             }
8356             
8357             if (this.after) {
8358                 inputblock.cn.push({
8359                     tag :'span',
8360                     cls : 'input-group-addon',
8361                     html : this.after
8362                 });
8363             }
8364             
8365         };
8366         
8367         var box = {
8368             tag: 'div',
8369             cn: [
8370                 {
8371                     tag: 'input',
8372                     type : 'hidden',
8373                     cls: 'form-hidden-field'
8374                 },
8375                 inputblock
8376             ]
8377             
8378         };
8379         
8380         if(this.multiple){
8381             Roo.log('multiple');
8382             
8383             box = {
8384                 tag: 'div',
8385                 cn: [
8386                     {
8387                         tag: 'input',
8388                         type : 'hidden',
8389                         cls: 'form-hidden-field'
8390                     },
8391                     {
8392                         tag: 'ul',
8393                         cls: 'select2-choices',
8394                         cn:[
8395                             {
8396                                 tag: 'li',
8397                                 cls: 'select2-search-field',
8398                                 cn: [
8399
8400                                     inputblock
8401                                 ]
8402                             }
8403                         ]
8404                     }
8405                 ]
8406             }
8407         };
8408         
8409         var combobox = {
8410             cls: 'select2-container input-group',
8411             cn: [
8412                 box
8413 //                {
8414 //                    tag: 'ul',
8415 //                    cls: 'typeahead typeahead-long dropdown-menu',
8416 //                    style: 'display:none'
8417 //                }
8418             ]
8419         };
8420         
8421         if(!this.multiple && this.showToggleBtn){
8422             
8423             var caret = {
8424                         tag: 'span',
8425                         cls: 'caret'
8426              };
8427             if (this.caret != false) {
8428                 caret = {
8429                      tag: 'i',
8430                      cls: 'fa fa-' + this.caret
8431                 };
8432                 
8433             }
8434             
8435             combobox.cn.push({
8436                 tag :'span',
8437                 cls : 'input-group-addon btn dropdown-toggle',
8438                 cn : [
8439                     caret,
8440                     {
8441                         tag: 'span',
8442                         cls: 'combobox-clear',
8443                         cn  : [
8444                             {
8445                                 tag : 'i',
8446                                 cls: 'icon-remove'
8447                             }
8448                         ]
8449                     }
8450                 ]
8451
8452             })
8453         }
8454         
8455         if(this.multiple){
8456             combobox.cls += ' select2-container-multi';
8457         }
8458         
8459         if (align ==='left' && this.fieldLabel.length) {
8460             
8461                 Roo.log("left and has label");
8462                 cfg.cn = [
8463                     
8464                     {
8465                         tag: 'label',
8466                         'for' :  id,
8467                         cls : 'control-label col-sm-' + this.labelWidth,
8468                         html : this.fieldLabel
8469                         
8470                     },
8471                     {
8472                         cls : "col-sm-" + (12 - this.labelWidth), 
8473                         cn: [
8474                             combobox
8475                         ]
8476                     }
8477                     
8478                 ];
8479         } else if ( this.fieldLabel.length) {
8480                 Roo.log(" label");
8481                  cfg.cn = [
8482                    
8483                     {
8484                         tag: 'label',
8485                         //cls : 'input-group-addon',
8486                         html : this.fieldLabel
8487                         
8488                     },
8489                     
8490                     combobox
8491                     
8492                 ];
8493
8494         } else {
8495             
8496                 Roo.log(" no label && no align");
8497                 cfg = combobox
8498                      
8499                 
8500         }
8501          
8502         var settings=this;
8503         ['xs','sm','md','lg'].map(function(size){
8504             if (settings[size]) {
8505                 cfg.cls += ' col-' + size + '-' + settings[size];
8506             }
8507         });
8508         
8509         return cfg;
8510         
8511     },
8512     
8513     
8514     
8515     // private
8516     onResize : function(w, h){
8517 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8518 //        if(typeof w == 'number'){
8519 //            var x = w - this.trigger.getWidth();
8520 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8521 //            this.trigger.setStyle('left', x+'px');
8522 //        }
8523     },
8524
8525     // private
8526     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8527
8528     // private
8529     getResizeEl : function(){
8530         return this.inputEl();
8531     },
8532
8533     // private
8534     getPositionEl : function(){
8535         return this.inputEl();
8536     },
8537
8538     // private
8539     alignErrorIcon : function(){
8540         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8541     },
8542
8543     // private
8544     initEvents : function(){
8545         
8546         this.createList();
8547         
8548         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8549         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8550         if(!this.multiple && this.showToggleBtn){
8551             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8552             if(this.hideTrigger){
8553                 this.trigger.setDisplayed(false);
8554             }
8555             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8556         }
8557         
8558         if(this.multiple){
8559             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8560         }
8561         
8562         //this.trigger.addClassOnOver('x-form-trigger-over');
8563         //this.trigger.addClassOnClick('x-form-trigger-click');
8564         
8565         //if(!this.width){
8566         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8567         //}
8568     },
8569     
8570     createList : function()
8571     {
8572         this.list = Roo.get(document.body).createChild({
8573             tag: 'ul',
8574             cls: 'typeahead typeahead-long dropdown-menu',
8575             style: 'display:none'
8576         });
8577         
8578         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8579         
8580     },
8581
8582     // private
8583     initTrigger : function(){
8584        
8585     },
8586
8587     // private
8588     onDestroy : function(){
8589         if(this.trigger){
8590             this.trigger.removeAllListeners();
8591           //  this.trigger.remove();
8592         }
8593         //if(this.wrap){
8594         //    this.wrap.remove();
8595         //}
8596         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8597     },
8598
8599     // private
8600     onFocus : function(){
8601         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8602         /*
8603         if(!this.mimicing){
8604             this.wrap.addClass('x-trigger-wrap-focus');
8605             this.mimicing = true;
8606             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8607             if(this.monitorTab){
8608                 this.el.on("keydown", this.checkTab, this);
8609             }
8610         }
8611         */
8612     },
8613
8614     // private
8615     checkTab : function(e){
8616         if(e.getKey() == e.TAB){
8617             this.triggerBlur();
8618         }
8619     },
8620
8621     // private
8622     onBlur : function(){
8623         // do nothing
8624     },
8625
8626     // private
8627     mimicBlur : function(e, t){
8628         /*
8629         if(!this.wrap.contains(t) && this.validateBlur()){
8630             this.triggerBlur();
8631         }
8632         */
8633     },
8634
8635     // private
8636     triggerBlur : function(){
8637         this.mimicing = false;
8638         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8639         if(this.monitorTab){
8640             this.el.un("keydown", this.checkTab, this);
8641         }
8642         //this.wrap.removeClass('x-trigger-wrap-focus');
8643         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8644     },
8645
8646     // private
8647     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8648     validateBlur : function(e, t){
8649         return true;
8650     },
8651
8652     // private
8653     onDisable : function(){
8654         this.inputEl().dom.disabled = true;
8655         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8656         //if(this.wrap){
8657         //    this.wrap.addClass('x-item-disabled');
8658         //}
8659     },
8660
8661     // private
8662     onEnable : function(){
8663         this.inputEl().dom.disabled = false;
8664         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8665         //if(this.wrap){
8666         //    this.el.removeClass('x-item-disabled');
8667         //}
8668     },
8669
8670     // private
8671     onShow : function(){
8672         var ae = this.getActionEl();
8673         
8674         if(ae){
8675             ae.dom.style.display = '';
8676             ae.dom.style.visibility = 'visible';
8677         }
8678     },
8679
8680     // private
8681     
8682     onHide : function(){
8683         var ae = this.getActionEl();
8684         ae.dom.style.display = 'none';
8685     },
8686
8687     /**
8688      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8689      * by an implementing function.
8690      * @method
8691      * @param {EventObject} e
8692      */
8693     onTriggerClick : Roo.emptyFn
8694 });
8695  /*
8696  * Based on:
8697  * Ext JS Library 1.1.1
8698  * Copyright(c) 2006-2007, Ext JS, LLC.
8699  *
8700  * Originally Released Under LGPL - original licence link has changed is not relivant.
8701  *
8702  * Fork - LGPL
8703  * <script type="text/javascript">
8704  */
8705
8706
8707 /**
8708  * @class Roo.data.SortTypes
8709  * @singleton
8710  * Defines the default sorting (casting?) comparison functions used when sorting data.
8711  */
8712 Roo.data.SortTypes = {
8713     /**
8714      * Default sort that does nothing
8715      * @param {Mixed} s The value being converted
8716      * @return {Mixed} The comparison value
8717      */
8718     none : function(s){
8719         return s;
8720     },
8721     
8722     /**
8723      * The regular expression used to strip tags
8724      * @type {RegExp}
8725      * @property
8726      */
8727     stripTagsRE : /<\/?[^>]+>/gi,
8728     
8729     /**
8730      * Strips all HTML tags to sort on text only
8731      * @param {Mixed} s The value being converted
8732      * @return {String} The comparison value
8733      */
8734     asText : function(s){
8735         return String(s).replace(this.stripTagsRE, "");
8736     },
8737     
8738     /**
8739      * Strips all HTML tags to sort on text only - Case insensitive
8740      * @param {Mixed} s The value being converted
8741      * @return {String} The comparison value
8742      */
8743     asUCText : function(s){
8744         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8745     },
8746     
8747     /**
8748      * Case insensitive string
8749      * @param {Mixed} s The value being converted
8750      * @return {String} The comparison value
8751      */
8752     asUCString : function(s) {
8753         return String(s).toUpperCase();
8754     },
8755     
8756     /**
8757      * Date sorting
8758      * @param {Mixed} s The value being converted
8759      * @return {Number} The comparison value
8760      */
8761     asDate : function(s) {
8762         if(!s){
8763             return 0;
8764         }
8765         if(s instanceof Date){
8766             return s.getTime();
8767         }
8768         return Date.parse(String(s));
8769     },
8770     
8771     /**
8772      * Float sorting
8773      * @param {Mixed} s The value being converted
8774      * @return {Float} The comparison value
8775      */
8776     asFloat : function(s) {
8777         var val = parseFloat(String(s).replace(/,/g, ""));
8778         if(isNaN(val)) val = 0;
8779         return val;
8780     },
8781     
8782     /**
8783      * Integer sorting
8784      * @param {Mixed} s The value being converted
8785      * @return {Number} The comparison value
8786      */
8787     asInt : function(s) {
8788         var val = parseInt(String(s).replace(/,/g, ""));
8789         if(isNaN(val)) val = 0;
8790         return val;
8791     }
8792 };/*
8793  * Based on:
8794  * Ext JS Library 1.1.1
8795  * Copyright(c) 2006-2007, Ext JS, LLC.
8796  *
8797  * Originally Released Under LGPL - original licence link has changed is not relivant.
8798  *
8799  * Fork - LGPL
8800  * <script type="text/javascript">
8801  */
8802
8803 /**
8804 * @class Roo.data.Record
8805  * Instances of this class encapsulate both record <em>definition</em> information, and record
8806  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8807  * to access Records cached in an {@link Roo.data.Store} object.<br>
8808  * <p>
8809  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8810  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8811  * objects.<br>
8812  * <p>
8813  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8814  * @constructor
8815  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8816  * {@link #create}. The parameters are the same.
8817  * @param {Array} data An associative Array of data values keyed by the field name.
8818  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8819  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8820  * not specified an integer id is generated.
8821  */
8822 Roo.data.Record = function(data, id){
8823     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8824     this.data = data;
8825 };
8826
8827 /**
8828  * Generate a constructor for a specific record layout.
8829  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8830  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8831  * Each field definition object may contain the following properties: <ul>
8832  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
8833  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8834  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8835  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8836  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8837  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8838  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8839  * this may be omitted.</p></li>
8840  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8841  * <ul><li>auto (Default, implies no conversion)</li>
8842  * <li>string</li>
8843  * <li>int</li>
8844  * <li>float</li>
8845  * <li>boolean</li>
8846  * <li>date</li></ul></p></li>
8847  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8848  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8849  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8850  * by the Reader into an object that will be stored in the Record. It is passed the
8851  * following parameters:<ul>
8852  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8853  * </ul></p></li>
8854  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8855  * </ul>
8856  * <br>usage:<br><pre><code>
8857 var TopicRecord = Roo.data.Record.create(
8858     {name: 'title', mapping: 'topic_title'},
8859     {name: 'author', mapping: 'username'},
8860     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8861     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8862     {name: 'lastPoster', mapping: 'user2'},
8863     {name: 'excerpt', mapping: 'post_text'}
8864 );
8865
8866 var myNewRecord = new TopicRecord({
8867     title: 'Do my job please',
8868     author: 'noobie',
8869     totalPosts: 1,
8870     lastPost: new Date(),
8871     lastPoster: 'Animal',
8872     excerpt: 'No way dude!'
8873 });
8874 myStore.add(myNewRecord);
8875 </code></pre>
8876  * @method create
8877  * @static
8878  */
8879 Roo.data.Record.create = function(o){
8880     var f = function(){
8881         f.superclass.constructor.apply(this, arguments);
8882     };
8883     Roo.extend(f, Roo.data.Record);
8884     var p = f.prototype;
8885     p.fields = new Roo.util.MixedCollection(false, function(field){
8886         return field.name;
8887     });
8888     for(var i = 0, len = o.length; i < len; i++){
8889         p.fields.add(new Roo.data.Field(o[i]));
8890     }
8891     f.getField = function(name){
8892         return p.fields.get(name);  
8893     };
8894     return f;
8895 };
8896
8897 Roo.data.Record.AUTO_ID = 1000;
8898 Roo.data.Record.EDIT = 'edit';
8899 Roo.data.Record.REJECT = 'reject';
8900 Roo.data.Record.COMMIT = 'commit';
8901
8902 Roo.data.Record.prototype = {
8903     /**
8904      * Readonly flag - true if this record has been modified.
8905      * @type Boolean
8906      */
8907     dirty : false,
8908     editing : false,
8909     error: null,
8910     modified: null,
8911
8912     // private
8913     join : function(store){
8914         this.store = store;
8915     },
8916
8917     /**
8918      * Set the named field to the specified value.
8919      * @param {String} name The name of the field to set.
8920      * @param {Object} value The value to set the field to.
8921      */
8922     set : function(name, value){
8923         if(this.data[name] == value){
8924             return;
8925         }
8926         this.dirty = true;
8927         if(!this.modified){
8928             this.modified = {};
8929         }
8930         if(typeof this.modified[name] == 'undefined'){
8931             this.modified[name] = this.data[name];
8932         }
8933         this.data[name] = value;
8934         if(!this.editing && this.store){
8935             this.store.afterEdit(this);
8936         }       
8937     },
8938
8939     /**
8940      * Get the value of the named field.
8941      * @param {String} name The name of the field to get the value of.
8942      * @return {Object} The value of the field.
8943      */
8944     get : function(name){
8945         return this.data[name]; 
8946     },
8947
8948     // private
8949     beginEdit : function(){
8950         this.editing = true;
8951         this.modified = {}; 
8952     },
8953
8954     // private
8955     cancelEdit : function(){
8956         this.editing = false;
8957         delete this.modified;
8958     },
8959
8960     // private
8961     endEdit : function(){
8962         this.editing = false;
8963         if(this.dirty && this.store){
8964             this.store.afterEdit(this);
8965         }
8966     },
8967
8968     /**
8969      * Usually called by the {@link Roo.data.Store} which owns the Record.
8970      * Rejects all changes made to the Record since either creation, or the last commit operation.
8971      * Modified fields are reverted to their original values.
8972      * <p>
8973      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8974      * of reject operations.
8975      */
8976     reject : function(){
8977         var m = this.modified;
8978         for(var n in m){
8979             if(typeof m[n] != "function"){
8980                 this.data[n] = m[n];
8981             }
8982         }
8983         this.dirty = false;
8984         delete this.modified;
8985         this.editing = false;
8986         if(this.store){
8987             this.store.afterReject(this);
8988         }
8989     },
8990
8991     /**
8992      * Usually called by the {@link Roo.data.Store} which owns the Record.
8993      * Commits all changes made to the Record since either creation, or the last commit operation.
8994      * <p>
8995      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8996      * of commit operations.
8997      */
8998     commit : function(){
8999         this.dirty = false;
9000         delete this.modified;
9001         this.editing = false;
9002         if(this.store){
9003             this.store.afterCommit(this);
9004         }
9005     },
9006
9007     // private
9008     hasError : function(){
9009         return this.error != null;
9010     },
9011
9012     // private
9013     clearError : function(){
9014         this.error = null;
9015     },
9016
9017     /**
9018      * Creates a copy of this record.
9019      * @param {String} id (optional) A new record id if you don't want to use this record's id
9020      * @return {Record}
9021      */
9022     copy : function(newId) {
9023         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9024     }
9025 };/*
9026  * Based on:
9027  * Ext JS Library 1.1.1
9028  * Copyright(c) 2006-2007, Ext JS, LLC.
9029  *
9030  * Originally Released Under LGPL - original licence link has changed is not relivant.
9031  *
9032  * Fork - LGPL
9033  * <script type="text/javascript">
9034  */
9035
9036
9037
9038 /**
9039  * @class Roo.data.Store
9040  * @extends Roo.util.Observable
9041  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9042  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9043  * <p>
9044  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
9045  * has no knowledge of the format of the data returned by the Proxy.<br>
9046  * <p>
9047  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9048  * instances from the data object. These records are cached and made available through accessor functions.
9049  * @constructor
9050  * Creates a new Store.
9051  * @param {Object} config A config object containing the objects needed for the Store to access data,
9052  * and read the data into Records.
9053  */
9054 Roo.data.Store = function(config){
9055     this.data = new Roo.util.MixedCollection(false);
9056     this.data.getKey = function(o){
9057         return o.id;
9058     };
9059     this.baseParams = {};
9060     // private
9061     this.paramNames = {
9062         "start" : "start",
9063         "limit" : "limit",
9064         "sort" : "sort",
9065         "dir" : "dir",
9066         "multisort" : "_multisort"
9067     };
9068
9069     if(config && config.data){
9070         this.inlineData = config.data;
9071         delete config.data;
9072     }
9073
9074     Roo.apply(this, config);
9075     
9076     if(this.reader){ // reader passed
9077         this.reader = Roo.factory(this.reader, Roo.data);
9078         this.reader.xmodule = this.xmodule || false;
9079         if(!this.recordType){
9080             this.recordType = this.reader.recordType;
9081         }
9082         if(this.reader.onMetaChange){
9083             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9084         }
9085     }
9086
9087     if(this.recordType){
9088         this.fields = this.recordType.prototype.fields;
9089     }
9090     this.modified = [];
9091
9092     this.addEvents({
9093         /**
9094          * @event datachanged
9095          * Fires when the data cache has changed, and a widget which is using this Store
9096          * as a Record cache should refresh its view.
9097          * @param {Store} this
9098          */
9099         datachanged : true,
9100         /**
9101          * @event metachange
9102          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9103          * @param {Store} this
9104          * @param {Object} meta The JSON metadata
9105          */
9106         metachange : true,
9107         /**
9108          * @event add
9109          * Fires when Records have been added to the Store
9110          * @param {Store} this
9111          * @param {Roo.data.Record[]} records The array of Records added
9112          * @param {Number} index The index at which the record(s) were added
9113          */
9114         add : true,
9115         /**
9116          * @event remove
9117          * Fires when a Record has been removed from the Store
9118          * @param {Store} this
9119          * @param {Roo.data.Record} record The Record that was removed
9120          * @param {Number} index The index at which the record was removed
9121          */
9122         remove : true,
9123         /**
9124          * @event update
9125          * Fires when a Record has been updated
9126          * @param {Store} this
9127          * @param {Roo.data.Record} record The Record that was updated
9128          * @param {String} operation The update operation being performed.  Value may be one of:
9129          * <pre><code>
9130  Roo.data.Record.EDIT
9131  Roo.data.Record.REJECT
9132  Roo.data.Record.COMMIT
9133          * </code></pre>
9134          */
9135         update : true,
9136         /**
9137          * @event clear
9138          * Fires when the data cache has been cleared.
9139          * @param {Store} this
9140          */
9141         clear : true,
9142         /**
9143          * @event beforeload
9144          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9145          * the load action will be canceled.
9146          * @param {Store} this
9147          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9148          */
9149         beforeload : true,
9150         /**
9151          * @event beforeloadadd
9152          * Fires after a new set of Records has been loaded.
9153          * @param {Store} this
9154          * @param {Roo.data.Record[]} records The Records that were loaded
9155          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9156          */
9157         beforeloadadd : true,
9158         /**
9159          * @event load
9160          * Fires after a new set of Records has been loaded, before they are added to the store.
9161          * @param {Store} this
9162          * @param {Roo.data.Record[]} records The Records that were loaded
9163          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9164          * @params {Object} return from reader
9165          */
9166         load : true,
9167         /**
9168          * @event loadexception
9169          * Fires if an exception occurs in the Proxy during loading.
9170          * Called with the signature of the Proxy's "loadexception" event.
9171          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9172          * 
9173          * @param {Proxy} 
9174          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9175          * @param {Object} load options 
9176          * @param {Object} jsonData from your request (normally this contains the Exception)
9177          */
9178         loadexception : true
9179     });
9180     
9181     if(this.proxy){
9182         this.proxy = Roo.factory(this.proxy, Roo.data);
9183         this.proxy.xmodule = this.xmodule || false;
9184         this.relayEvents(this.proxy,  ["loadexception"]);
9185     }
9186     this.sortToggle = {};
9187     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9188
9189     Roo.data.Store.superclass.constructor.call(this);
9190
9191     if(this.inlineData){
9192         this.loadData(this.inlineData);
9193         delete this.inlineData;
9194     }
9195 };
9196
9197 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9198      /**
9199     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9200     * without a remote query - used by combo/forms at present.
9201     */
9202     
9203     /**
9204     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9205     */
9206     /**
9207     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9208     */
9209     /**
9210     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9211     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9212     */
9213     /**
9214     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9215     * on any HTTP request
9216     */
9217     /**
9218     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9219     */
9220     /**
9221     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9222     */
9223     multiSort: false,
9224     /**
9225     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9226     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9227     */
9228     remoteSort : false,
9229
9230     /**
9231     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9232      * loaded or when a record is removed. (defaults to false).
9233     */
9234     pruneModifiedRecords : false,
9235
9236     // private
9237     lastOptions : null,
9238
9239     /**
9240      * Add Records to the Store and fires the add event.
9241      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9242      */
9243     add : function(records){
9244         records = [].concat(records);
9245         for(var i = 0, len = records.length; i < len; i++){
9246             records[i].join(this);
9247         }
9248         var index = this.data.length;
9249         this.data.addAll(records);
9250         this.fireEvent("add", this, records, index);
9251     },
9252
9253     /**
9254      * Remove a Record from the Store and fires the remove event.
9255      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9256      */
9257     remove : function(record){
9258         var index = this.data.indexOf(record);
9259         this.data.removeAt(index);
9260         if(this.pruneModifiedRecords){
9261             this.modified.remove(record);
9262         }
9263         this.fireEvent("remove", this, record, index);
9264     },
9265
9266     /**
9267      * Remove all Records from the Store and fires the clear event.
9268      */
9269     removeAll : function(){
9270         this.data.clear();
9271         if(this.pruneModifiedRecords){
9272             this.modified = [];
9273         }
9274         this.fireEvent("clear", this);
9275     },
9276
9277     /**
9278      * Inserts Records to the Store at the given index and fires the add event.
9279      * @param {Number} index The start index at which to insert the passed Records.
9280      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9281      */
9282     insert : function(index, records){
9283         records = [].concat(records);
9284         for(var i = 0, len = records.length; i < len; i++){
9285             this.data.insert(index, records[i]);
9286             records[i].join(this);
9287         }
9288         this.fireEvent("add", this, records, index);
9289     },
9290
9291     /**
9292      * Get the index within the cache of the passed Record.
9293      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9294      * @return {Number} The index of the passed Record. Returns -1 if not found.
9295      */
9296     indexOf : function(record){
9297         return this.data.indexOf(record);
9298     },
9299
9300     /**
9301      * Get the index within the cache of the Record with the passed id.
9302      * @param {String} id The id of the Record to find.
9303      * @return {Number} The index of the Record. Returns -1 if not found.
9304      */
9305     indexOfId : function(id){
9306         return this.data.indexOfKey(id);
9307     },
9308
9309     /**
9310      * Get the Record with the specified id.
9311      * @param {String} id The id of the Record to find.
9312      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9313      */
9314     getById : function(id){
9315         return this.data.key(id);
9316     },
9317
9318     /**
9319      * Get the Record at the specified index.
9320      * @param {Number} index The index of the Record to find.
9321      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9322      */
9323     getAt : function(index){
9324         return this.data.itemAt(index);
9325     },
9326
9327     /**
9328      * Returns a range of Records between specified indices.
9329      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9330      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9331      * @return {Roo.data.Record[]} An array of Records
9332      */
9333     getRange : function(start, end){
9334         return this.data.getRange(start, end);
9335     },
9336
9337     // private
9338     storeOptions : function(o){
9339         o = Roo.apply({}, o);
9340         delete o.callback;
9341         delete o.scope;
9342         this.lastOptions = o;
9343     },
9344
9345     /**
9346      * Loads the Record cache from the configured Proxy using the configured Reader.
9347      * <p>
9348      * If using remote paging, then the first load call must specify the <em>start</em>
9349      * and <em>limit</em> properties in the options.params property to establish the initial
9350      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9351      * <p>
9352      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9353      * and this call will return before the new data has been loaded. Perform any post-processing
9354      * in a callback function, or in a "load" event handler.</strong>
9355      * <p>
9356      * @param {Object} options An object containing properties which control loading options:<ul>
9357      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9358      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9359      * passed the following arguments:<ul>
9360      * <li>r : Roo.data.Record[]</li>
9361      * <li>options: Options object from the load call</li>
9362      * <li>success: Boolean success indicator</li></ul></li>
9363      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9364      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9365      * </ul>
9366      */
9367     load : function(options){
9368         options = options || {};
9369         if(this.fireEvent("beforeload", this, options) !== false){
9370             this.storeOptions(options);
9371             var p = Roo.apply(options.params || {}, this.baseParams);
9372             // if meta was not loaded from remote source.. try requesting it.
9373             if (!this.reader.metaFromRemote) {
9374                 p._requestMeta = 1;
9375             }
9376             if(this.sortInfo && this.remoteSort){
9377                 var pn = this.paramNames;
9378                 p[pn["sort"]] = this.sortInfo.field;
9379                 p[pn["dir"]] = this.sortInfo.direction;
9380             }
9381             if (this.multiSort) {
9382                 var pn = this.paramNames;
9383                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9384             }
9385             
9386             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9387         }
9388     },
9389
9390     /**
9391      * Reloads the Record cache from the configured Proxy using the configured Reader and
9392      * the options from the last load operation performed.
9393      * @param {Object} options (optional) An object containing properties which may override the options
9394      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9395      * the most recently used options are reused).
9396      */
9397     reload : function(options){
9398         this.load(Roo.applyIf(options||{}, this.lastOptions));
9399     },
9400
9401     // private
9402     // Called as a callback by the Reader during a load operation.
9403     loadRecords : function(o, options, success){
9404         if(!o || success === false){
9405             if(success !== false){
9406                 this.fireEvent("load", this, [], options, o);
9407             }
9408             if(options.callback){
9409                 options.callback.call(options.scope || this, [], options, false);
9410             }
9411             return;
9412         }
9413         // if data returned failure - throw an exception.
9414         if (o.success === false) {
9415             // show a message if no listener is registered.
9416             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9417                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9418             }
9419             // loadmask wil be hooked into this..
9420             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9421             return;
9422         }
9423         var r = o.records, t = o.totalRecords || r.length;
9424         
9425         this.fireEvent("beforeloadadd", this, r, options, o);
9426         
9427         if(!options || options.add !== true){
9428             if(this.pruneModifiedRecords){
9429                 this.modified = [];
9430             }
9431             for(var i = 0, len = r.length; i < len; i++){
9432                 r[i].join(this);
9433             }
9434             if(this.snapshot){
9435                 this.data = this.snapshot;
9436                 delete this.snapshot;
9437             }
9438             this.data.clear();
9439             this.data.addAll(r);
9440             this.totalLength = t;
9441             this.applySort();
9442             this.fireEvent("datachanged", this);
9443         }else{
9444             this.totalLength = Math.max(t, this.data.length+r.length);
9445             this.add(r);
9446         }
9447         this.fireEvent("load", this, r, options, o);
9448         if(options.callback){
9449             options.callback.call(options.scope || this, r, options, true);
9450         }
9451     },
9452
9453
9454     /**
9455      * Loads data from a passed data block. A Reader which understands the format of the data
9456      * must have been configured in the constructor.
9457      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9458      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9459      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9460      */
9461     loadData : function(o, append){
9462         var r = this.reader.readRecords(o);
9463         this.loadRecords(r, {add: append}, true);
9464     },
9465
9466     /**
9467      * Gets the number of cached records.
9468      * <p>
9469      * <em>If using paging, this may not be the total size of the dataset. If the data object
9470      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9471      * the data set size</em>
9472      */
9473     getCount : function(){
9474         return this.data.length || 0;
9475     },
9476
9477     /**
9478      * Gets the total number of records in the dataset as returned by the server.
9479      * <p>
9480      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9481      * the dataset size</em>
9482      */
9483     getTotalCount : function(){
9484         return this.totalLength || 0;
9485     },
9486
9487     /**
9488      * Returns the sort state of the Store as an object with two properties:
9489      * <pre><code>
9490  field {String} The name of the field by which the Records are sorted
9491  direction {String} The sort order, "ASC" or "DESC"
9492      * </code></pre>
9493      */
9494     getSortState : function(){
9495         return this.sortInfo;
9496     },
9497
9498     // private
9499     applySort : function(){
9500         if(this.sortInfo && !this.remoteSort){
9501             var s = this.sortInfo, f = s.field;
9502             var st = this.fields.get(f).sortType;
9503             var fn = function(r1, r2){
9504                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9505                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9506             };
9507             this.data.sort(s.direction, fn);
9508             if(this.snapshot && this.snapshot != this.data){
9509                 this.snapshot.sort(s.direction, fn);
9510             }
9511         }
9512     },
9513
9514     /**
9515      * Sets the default sort column and order to be used by the next load operation.
9516      * @param {String} fieldName The name of the field to sort by.
9517      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9518      */
9519     setDefaultSort : function(field, dir){
9520         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9521     },
9522
9523     /**
9524      * Sort the Records.
9525      * If remote sorting is used, the sort is performed on the server, and the cache is
9526      * reloaded. If local sorting is used, the cache is sorted internally.
9527      * @param {String} fieldName The name of the field to sort by.
9528      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9529      */
9530     sort : function(fieldName, dir){
9531         var f = this.fields.get(fieldName);
9532         if(!dir){
9533             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9534             
9535             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9536                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9537             }else{
9538                 dir = f.sortDir;
9539             }
9540         }
9541         this.sortToggle[f.name] = dir;
9542         this.sortInfo = {field: f.name, direction: dir};
9543         if(!this.remoteSort){
9544             this.applySort();
9545             this.fireEvent("datachanged", this);
9546         }else{
9547             this.load(this.lastOptions);
9548         }
9549     },
9550
9551     /**
9552      * Calls the specified function for each of the Records in the cache.
9553      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9554      * Returning <em>false</em> aborts and exits the iteration.
9555      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9556      */
9557     each : function(fn, scope){
9558         this.data.each(fn, scope);
9559     },
9560
9561     /**
9562      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9563      * (e.g., during paging).
9564      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9565      */
9566     getModifiedRecords : function(){
9567         return this.modified;
9568     },
9569
9570     // private
9571     createFilterFn : function(property, value, anyMatch){
9572         if(!value.exec){ // not a regex
9573             value = String(value);
9574             if(value.length == 0){
9575                 return false;
9576             }
9577             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9578         }
9579         return function(r){
9580             return value.test(r.data[property]);
9581         };
9582     },
9583
9584     /**
9585      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9586      * @param {String} property A field on your records
9587      * @param {Number} start The record index to start at (defaults to 0)
9588      * @param {Number} end The last record index to include (defaults to length - 1)
9589      * @return {Number} The sum
9590      */
9591     sum : function(property, start, end){
9592         var rs = this.data.items, v = 0;
9593         start = start || 0;
9594         end = (end || end === 0) ? end : rs.length-1;
9595
9596         for(var i = start; i <= end; i++){
9597             v += (rs[i].data[property] || 0);
9598         }
9599         return v;
9600     },
9601
9602     /**
9603      * Filter the records by a specified property.
9604      * @param {String} field A field on your records
9605      * @param {String/RegExp} value Either a string that the field
9606      * should start with or a RegExp to test against the field
9607      * @param {Boolean} anyMatch True to match any part not just the beginning
9608      */
9609     filter : function(property, value, anyMatch){
9610         var fn = this.createFilterFn(property, value, anyMatch);
9611         return fn ? this.filterBy(fn) : this.clearFilter();
9612     },
9613
9614     /**
9615      * Filter by a function. The specified function will be called with each
9616      * record in this data source. If the function returns true the record is included,
9617      * otherwise it is filtered.
9618      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9619      * @param {Object} scope (optional) The scope of the function (defaults to this)
9620      */
9621     filterBy : function(fn, scope){
9622         this.snapshot = this.snapshot || this.data;
9623         this.data = this.queryBy(fn, scope||this);
9624         this.fireEvent("datachanged", this);
9625     },
9626
9627     /**
9628      * Query the records by a specified property.
9629      * @param {String} field A field on your records
9630      * @param {String/RegExp} value Either a string that the field
9631      * should start with or a RegExp to test against the field
9632      * @param {Boolean} anyMatch True to match any part not just the beginning
9633      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9634      */
9635     query : function(property, value, anyMatch){
9636         var fn = this.createFilterFn(property, value, anyMatch);
9637         return fn ? this.queryBy(fn) : this.data.clone();
9638     },
9639
9640     /**
9641      * Query by a function. The specified function will be called with each
9642      * record in this data source. If the function returns true the record is included
9643      * in the results.
9644      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9645      * @param {Object} scope (optional) The scope of the function (defaults to this)
9646       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9647      **/
9648     queryBy : function(fn, scope){
9649         var data = this.snapshot || this.data;
9650         return data.filterBy(fn, scope||this);
9651     },
9652
9653     /**
9654      * Collects unique values for a particular dataIndex from this store.
9655      * @param {String} dataIndex The property to collect
9656      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9657      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9658      * @return {Array} An array of the unique values
9659      **/
9660     collect : function(dataIndex, allowNull, bypassFilter){
9661         var d = (bypassFilter === true && this.snapshot) ?
9662                 this.snapshot.items : this.data.items;
9663         var v, sv, r = [], l = {};
9664         for(var i = 0, len = d.length; i < len; i++){
9665             v = d[i].data[dataIndex];
9666             sv = String(v);
9667             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9668                 l[sv] = true;
9669                 r[r.length] = v;
9670             }
9671         }
9672         return r;
9673     },
9674
9675     /**
9676      * Revert to a view of the Record cache with no filtering applied.
9677      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9678      */
9679     clearFilter : function(suppressEvent){
9680         if(this.snapshot && this.snapshot != this.data){
9681             this.data = this.snapshot;
9682             delete this.snapshot;
9683             if(suppressEvent !== true){
9684                 this.fireEvent("datachanged", this);
9685             }
9686         }
9687     },
9688
9689     // private
9690     afterEdit : function(record){
9691         if(this.modified.indexOf(record) == -1){
9692             this.modified.push(record);
9693         }
9694         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9695     },
9696     
9697     // private
9698     afterReject : function(record){
9699         this.modified.remove(record);
9700         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9701     },
9702
9703     // private
9704     afterCommit : function(record){
9705         this.modified.remove(record);
9706         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9707     },
9708
9709     /**
9710      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9711      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9712      */
9713     commitChanges : function(){
9714         var m = this.modified.slice(0);
9715         this.modified = [];
9716         for(var i = 0, len = m.length; i < len; i++){
9717             m[i].commit();
9718         }
9719     },
9720
9721     /**
9722      * Cancel outstanding changes on all changed records.
9723      */
9724     rejectChanges : function(){
9725         var m = this.modified.slice(0);
9726         this.modified = [];
9727         for(var i = 0, len = m.length; i < len; i++){
9728             m[i].reject();
9729         }
9730     },
9731
9732     onMetaChange : function(meta, rtype, o){
9733         this.recordType = rtype;
9734         this.fields = rtype.prototype.fields;
9735         delete this.snapshot;
9736         this.sortInfo = meta.sortInfo || this.sortInfo;
9737         this.modified = [];
9738         this.fireEvent('metachange', this, this.reader.meta);
9739     },
9740     
9741     moveIndex : function(data, type)
9742     {
9743         var index = this.indexOf(data);
9744         
9745         var newIndex = index + type;
9746         
9747         this.remove(data);
9748         
9749         this.insert(newIndex, data);
9750         
9751     }
9752 });/*
9753  * Based on:
9754  * Ext JS Library 1.1.1
9755  * Copyright(c) 2006-2007, Ext JS, LLC.
9756  *
9757  * Originally Released Under LGPL - original licence link has changed is not relivant.
9758  *
9759  * Fork - LGPL
9760  * <script type="text/javascript">
9761  */
9762
9763 /**
9764  * @class Roo.data.SimpleStore
9765  * @extends Roo.data.Store
9766  * Small helper class to make creating Stores from Array data easier.
9767  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9768  * @cfg {Array} fields An array of field definition objects, or field name strings.
9769  * @cfg {Array} data The multi-dimensional array of data
9770  * @constructor
9771  * @param {Object} config
9772  */
9773 Roo.data.SimpleStore = function(config){
9774     Roo.data.SimpleStore.superclass.constructor.call(this, {
9775         isLocal : true,
9776         reader: new Roo.data.ArrayReader({
9777                 id: config.id
9778             },
9779             Roo.data.Record.create(config.fields)
9780         ),
9781         proxy : new Roo.data.MemoryProxy(config.data)
9782     });
9783     this.load();
9784 };
9785 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9786  * Based on:
9787  * Ext JS Library 1.1.1
9788  * Copyright(c) 2006-2007, Ext JS, LLC.
9789  *
9790  * Originally Released Under LGPL - original licence link has changed is not relivant.
9791  *
9792  * Fork - LGPL
9793  * <script type="text/javascript">
9794  */
9795
9796 /**
9797 /**
9798  * @extends Roo.data.Store
9799  * @class Roo.data.JsonStore
9800  * Small helper class to make creating Stores for JSON data easier. <br/>
9801 <pre><code>
9802 var store = new Roo.data.JsonStore({
9803     url: 'get-images.php',
9804     root: 'images',
9805     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9806 });
9807 </code></pre>
9808  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9809  * JsonReader and HttpProxy (unless inline data is provided).</b>
9810  * @cfg {Array} fields An array of field definition objects, or field name strings.
9811  * @constructor
9812  * @param {Object} config
9813  */
9814 Roo.data.JsonStore = function(c){
9815     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9816         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9817         reader: new Roo.data.JsonReader(c, c.fields)
9818     }));
9819 };
9820 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9821  * Based on:
9822  * Ext JS Library 1.1.1
9823  * Copyright(c) 2006-2007, Ext JS, LLC.
9824  *
9825  * Originally Released Under LGPL - original licence link has changed is not relivant.
9826  *
9827  * Fork - LGPL
9828  * <script type="text/javascript">
9829  */
9830
9831  
9832 Roo.data.Field = function(config){
9833     if(typeof config == "string"){
9834         config = {name: config};
9835     }
9836     Roo.apply(this, config);
9837     
9838     if(!this.type){
9839         this.type = "auto";
9840     }
9841     
9842     var st = Roo.data.SortTypes;
9843     // named sortTypes are supported, here we look them up
9844     if(typeof this.sortType == "string"){
9845         this.sortType = st[this.sortType];
9846     }
9847     
9848     // set default sortType for strings and dates
9849     if(!this.sortType){
9850         switch(this.type){
9851             case "string":
9852                 this.sortType = st.asUCString;
9853                 break;
9854             case "date":
9855                 this.sortType = st.asDate;
9856                 break;
9857             default:
9858                 this.sortType = st.none;
9859         }
9860     }
9861
9862     // define once
9863     var stripRe = /[\$,%]/g;
9864
9865     // prebuilt conversion function for this field, instead of
9866     // switching every time we're reading a value
9867     if(!this.convert){
9868         var cv, dateFormat = this.dateFormat;
9869         switch(this.type){
9870             case "":
9871             case "auto":
9872             case undefined:
9873                 cv = function(v){ return v; };
9874                 break;
9875             case "string":
9876                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9877                 break;
9878             case "int":
9879                 cv = function(v){
9880                     return v !== undefined && v !== null && v !== '' ?
9881                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9882                     };
9883                 break;
9884             case "float":
9885                 cv = function(v){
9886                     return v !== undefined && v !== null && v !== '' ?
9887                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9888                     };
9889                 break;
9890             case "bool":
9891             case "boolean":
9892                 cv = function(v){ return v === true || v === "true" || v == 1; };
9893                 break;
9894             case "date":
9895                 cv = function(v){
9896                     if(!v){
9897                         return '';
9898                     }
9899                     if(v instanceof Date){
9900                         return v;
9901                     }
9902                     if(dateFormat){
9903                         if(dateFormat == "timestamp"){
9904                             return new Date(v*1000);
9905                         }
9906                         return Date.parseDate(v, dateFormat);
9907                     }
9908                     var parsed = Date.parse(v);
9909                     return parsed ? new Date(parsed) : null;
9910                 };
9911              break;
9912             
9913         }
9914         this.convert = cv;
9915     }
9916 };
9917
9918 Roo.data.Field.prototype = {
9919     dateFormat: null,
9920     defaultValue: "",
9921     mapping: null,
9922     sortType : null,
9923     sortDir : "ASC"
9924 };/*
9925  * Based on:
9926  * Ext JS Library 1.1.1
9927  * Copyright(c) 2006-2007, Ext JS, LLC.
9928  *
9929  * Originally Released Under LGPL - original licence link has changed is not relivant.
9930  *
9931  * Fork - LGPL
9932  * <script type="text/javascript">
9933  */
9934  
9935 // Base class for reading structured data from a data source.  This class is intended to be
9936 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9937
9938 /**
9939  * @class Roo.data.DataReader
9940  * Base class for reading structured data from a data source.  This class is intended to be
9941  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9942  */
9943
9944 Roo.data.DataReader = function(meta, recordType){
9945     
9946     this.meta = meta;
9947     
9948     this.recordType = recordType instanceof Array ? 
9949         Roo.data.Record.create(recordType) : recordType;
9950 };
9951
9952 Roo.data.DataReader.prototype = {
9953      /**
9954      * Create an empty record
9955      * @param {Object} data (optional) - overlay some values
9956      * @return {Roo.data.Record} record created.
9957      */
9958     newRow :  function(d) {
9959         var da =  {};
9960         this.recordType.prototype.fields.each(function(c) {
9961             switch( c.type) {
9962                 case 'int' : da[c.name] = 0; break;
9963                 case 'date' : da[c.name] = new Date(); break;
9964                 case 'float' : da[c.name] = 0.0; break;
9965                 case 'boolean' : da[c.name] = false; break;
9966                 default : da[c.name] = ""; break;
9967             }
9968             
9969         });
9970         return new this.recordType(Roo.apply(da, d));
9971     }
9972     
9973 };/*
9974  * Based on:
9975  * Ext JS Library 1.1.1
9976  * Copyright(c) 2006-2007, Ext JS, LLC.
9977  *
9978  * Originally Released Under LGPL - original licence link has changed is not relivant.
9979  *
9980  * Fork - LGPL
9981  * <script type="text/javascript">
9982  */
9983
9984 /**
9985  * @class Roo.data.DataProxy
9986  * @extends Roo.data.Observable
9987  * This class is an abstract base class for implementations which provide retrieval of
9988  * unformatted data objects.<br>
9989  * <p>
9990  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9991  * (of the appropriate type which knows how to parse the data object) to provide a block of
9992  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9993  * <p>
9994  * Custom implementations must implement the load method as described in
9995  * {@link Roo.data.HttpProxy#load}.
9996  */
9997 Roo.data.DataProxy = function(){
9998     this.addEvents({
9999         /**
10000          * @event beforeload
10001          * Fires before a network request is made to retrieve a data object.
10002          * @param {Object} This DataProxy object.
10003          * @param {Object} params The params parameter to the load function.
10004          */
10005         beforeload : true,
10006         /**
10007          * @event load
10008          * Fires before the load method's callback is called.
10009          * @param {Object} This DataProxy object.
10010          * @param {Object} o The data object.
10011          * @param {Object} arg The callback argument object passed to the load function.
10012          */
10013         load : true,
10014         /**
10015          * @event loadexception
10016          * Fires if an Exception occurs during data retrieval.
10017          * @param {Object} This DataProxy object.
10018          * @param {Object} o The data object.
10019          * @param {Object} arg The callback argument object passed to the load function.
10020          * @param {Object} e The Exception.
10021          */
10022         loadexception : true
10023     });
10024     Roo.data.DataProxy.superclass.constructor.call(this);
10025 };
10026
10027 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10028
10029     /**
10030      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10031      */
10032 /*
10033  * Based on:
10034  * Ext JS Library 1.1.1
10035  * Copyright(c) 2006-2007, Ext JS, LLC.
10036  *
10037  * Originally Released Under LGPL - original licence link has changed is not relivant.
10038  *
10039  * Fork - LGPL
10040  * <script type="text/javascript">
10041  */
10042 /**
10043  * @class Roo.data.MemoryProxy
10044  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10045  * to the Reader when its load method is called.
10046  * @constructor
10047  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10048  */
10049 Roo.data.MemoryProxy = function(data){
10050     if (data.data) {
10051         data = data.data;
10052     }
10053     Roo.data.MemoryProxy.superclass.constructor.call(this);
10054     this.data = data;
10055 };
10056
10057 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10058     /**
10059      * Load data from the requested source (in this case an in-memory
10060      * data object passed to the constructor), read the data object into
10061      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10062      * process that block using the passed callback.
10063      * @param {Object} params This parameter is not used by the MemoryProxy class.
10064      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10065      * object into a block of Roo.data.Records.
10066      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10067      * The function must be passed <ul>
10068      * <li>The Record block object</li>
10069      * <li>The "arg" argument from the load function</li>
10070      * <li>A boolean success indicator</li>
10071      * </ul>
10072      * @param {Object} scope The scope in which to call the callback
10073      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10074      */
10075     load : function(params, reader, callback, scope, arg){
10076         params = params || {};
10077         var result;
10078         try {
10079             result = reader.readRecords(this.data);
10080         }catch(e){
10081             this.fireEvent("loadexception", this, arg, null, e);
10082             callback.call(scope, null, arg, false);
10083             return;
10084         }
10085         callback.call(scope, result, arg, true);
10086     },
10087     
10088     // private
10089     update : function(params, records){
10090         
10091     }
10092 });/*
10093  * Based on:
10094  * Ext JS Library 1.1.1
10095  * Copyright(c) 2006-2007, Ext JS, LLC.
10096  *
10097  * Originally Released Under LGPL - original licence link has changed is not relivant.
10098  *
10099  * Fork - LGPL
10100  * <script type="text/javascript">
10101  */
10102 /**
10103  * @class Roo.data.HttpProxy
10104  * @extends Roo.data.DataProxy
10105  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10106  * configured to reference a certain URL.<br><br>
10107  * <p>
10108  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10109  * from which the running page was served.<br><br>
10110  * <p>
10111  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10112  * <p>
10113  * Be aware that to enable the browser to parse an XML document, the server must set
10114  * the Content-Type header in the HTTP response to "text/xml".
10115  * @constructor
10116  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10117  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10118  * will be used to make the request.
10119  */
10120 Roo.data.HttpProxy = function(conn){
10121     Roo.data.HttpProxy.superclass.constructor.call(this);
10122     // is conn a conn config or a real conn?
10123     this.conn = conn;
10124     this.useAjax = !conn || !conn.events;
10125   
10126 };
10127
10128 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10129     // thse are take from connection...
10130     
10131     /**
10132      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10133      */
10134     /**
10135      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10136      * extra parameters to each request made by this object. (defaults to undefined)
10137      */
10138     /**
10139      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10140      *  to each request made by this object. (defaults to undefined)
10141      */
10142     /**
10143      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
10144      */
10145     /**
10146      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10147      */
10148      /**
10149      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10150      * @type Boolean
10151      */
10152   
10153
10154     /**
10155      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10156      * @type Boolean
10157      */
10158     /**
10159      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10160      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10161      * a finer-grained basis than the DataProxy events.
10162      */
10163     getConnection : function(){
10164         return this.useAjax ? Roo.Ajax : this.conn;
10165     },
10166
10167     /**
10168      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10169      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10170      * process that block using the passed callback.
10171      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10172      * for the request to the remote server.
10173      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10174      * object into a block of Roo.data.Records.
10175      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10176      * The function must be passed <ul>
10177      * <li>The Record block object</li>
10178      * <li>The "arg" argument from the load function</li>
10179      * <li>A boolean success indicator</li>
10180      * </ul>
10181      * @param {Object} scope The scope in which to call the callback
10182      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10183      */
10184     load : function(params, reader, callback, scope, arg){
10185         if(this.fireEvent("beforeload", this, params) !== false){
10186             var  o = {
10187                 params : params || {},
10188                 request: {
10189                     callback : callback,
10190                     scope : scope,
10191                     arg : arg
10192                 },
10193                 reader: reader,
10194                 callback : this.loadResponse,
10195                 scope: this
10196             };
10197             if(this.useAjax){
10198                 Roo.applyIf(o, this.conn);
10199                 if(this.activeRequest){
10200                     Roo.Ajax.abort(this.activeRequest);
10201                 }
10202                 this.activeRequest = Roo.Ajax.request(o);
10203             }else{
10204                 this.conn.request(o);
10205             }
10206         }else{
10207             callback.call(scope||this, null, arg, false);
10208         }
10209     },
10210
10211     // private
10212     loadResponse : function(o, success, response){
10213         delete this.activeRequest;
10214         if(!success){
10215             this.fireEvent("loadexception", this, o, response);
10216             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10217             return;
10218         }
10219         var result;
10220         try {
10221             result = o.reader.read(response);
10222         }catch(e){
10223             this.fireEvent("loadexception", this, o, response, e);
10224             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10225             return;
10226         }
10227         
10228         this.fireEvent("load", this, o, o.request.arg);
10229         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10230     },
10231
10232     // private
10233     update : function(dataSet){
10234
10235     },
10236
10237     // private
10238     updateResponse : function(dataSet){
10239
10240     }
10241 });/*
10242  * Based on:
10243  * Ext JS Library 1.1.1
10244  * Copyright(c) 2006-2007, Ext JS, LLC.
10245  *
10246  * Originally Released Under LGPL - original licence link has changed is not relivant.
10247  *
10248  * Fork - LGPL
10249  * <script type="text/javascript">
10250  */
10251
10252 /**
10253  * @class Roo.data.ScriptTagProxy
10254  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10255  * other than the originating domain of the running page.<br><br>
10256  * <p>
10257  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
10258  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10259  * <p>
10260  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10261  * source code that is used as the source inside a &lt;script> tag.<br><br>
10262  * <p>
10263  * In order for the browser to process the returned data, the server must wrap the data object
10264  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10265  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10266  * depending on whether the callback name was passed:
10267  * <p>
10268  * <pre><code>
10269 boolean scriptTag = false;
10270 String cb = request.getParameter("callback");
10271 if (cb != null) {
10272     scriptTag = true;
10273     response.setContentType("text/javascript");
10274 } else {
10275     response.setContentType("application/x-json");
10276 }
10277 Writer out = response.getWriter();
10278 if (scriptTag) {
10279     out.write(cb + "(");
10280 }
10281 out.print(dataBlock.toJsonString());
10282 if (scriptTag) {
10283     out.write(");");
10284 }
10285 </pre></code>
10286  *
10287  * @constructor
10288  * @param {Object} config A configuration object.
10289  */
10290 Roo.data.ScriptTagProxy = function(config){
10291     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10292     Roo.apply(this, config);
10293     this.head = document.getElementsByTagName("head")[0];
10294 };
10295
10296 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10297
10298 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10299     /**
10300      * @cfg {String} url The URL from which to request the data object.
10301      */
10302     /**
10303      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10304      */
10305     timeout : 30000,
10306     /**
10307      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10308      * the server the name of the callback function set up by the load call to process the returned data object.
10309      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10310      * javascript output which calls this named function passing the data object as its only parameter.
10311      */
10312     callbackParam : "callback",
10313     /**
10314      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10315      * name to the request.
10316      */
10317     nocache : true,
10318
10319     /**
10320      * Load data from the configured URL, read the data object into
10321      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10322      * process that block using the passed callback.
10323      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10324      * for the request to the remote server.
10325      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10326      * object into a block of Roo.data.Records.
10327      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10328      * The function must be passed <ul>
10329      * <li>The Record block object</li>
10330      * <li>The "arg" argument from the load function</li>
10331      * <li>A boolean success indicator</li>
10332      * </ul>
10333      * @param {Object} scope The scope in which to call the callback
10334      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10335      */
10336     load : function(params, reader, callback, scope, arg){
10337         if(this.fireEvent("beforeload", this, params) !== false){
10338
10339             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10340
10341             var url = this.url;
10342             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10343             if(this.nocache){
10344                 url += "&_dc=" + (new Date().getTime());
10345             }
10346             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10347             var trans = {
10348                 id : transId,
10349                 cb : "stcCallback"+transId,
10350                 scriptId : "stcScript"+transId,
10351                 params : params,
10352                 arg : arg,
10353                 url : url,
10354                 callback : callback,
10355                 scope : scope,
10356                 reader : reader
10357             };
10358             var conn = this;
10359
10360             window[trans.cb] = function(o){
10361                 conn.handleResponse(o, trans);
10362             };
10363
10364             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10365
10366             if(this.autoAbort !== false){
10367                 this.abort();
10368             }
10369
10370             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10371
10372             var script = document.createElement("script");
10373             script.setAttribute("src", url);
10374             script.setAttribute("type", "text/javascript");
10375             script.setAttribute("id", trans.scriptId);
10376             this.head.appendChild(script);
10377
10378             this.trans = trans;
10379         }else{
10380             callback.call(scope||this, null, arg, false);
10381         }
10382     },
10383
10384     // private
10385     isLoading : function(){
10386         return this.trans ? true : false;
10387     },
10388
10389     /**
10390      * Abort the current server request.
10391      */
10392     abort : function(){
10393         if(this.isLoading()){
10394             this.destroyTrans(this.trans);
10395         }
10396     },
10397
10398     // private
10399     destroyTrans : function(trans, isLoaded){
10400         this.head.removeChild(document.getElementById(trans.scriptId));
10401         clearTimeout(trans.timeoutId);
10402         if(isLoaded){
10403             window[trans.cb] = undefined;
10404             try{
10405                 delete window[trans.cb];
10406             }catch(e){}
10407         }else{
10408             // if hasn't been loaded, wait for load to remove it to prevent script error
10409             window[trans.cb] = function(){
10410                 window[trans.cb] = undefined;
10411                 try{
10412                     delete window[trans.cb];
10413                 }catch(e){}
10414             };
10415         }
10416     },
10417
10418     // private
10419     handleResponse : function(o, trans){
10420         this.trans = false;
10421         this.destroyTrans(trans, true);
10422         var result;
10423         try {
10424             result = trans.reader.readRecords(o);
10425         }catch(e){
10426             this.fireEvent("loadexception", this, o, trans.arg, e);
10427             trans.callback.call(trans.scope||window, null, trans.arg, false);
10428             return;
10429         }
10430         this.fireEvent("load", this, o, trans.arg);
10431         trans.callback.call(trans.scope||window, result, trans.arg, true);
10432     },
10433
10434     // private
10435     handleFailure : function(trans){
10436         this.trans = false;
10437         this.destroyTrans(trans, false);
10438         this.fireEvent("loadexception", this, null, trans.arg);
10439         trans.callback.call(trans.scope||window, null, trans.arg, false);
10440     }
10441 });/*
10442  * Based on:
10443  * Ext JS Library 1.1.1
10444  * Copyright(c) 2006-2007, Ext JS, LLC.
10445  *
10446  * Originally Released Under LGPL - original licence link has changed is not relivant.
10447  *
10448  * Fork - LGPL
10449  * <script type="text/javascript">
10450  */
10451
10452 /**
10453  * @class Roo.data.JsonReader
10454  * @extends Roo.data.DataReader
10455  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10456  * based on mappings in a provided Roo.data.Record constructor.
10457  * 
10458  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10459  * in the reply previously. 
10460  * 
10461  * <p>
10462  * Example code:
10463  * <pre><code>
10464 var RecordDef = Roo.data.Record.create([
10465     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10466     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10467 ]);
10468 var myReader = new Roo.data.JsonReader({
10469     totalProperty: "results",    // The property which contains the total dataset size (optional)
10470     root: "rows",                // The property which contains an Array of row objects
10471     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10472 }, RecordDef);
10473 </code></pre>
10474  * <p>
10475  * This would consume a JSON file like this:
10476  * <pre><code>
10477 { 'results': 2, 'rows': [
10478     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10479     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10480 }
10481 </code></pre>
10482  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10483  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10484  * paged from the remote server.
10485  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10486  * @cfg {String} root name of the property which contains the Array of row objects.
10487  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10488  * @constructor
10489  * Create a new JsonReader
10490  * @param {Object} meta Metadata configuration options
10491  * @param {Object} recordType Either an Array of field definition objects,
10492  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10493  */
10494 Roo.data.JsonReader = function(meta, recordType){
10495     
10496     meta = meta || {};
10497     // set some defaults:
10498     Roo.applyIf(meta, {
10499         totalProperty: 'total',
10500         successProperty : 'success',
10501         root : 'data',
10502         id : 'id'
10503     });
10504     
10505     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10506 };
10507 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10508     
10509     /**
10510      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10511      * Used by Store query builder to append _requestMeta to params.
10512      * 
10513      */
10514     metaFromRemote : false,
10515     /**
10516      * This method is only used by a DataProxy which has retrieved data from a remote server.
10517      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10518      * @return {Object} data A data block which is used by an Roo.data.Store object as
10519      * a cache of Roo.data.Records.
10520      */
10521     read : function(response){
10522         var json = response.responseText;
10523        
10524         var o = /* eval:var:o */ eval("("+json+")");
10525         if(!o) {
10526             throw {message: "JsonReader.read: Json object not found"};
10527         }
10528         
10529         if(o.metaData){
10530             
10531             delete this.ef;
10532             this.metaFromRemote = true;
10533             this.meta = o.metaData;
10534             this.recordType = Roo.data.Record.create(o.metaData.fields);
10535             this.onMetaChange(this.meta, this.recordType, o);
10536         }
10537         return this.readRecords(o);
10538     },
10539
10540     // private function a store will implement
10541     onMetaChange : function(meta, recordType, o){
10542
10543     },
10544
10545     /**
10546          * @ignore
10547          */
10548     simpleAccess: function(obj, subsc) {
10549         return obj[subsc];
10550     },
10551
10552         /**
10553          * @ignore
10554          */
10555     getJsonAccessor: function(){
10556         var re = /[\[\.]/;
10557         return function(expr) {
10558             try {
10559                 return(re.test(expr))
10560                     ? new Function("obj", "return obj." + expr)
10561                     : function(obj){
10562                         return obj[expr];
10563                     };
10564             } catch(e){}
10565             return Roo.emptyFn;
10566         };
10567     }(),
10568
10569     /**
10570      * Create a data block containing Roo.data.Records from an XML document.
10571      * @param {Object} o An object which contains an Array of row objects in the property specified
10572      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10573      * which contains the total size of the dataset.
10574      * @return {Object} data A data block which is used by an Roo.data.Store object as
10575      * a cache of Roo.data.Records.
10576      */
10577     readRecords : function(o){
10578         /**
10579          * After any data loads, the raw JSON data is available for further custom processing.
10580          * @type Object
10581          */
10582         this.o = o;
10583         var s = this.meta, Record = this.recordType,
10584             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10585
10586 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10587         if (!this.ef) {
10588             if(s.totalProperty) {
10589                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10590                 }
10591                 if(s.successProperty) {
10592                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10593                 }
10594                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10595                 if (s.id) {
10596                         var g = this.getJsonAccessor(s.id);
10597                         this.getId = function(rec) {
10598                                 var r = g(rec);  
10599                                 return (r === undefined || r === "") ? null : r;
10600                         };
10601                 } else {
10602                         this.getId = function(){return null;};
10603                 }
10604             this.ef = [];
10605             for(var jj = 0; jj < fl; jj++){
10606                 f = fi[jj];
10607                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10608                 this.ef[jj] = this.getJsonAccessor(map);
10609             }
10610         }
10611
10612         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10613         if(s.totalProperty){
10614             var vt = parseInt(this.getTotal(o), 10);
10615             if(!isNaN(vt)){
10616                 totalRecords = vt;
10617             }
10618         }
10619         if(s.successProperty){
10620             var vs = this.getSuccess(o);
10621             if(vs === false || vs === 'false'){
10622                 success = false;
10623             }
10624         }
10625         var records = [];
10626         for(var i = 0; i < c; i++){
10627                 var n = root[i];
10628             var values = {};
10629             var id = this.getId(n);
10630             for(var j = 0; j < fl; j++){
10631                 f = fi[j];
10632             var v = this.ef[j](n);
10633             if (!f.convert) {
10634                 Roo.log('missing convert for ' + f.name);
10635                 Roo.log(f);
10636                 continue;
10637             }
10638             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10639             }
10640             var record = new Record(values, id);
10641             record.json = n;
10642             records[i] = record;
10643         }
10644         return {
10645             raw : o,
10646             success : success,
10647             records : records,
10648             totalRecords : totalRecords
10649         };
10650     }
10651 });/*
10652  * Based on:
10653  * Ext JS Library 1.1.1
10654  * Copyright(c) 2006-2007, Ext JS, LLC.
10655  *
10656  * Originally Released Under LGPL - original licence link has changed is not relivant.
10657  *
10658  * Fork - LGPL
10659  * <script type="text/javascript">
10660  */
10661
10662 /**
10663  * @class Roo.data.ArrayReader
10664  * @extends Roo.data.DataReader
10665  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10666  * Each element of that Array represents a row of data fields. The
10667  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10668  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10669  * <p>
10670  * Example code:.
10671  * <pre><code>
10672 var RecordDef = Roo.data.Record.create([
10673     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10674     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10675 ]);
10676 var myReader = new Roo.data.ArrayReader({
10677     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10678 }, RecordDef);
10679 </code></pre>
10680  * <p>
10681  * This would consume an Array like this:
10682  * <pre><code>
10683 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10684   </code></pre>
10685  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10686  * @constructor
10687  * Create a new JsonReader
10688  * @param {Object} meta Metadata configuration options.
10689  * @param {Object} recordType Either an Array of field definition objects
10690  * as specified to {@link Roo.data.Record#create},
10691  * or an {@link Roo.data.Record} object
10692  * created using {@link Roo.data.Record#create}.
10693  */
10694 Roo.data.ArrayReader = function(meta, recordType){
10695     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10696 };
10697
10698 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10699     /**
10700      * Create a data block containing Roo.data.Records from an XML document.
10701      * @param {Object} o An Array of row objects which represents the dataset.
10702      * @return {Object} data A data block which is used by an Roo.data.Store object as
10703      * a cache of Roo.data.Records.
10704      */
10705     readRecords : function(o){
10706         var sid = this.meta ? this.meta.id : null;
10707         var recordType = this.recordType, fields = recordType.prototype.fields;
10708         var records = [];
10709         var root = o;
10710             for(var i = 0; i < root.length; i++){
10711                     var n = root[i];
10712                 var values = {};
10713                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10714                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10715                 var f = fields.items[j];
10716                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10717                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10718                 v = f.convert(v);
10719                 values[f.name] = v;
10720             }
10721                 var record = new recordType(values, id);
10722                 record.json = n;
10723                 records[records.length] = record;
10724             }
10725             return {
10726                 records : records,
10727                 totalRecords : records.length
10728             };
10729     }
10730 });/*
10731  * - LGPL
10732  * * 
10733  */
10734
10735 /**
10736  * @class Roo.bootstrap.ComboBox
10737  * @extends Roo.bootstrap.TriggerField
10738  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10739  * @cfg {Boolean} append (true|false) default false
10740  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10741  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10742  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10743  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10744  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10745  * @constructor
10746  * Create a new ComboBox.
10747  * @param {Object} config Configuration options
10748  */
10749 Roo.bootstrap.ComboBox = function(config){
10750     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10751     this.addEvents({
10752         /**
10753          * @event expand
10754          * Fires when the dropdown list is expanded
10755              * @param {Roo.bootstrap.ComboBox} combo This combo box
10756              */
10757         'expand' : true,
10758         /**
10759          * @event collapse
10760          * Fires when the dropdown list is collapsed
10761              * @param {Roo.bootstrap.ComboBox} combo This combo box
10762              */
10763         'collapse' : true,
10764         /**
10765          * @event beforeselect
10766          * Fires before a list item is selected. Return false to cancel the selection.
10767              * @param {Roo.bootstrap.ComboBox} combo This combo box
10768              * @param {Roo.data.Record} record The data record returned from the underlying store
10769              * @param {Number} index The index of the selected item in the dropdown list
10770              */
10771         'beforeselect' : true,
10772         /**
10773          * @event select
10774          * Fires when a list item is selected
10775              * @param {Roo.bootstrap.ComboBox} combo This combo box
10776              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10777              * @param {Number} index The index of the selected item in the dropdown list
10778              */
10779         'select' : true,
10780         /**
10781          * @event beforequery
10782          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10783          * The event object passed has these properties:
10784              * @param {Roo.bootstrap.ComboBox} combo This combo box
10785              * @param {String} query The query
10786              * @param {Boolean} forceAll true to force "all" query
10787              * @param {Boolean} cancel true to cancel the query
10788              * @param {Object} e The query event object
10789              */
10790         'beforequery': true,
10791          /**
10792          * @event add
10793          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10794              * @param {Roo.bootstrap.ComboBox} combo This combo box
10795              */
10796         'add' : true,
10797         /**
10798          * @event edit
10799          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10800              * @param {Roo.bootstrap.ComboBox} combo This combo box
10801              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10802              */
10803         'edit' : true,
10804         /**
10805          * @event remove
10806          * Fires when the remove value from the combobox array
10807              * @param {Roo.bootstrap.ComboBox} combo This combo box
10808              */
10809         'remove' : true,
10810         /**
10811          * @event specialfilter
10812          * Fires when specialfilter
10813             * @param {Roo.bootstrap.ComboBox} combo This combo box
10814             */
10815         'specialfilter' : true
10816         
10817     });
10818     
10819     this.item = [];
10820     this.tickItems = [];
10821     
10822     this.selectedIndex = -1;
10823     if(this.mode == 'local'){
10824         if(config.queryDelay === undefined){
10825             this.queryDelay = 10;
10826         }
10827         if(config.minChars === undefined){
10828             this.minChars = 0;
10829         }
10830     }
10831 };
10832
10833 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10834      
10835     /**
10836      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10837      * rendering into an Roo.Editor, defaults to false)
10838      */
10839     /**
10840      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10841      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10842      */
10843     /**
10844      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10845      */
10846     /**
10847      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10848      * the dropdown list (defaults to undefined, with no header element)
10849      */
10850
10851      /**
10852      * @cfg {String/Roo.Template} tpl The template to use to render the output
10853      */
10854      
10855      /**
10856      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10857      */
10858     listWidth: undefined,
10859     /**
10860      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10861      * mode = 'remote' or 'text' if mode = 'local')
10862      */
10863     displayField: undefined,
10864     
10865     /**
10866      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10867      * mode = 'remote' or 'value' if mode = 'local'). 
10868      * Note: use of a valueField requires the user make a selection
10869      * in order for a value to be mapped.
10870      */
10871     valueField: undefined,
10872     
10873     
10874     /**
10875      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10876      * field's data value (defaults to the underlying DOM element's name)
10877      */
10878     hiddenName: undefined,
10879     /**
10880      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10881      */
10882     listClass: '',
10883     /**
10884      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10885      */
10886     selectedClass: 'active',
10887     
10888     /**
10889      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10890      */
10891     shadow:'sides',
10892     /**
10893      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10894      * anchor positions (defaults to 'tl-bl')
10895      */
10896     listAlign: 'tl-bl?',
10897     /**
10898      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10899      */
10900     maxHeight: 300,
10901     /**
10902      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10903      * query specified by the allQuery config option (defaults to 'query')
10904      */
10905     triggerAction: 'query',
10906     /**
10907      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10908      * (defaults to 4, does not apply if editable = false)
10909      */
10910     minChars : 4,
10911     /**
10912      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10913      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10914      */
10915     typeAhead: false,
10916     /**
10917      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10918      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10919      */
10920     queryDelay: 500,
10921     /**
10922      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10923      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10924      */
10925     pageSize: 0,
10926     /**
10927      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10928      * when editable = true (defaults to false)
10929      */
10930     selectOnFocus:false,
10931     /**
10932      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10933      */
10934     queryParam: 'query',
10935     /**
10936      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10937      * when mode = 'remote' (defaults to 'Loading...')
10938      */
10939     loadingText: 'Loading...',
10940     /**
10941      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10942      */
10943     resizable: false,
10944     /**
10945      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10946      */
10947     handleHeight : 8,
10948     /**
10949      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10950      * traditional select (defaults to true)
10951      */
10952     editable: true,
10953     /**
10954      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10955      */
10956     allQuery: '',
10957     /**
10958      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10959      */
10960     mode: 'remote',
10961     /**
10962      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10963      * listWidth has a higher value)
10964      */
10965     minListWidth : 70,
10966     /**
10967      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10968      * allow the user to set arbitrary text into the field (defaults to false)
10969      */
10970     forceSelection:false,
10971     /**
10972      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10973      * if typeAhead = true (defaults to 250)
10974      */
10975     typeAheadDelay : 250,
10976     /**
10977      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10978      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10979      */
10980     valueNotFoundText : undefined,
10981     /**
10982      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10983      */
10984     blockFocus : false,
10985     
10986     /**
10987      * @cfg {Boolean} disableClear Disable showing of clear button.
10988      */
10989     disableClear : false,
10990     /**
10991      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10992      */
10993     alwaysQuery : false,
10994     
10995     /**
10996      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10997      */
10998     multiple : false,
10999     
11000     /**
11001      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11002      */
11003     invalidClass : "has-warning",
11004     
11005     /**
11006      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11007      */
11008     validClass : "has-success",
11009     
11010     /**
11011      * @cfg {Boolean} specialFilter (true|false) special filter default false
11012      */
11013     specialFilter : false,
11014     
11015     //private
11016     addicon : false,
11017     editicon: false,
11018     
11019     page: 0,
11020     hasQuery: false,
11021     append: false,
11022     loadNext: false,
11023     autoFocus : true,
11024     tickable : false,
11025     btnPosition : 'right',
11026     triggerList : true,
11027     showToggleBtn : true,
11028     // element that contains real text value.. (when hidden is used..)
11029     
11030     getAutoCreate : function()
11031     {
11032         var cfg = false;
11033         
11034         /*
11035          *  Normal ComboBox
11036          */
11037         if(!this.tickable){
11038             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11039             return cfg;
11040         }
11041         
11042         /*
11043          *  ComboBox with tickable selections
11044          */
11045              
11046         var align = this.labelAlign || this.parentLabelAlign();
11047         
11048         cfg = {
11049             cls : 'form-group roo-combobox-tickable' //input-group
11050         };
11051         
11052         var buttons = {
11053             tag : 'div',
11054             cls : 'tickable-buttons',
11055             cn : [
11056                 {
11057                     tag : 'button',
11058                     type : 'button',
11059                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11060                     html : 'Edit'
11061                 },
11062                 {
11063                     tag : 'button',
11064                     type : 'button',
11065                     name : 'ok',
11066                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11067                     html : 'Done'
11068                 },
11069                 {
11070                     tag : 'button',
11071                     type : 'button',
11072                     name : 'cancel',
11073                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11074                     html : 'Cancel'
11075                 }
11076             ]
11077         };
11078         
11079         if(this.editable){
11080             buttons.cn.unshift({
11081                 tag: 'input',
11082                 cls: 'select2-search-field-input'
11083             });
11084         }
11085         
11086         var _this = this;
11087         
11088         Roo.each(buttons.cn, function(c){
11089             if (_this.size) {
11090                 c.cls += ' btn-' + _this.size;
11091             }
11092
11093             if (_this.disabled) {
11094                 c.disabled = true;
11095             }
11096         });
11097         
11098         var box = {
11099             tag: 'div',
11100             cn: [
11101                 {
11102                     tag: 'input',
11103                     type : 'hidden',
11104                     cls: 'form-hidden-field'
11105                 },
11106                 {
11107                     tag: 'ul',
11108                     cls: 'select2-choices',
11109                     cn:[
11110                         {
11111                             tag: 'li',
11112                             cls: 'select2-search-field',
11113                             cn: [
11114
11115                                 buttons
11116                             ]
11117                         }
11118                     ]
11119                 }
11120             ]
11121         }
11122         
11123         var combobox = {
11124             cls: 'select2-container input-group select2-container-multi',
11125             cn: [
11126                 box
11127 //                {
11128 //                    tag: 'ul',
11129 //                    cls: 'typeahead typeahead-long dropdown-menu',
11130 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11131 //                }
11132             ]
11133         };
11134         
11135         if(this.hasFeedback && !this.allowBlank){
11136             
11137             var feedback = {
11138                 tag: 'span',
11139                 cls: 'glyphicon form-control-feedback'
11140             };
11141
11142             combobox.cn.push(feedback);
11143         }
11144         
11145         if (align ==='left' && this.fieldLabel.length) {
11146             
11147                 Roo.log("left and has label");
11148                 cfg.cn = [
11149                     
11150                     {
11151                         tag: 'label',
11152                         'for' :  id,
11153                         cls : 'control-label col-sm-' + this.labelWidth,
11154                         html : this.fieldLabel
11155                         
11156                     },
11157                     {
11158                         cls : "col-sm-" + (12 - this.labelWidth), 
11159                         cn: [
11160                             combobox
11161                         ]
11162                     }
11163                     
11164                 ];
11165         } else if ( this.fieldLabel.length) {
11166                 Roo.log(" label");
11167                  cfg.cn = [
11168                    
11169                     {
11170                         tag: 'label',
11171                         //cls : 'input-group-addon',
11172                         html : this.fieldLabel
11173                         
11174                     },
11175                     
11176                     combobox
11177                     
11178                 ];
11179
11180         } else {
11181             
11182                 Roo.log(" no label && no align");
11183                 cfg = combobox
11184                      
11185                 
11186         }
11187          
11188         var settings=this;
11189         ['xs','sm','md','lg'].map(function(size){
11190             if (settings[size]) {
11191                 cfg.cls += ' col-' + size + '-' + settings[size];
11192             }
11193         });
11194         
11195         return cfg;
11196         
11197     },
11198     
11199     // private
11200     initEvents: function()
11201     {
11202         
11203         if (!this.store) {
11204             throw "can not find store for combo";
11205         }
11206         this.store = Roo.factory(this.store, Roo.data);
11207         
11208         if(this.tickable){
11209             this.initTickableEvents();
11210             return;
11211         }
11212         
11213         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11214         
11215         if(this.hiddenName){
11216             
11217             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11218             
11219             this.hiddenField.dom.value =
11220                 this.hiddenValue !== undefined ? this.hiddenValue :
11221                 this.value !== undefined ? this.value : '';
11222
11223             // prevent input submission
11224             this.el.dom.removeAttribute('name');
11225             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11226              
11227              
11228         }
11229         //if(Roo.isGecko){
11230         //    this.el.dom.setAttribute('autocomplete', 'off');
11231         //}
11232         
11233         var cls = 'x-combo-list';
11234         
11235         //this.list = new Roo.Layer({
11236         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11237         //});
11238         
11239         var _this = this;
11240         
11241         (function(){
11242             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11243             _this.list.setWidth(lw);
11244         }).defer(100);
11245         
11246         this.list.on('mouseover', this.onViewOver, this);
11247         this.list.on('mousemove', this.onViewMove, this);
11248         
11249         this.list.on('scroll', this.onViewScroll, this);
11250         
11251         /*
11252         this.list.swallowEvent('mousewheel');
11253         this.assetHeight = 0;
11254
11255         if(this.title){
11256             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11257             this.assetHeight += this.header.getHeight();
11258         }
11259
11260         this.innerList = this.list.createChild({cls:cls+'-inner'});
11261         this.innerList.on('mouseover', this.onViewOver, this);
11262         this.innerList.on('mousemove', this.onViewMove, this);
11263         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11264         
11265         if(this.allowBlank && !this.pageSize && !this.disableClear){
11266             this.footer = this.list.createChild({cls:cls+'-ft'});
11267             this.pageTb = new Roo.Toolbar(this.footer);
11268            
11269         }
11270         if(this.pageSize){
11271             this.footer = this.list.createChild({cls:cls+'-ft'});
11272             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11273                     {pageSize: this.pageSize});
11274             
11275         }
11276         
11277         if (this.pageTb && this.allowBlank && !this.disableClear) {
11278             var _this = this;
11279             this.pageTb.add(new Roo.Toolbar.Fill(), {
11280                 cls: 'x-btn-icon x-btn-clear',
11281                 text: '&#160;',
11282                 handler: function()
11283                 {
11284                     _this.collapse();
11285                     _this.clearValue();
11286                     _this.onSelect(false, -1);
11287                 }
11288             });
11289         }
11290         if (this.footer) {
11291             this.assetHeight += this.footer.getHeight();
11292         }
11293         */
11294             
11295         if(!this.tpl){
11296             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11297         }
11298
11299         this.view = new Roo.View(this.list, this.tpl, {
11300             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11301         });
11302         //this.view.wrapEl.setDisplayed(false);
11303         this.view.on('click', this.onViewClick, this);
11304         
11305         
11306         
11307         this.store.on('beforeload', this.onBeforeLoad, this);
11308         this.store.on('load', this.onLoad, this);
11309         this.store.on('loadexception', this.onLoadException, this);
11310         /*
11311         if(this.resizable){
11312             this.resizer = new Roo.Resizable(this.list,  {
11313                pinned:true, handles:'se'
11314             });
11315             this.resizer.on('resize', function(r, w, h){
11316                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11317                 this.listWidth = w;
11318                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11319                 this.restrictHeight();
11320             }, this);
11321             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11322         }
11323         */
11324         if(!this.editable){
11325             this.editable = true;
11326             this.setEditable(false);
11327         }
11328         
11329         /*
11330         
11331         if (typeof(this.events.add.listeners) != 'undefined') {
11332             
11333             this.addicon = this.wrap.createChild(
11334                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11335        
11336             this.addicon.on('click', function(e) {
11337                 this.fireEvent('add', this);
11338             }, this);
11339         }
11340         if (typeof(this.events.edit.listeners) != 'undefined') {
11341             
11342             this.editicon = this.wrap.createChild(
11343                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11344             if (this.addicon) {
11345                 this.editicon.setStyle('margin-left', '40px');
11346             }
11347             this.editicon.on('click', function(e) {
11348                 
11349                 // we fire even  if inothing is selected..
11350                 this.fireEvent('edit', this, this.lastData );
11351                 
11352             }, this);
11353         }
11354         */
11355         
11356         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11357             "up" : function(e){
11358                 this.inKeyMode = true;
11359                 this.selectPrev();
11360             },
11361
11362             "down" : function(e){
11363                 if(!this.isExpanded()){
11364                     this.onTriggerClick();
11365                 }else{
11366                     this.inKeyMode = true;
11367                     this.selectNext();
11368                 }
11369             },
11370
11371             "enter" : function(e){
11372 //                this.onViewClick();
11373                 //return true;
11374                 this.collapse();
11375                 
11376                 if(this.fireEvent("specialkey", this, e)){
11377                     this.onViewClick(false);
11378                 }
11379                 
11380                 return true;
11381             },
11382
11383             "esc" : function(e){
11384                 this.collapse();
11385             },
11386
11387             "tab" : function(e){
11388                 this.collapse();
11389                 
11390                 if(this.fireEvent("specialkey", this, e)){
11391                     this.onViewClick(false);
11392                 }
11393                 
11394                 return true;
11395             },
11396
11397             scope : this,
11398
11399             doRelay : function(foo, bar, hname){
11400                 if(hname == 'down' || this.scope.isExpanded()){
11401                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11402                 }
11403                 return true;
11404             },
11405
11406             forceKeyDown: true
11407         });
11408         
11409         
11410         this.queryDelay = Math.max(this.queryDelay || 10,
11411                 this.mode == 'local' ? 10 : 250);
11412         
11413         
11414         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11415         
11416         if(this.typeAhead){
11417             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11418         }
11419         if(this.editable !== false){
11420             this.inputEl().on("keyup", this.onKeyUp, this);
11421         }
11422         if(this.forceSelection){
11423             this.inputEl().on('blur', this.doForce, this);
11424         }
11425         
11426         if(this.multiple){
11427             this.choices = this.el.select('ul.select2-choices', true).first();
11428             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11429         }
11430     },
11431     
11432     initTickableEvents: function()
11433     {   
11434         this.createList();
11435         
11436         if(this.hiddenName){
11437             
11438             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11439             
11440             this.hiddenField.dom.value =
11441                 this.hiddenValue !== undefined ? this.hiddenValue :
11442                 this.value !== undefined ? this.value : '';
11443
11444             // prevent input submission
11445             this.el.dom.removeAttribute('name');
11446             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11447              
11448              
11449         }
11450         
11451 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11452         
11453         this.choices = this.el.select('ul.select2-choices', true).first();
11454         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11455         if(this.triggerList){
11456             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11457         }
11458          
11459         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11460         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11461         
11462         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11463         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11464         
11465         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11466         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11467         
11468         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11469         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11470         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11471         
11472         this.okBtn.hide();
11473         this.cancelBtn.hide();
11474         
11475         var _this = this;
11476         
11477         (function(){
11478             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11479             _this.list.setWidth(lw);
11480         }).defer(100);
11481         
11482         this.list.on('mouseover', this.onViewOver, this);
11483         this.list.on('mousemove', this.onViewMove, this);
11484         
11485         this.list.on('scroll', this.onViewScroll, this);
11486         
11487         if(!this.tpl){
11488             this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
11489         }
11490
11491         this.view = new Roo.View(this.list, this.tpl, {
11492             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11493         });
11494         
11495         //this.view.wrapEl.setDisplayed(false);
11496         this.view.on('click', this.onViewClick, this);
11497         
11498         
11499         
11500         this.store.on('beforeload', this.onBeforeLoad, this);
11501         this.store.on('load', this.onLoad, this);
11502         this.store.on('loadexception', this.onLoadException, this);
11503         
11504         if(this.editable){
11505             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11506                 "up" : function(e){
11507                     this.inKeyMode = true;
11508                     this.selectPrev();
11509                 },
11510
11511                 "down" : function(e){
11512                     this.inKeyMode = true;
11513                     this.selectNext();
11514                 },
11515
11516                 "enter" : function(e){
11517                     if(this.fireEvent("specialkey", this, e)){
11518                         this.onViewClick(false);
11519                     }
11520                     
11521                     return true;
11522                 },
11523
11524                 "esc" : function(e){
11525                     this.onTickableFooterButtonClick(e, false, false);
11526                 },
11527
11528                 "tab" : function(e){
11529                     this.fireEvent("specialkey", this, e);
11530                     
11531                     this.onTickableFooterButtonClick(e, false, false);
11532                     
11533                     return true;
11534                 },
11535
11536                 scope : this,
11537
11538                 doRelay : function(e, fn, key){
11539                     if(this.scope.isExpanded()){
11540                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11541                     }
11542                     return true;
11543                 },
11544
11545                 forceKeyDown: true
11546             });
11547         }
11548         
11549         this.queryDelay = Math.max(this.queryDelay || 10,
11550                 this.mode == 'local' ? 10 : 250);
11551         
11552         
11553         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11554         
11555         if(this.typeAhead){
11556             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11557         }
11558         
11559         if(this.editable !== false){
11560             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11561         }
11562         
11563     },
11564
11565     onDestroy : function(){
11566         if(this.view){
11567             this.view.setStore(null);
11568             this.view.el.removeAllListeners();
11569             this.view.el.remove();
11570             this.view.purgeListeners();
11571         }
11572         if(this.list){
11573             this.list.dom.innerHTML  = '';
11574         }
11575         
11576         if(this.store){
11577             this.store.un('beforeload', this.onBeforeLoad, this);
11578             this.store.un('load', this.onLoad, this);
11579             this.store.un('loadexception', this.onLoadException, this);
11580         }
11581         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11582     },
11583
11584     // private
11585     fireKey : function(e){
11586         if(e.isNavKeyPress() && !this.list.isVisible()){
11587             this.fireEvent("specialkey", this, e);
11588         }
11589     },
11590
11591     // private
11592     onResize: function(w, h){
11593 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11594 //        
11595 //        if(typeof w != 'number'){
11596 //            // we do not handle it!?!?
11597 //            return;
11598 //        }
11599 //        var tw = this.trigger.getWidth();
11600 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11601 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11602 //        var x = w - tw;
11603 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11604 //            
11605 //        //this.trigger.setStyle('left', x+'px');
11606 //        
11607 //        if(this.list && this.listWidth === undefined){
11608 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11609 //            this.list.setWidth(lw);
11610 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11611 //        }
11612         
11613     
11614         
11615     },
11616
11617     /**
11618      * Allow or prevent the user from directly editing the field text.  If false is passed,
11619      * the user will only be able to select from the items defined in the dropdown list.  This method
11620      * is the runtime equivalent of setting the 'editable' config option at config time.
11621      * @param {Boolean} value True to allow the user to directly edit the field text
11622      */
11623     setEditable : function(value){
11624         if(value == this.editable){
11625             return;
11626         }
11627         this.editable = value;
11628         if(!value){
11629             this.inputEl().dom.setAttribute('readOnly', true);
11630             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11631             this.inputEl().addClass('x-combo-noedit');
11632         }else{
11633             this.inputEl().dom.setAttribute('readOnly', false);
11634             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11635             this.inputEl().removeClass('x-combo-noedit');
11636         }
11637     },
11638
11639     // private
11640     
11641     onBeforeLoad : function(combo,opts){
11642         if(!this.hasFocus){
11643             return;
11644         }
11645          if (!opts.add) {
11646             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11647          }
11648         this.restrictHeight();
11649         this.selectedIndex = -1;
11650     },
11651
11652     // private
11653     onLoad : function(){
11654         
11655         this.hasQuery = false;
11656         
11657         if(!this.hasFocus){
11658             return;
11659         }
11660         
11661         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11662             this.loading.hide();
11663         }
11664              
11665         if(this.store.getCount() > 0){
11666             this.expand();
11667             this.restrictHeight();
11668             if(this.lastQuery == this.allQuery){
11669                 if(this.editable && !this.tickable){
11670                     this.inputEl().dom.select();
11671                 }
11672                 
11673                 if(
11674                     !this.selectByValue(this.value, true) &&
11675                     this.autoFocus && 
11676                     (
11677                         !this.store.lastOptions ||
11678                         typeof(this.store.lastOptions.add) == 'undefined' || 
11679                         this.store.lastOptions.add != true
11680                     )
11681                 ){
11682                     this.select(0, true);
11683                 }
11684             }else{
11685                 if(this.autoFocus){
11686                     this.selectNext();
11687                 }
11688                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11689                     this.taTask.delay(this.typeAheadDelay);
11690                 }
11691             }
11692         }else{
11693             this.onEmptyResults();
11694         }
11695         
11696         //this.el.focus();
11697     },
11698     // private
11699     onLoadException : function()
11700     {
11701         this.hasQuery = false;
11702         
11703         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11704             this.loading.hide();
11705         }
11706         
11707         if(this.tickable && this.editable){
11708             return;
11709         }
11710         
11711         this.collapse();
11712         
11713         Roo.log(this.store.reader.jsonData);
11714         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11715             // fixme
11716             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11717         }
11718         
11719         
11720     },
11721     // private
11722     onTypeAhead : function(){
11723         if(this.store.getCount() > 0){
11724             var r = this.store.getAt(0);
11725             var newValue = r.data[this.displayField];
11726             var len = newValue.length;
11727             var selStart = this.getRawValue().length;
11728             
11729             if(selStart != len){
11730                 this.setRawValue(newValue);
11731                 this.selectText(selStart, newValue.length);
11732             }
11733         }
11734     },
11735
11736     // private
11737     onSelect : function(record, index){
11738         
11739         if(this.fireEvent('beforeselect', this, record, index) !== false){
11740         
11741             this.setFromData(index > -1 ? record.data : false);
11742             
11743             this.collapse();
11744             this.fireEvent('select', this, record, index);
11745         }
11746     },
11747
11748     /**
11749      * Returns the currently selected field value or empty string if no value is set.
11750      * @return {String} value The selected value
11751      */
11752     getValue : function(){
11753         
11754         if(this.multiple){
11755             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11756         }
11757         
11758         if(this.valueField){
11759             return typeof this.value != 'undefined' ? this.value : '';
11760         }else{
11761             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11762         }
11763     },
11764
11765     /**
11766      * Clears any text/value currently set in the field
11767      */
11768     clearValue : function(){
11769         if(this.hiddenField){
11770             this.hiddenField.dom.value = '';
11771         }
11772         this.value = '';
11773         this.setRawValue('');
11774         this.lastSelectionText = '';
11775         this.lastData = false;
11776         
11777     },
11778
11779     /**
11780      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11781      * will be displayed in the field.  If the value does not match the data value of an existing item,
11782      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11783      * Otherwise the field will be blank (although the value will still be set).
11784      * @param {String} value The value to match
11785      */
11786     setValue : function(v){
11787         if(this.multiple){
11788             this.syncValue();
11789             return;
11790         }
11791         
11792         var text = v;
11793         if(this.valueField){
11794             var r = this.findRecord(this.valueField, v);
11795             if(r){
11796                 text = r.data[this.displayField];
11797             }else if(this.valueNotFoundText !== undefined){
11798                 text = this.valueNotFoundText;
11799             }
11800         }
11801         this.lastSelectionText = text;
11802         if(this.hiddenField){
11803             this.hiddenField.dom.value = v;
11804         }
11805         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11806         this.value = v;
11807     },
11808     /**
11809      * @property {Object} the last set data for the element
11810      */
11811     
11812     lastData : false,
11813     /**
11814      * Sets the value of the field based on a object which is related to the record format for the store.
11815      * @param {Object} value the value to set as. or false on reset?
11816      */
11817     setFromData : function(o){
11818         
11819         if(this.multiple){
11820             this.addItem(o);
11821             return;
11822         }
11823             
11824         var dv = ''; // display value
11825         var vv = ''; // value value..
11826         this.lastData = o;
11827         if (this.displayField) {
11828             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11829         } else {
11830             // this is an error condition!!!
11831             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11832         }
11833         
11834         if(this.valueField){
11835             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11836         }
11837         
11838         if(this.hiddenField){
11839             this.hiddenField.dom.value = vv;
11840             
11841             this.lastSelectionText = dv;
11842             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11843             this.value = vv;
11844             return;
11845         }
11846         // no hidden field.. - we store the value in 'value', but still display
11847         // display field!!!!
11848         this.lastSelectionText = dv;
11849         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11850         this.value = vv;
11851         
11852         
11853     },
11854     // private
11855     reset : function(){
11856         // overridden so that last data is reset..
11857         
11858         if(this.multiple){
11859             this.clearItem();
11860             return;
11861         }
11862         
11863         this.setValue(this.originalValue);
11864         this.clearInvalid();
11865         this.lastData = false;
11866         if (this.view) {
11867             this.view.clearSelections();
11868         }
11869     },
11870     // private
11871     findRecord : function(prop, value){
11872         var record;
11873         if(this.store.getCount() > 0){
11874             this.store.each(function(r){
11875                 if(r.data[prop] == value){
11876                     record = r;
11877                     return false;
11878                 }
11879                 return true;
11880             });
11881         }
11882         return record;
11883     },
11884     
11885     getName: function()
11886     {
11887         // returns hidden if it's set..
11888         if (!this.rendered) {return ''};
11889         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11890         
11891     },
11892     // private
11893     onViewMove : function(e, t){
11894         this.inKeyMode = false;
11895     },
11896
11897     // private
11898     onViewOver : function(e, t){
11899         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11900             return;
11901         }
11902         var item = this.view.findItemFromChild(t);
11903         
11904         if(item){
11905             var index = this.view.indexOf(item);
11906             this.select(index, false);
11907         }
11908     },
11909
11910     // private
11911     onViewClick : function(view, doFocus, el, e)
11912     {
11913         var index = this.view.getSelectedIndexes()[0];
11914         
11915         var r = this.store.getAt(index);
11916         
11917         if(this.tickable){
11918             
11919             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
11920                 return;
11921             }
11922             
11923             var rm = false;
11924             var _this = this;
11925             
11926             Roo.each(this.tickItems, function(v,k){
11927                 
11928                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11929                     _this.tickItems.splice(k, 1);
11930                     
11931                     if(typeof(e) == 'undefined' && view == false){
11932                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
11933                     }
11934                     
11935                     rm = true;
11936                     return;
11937                 }
11938             });
11939             
11940             if(rm){
11941                 return;
11942             }
11943             
11944             this.tickItems.push(r.data);
11945             
11946             if(typeof(e) == 'undefined' && view == false){
11947                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
11948             }
11949                     
11950             return;
11951         }
11952         
11953         if(r){
11954             this.onSelect(r, index);
11955         }
11956         if(doFocus !== false && !this.blockFocus){
11957             this.inputEl().focus();
11958         }
11959     },
11960
11961     // private
11962     restrictHeight : function(){
11963         //this.innerList.dom.style.height = '';
11964         //var inner = this.innerList.dom;
11965         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11966         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11967         //this.list.beginUpdate();
11968         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11969         this.list.alignTo(this.inputEl(), this.listAlign);
11970         this.list.alignTo(this.inputEl(), this.listAlign);
11971         //this.list.endUpdate();
11972     },
11973
11974     // private
11975     onEmptyResults : function(){
11976         
11977         if(this.tickable && this.editable){
11978             this.restrictHeight();
11979             return;
11980         }
11981         
11982         this.collapse();
11983     },
11984
11985     /**
11986      * Returns true if the dropdown list is expanded, else false.
11987      */
11988     isExpanded : function(){
11989         return this.list.isVisible();
11990     },
11991
11992     /**
11993      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11994      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11995      * @param {String} value The data value of the item to select
11996      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11997      * selected item if it is not currently in view (defaults to true)
11998      * @return {Boolean} True if the value matched an item in the list, else false
11999      */
12000     selectByValue : function(v, scrollIntoView){
12001         if(v !== undefined && v !== null){
12002             var r = this.findRecord(this.valueField || this.displayField, v);
12003             if(r){
12004                 this.select(this.store.indexOf(r), scrollIntoView);
12005                 return true;
12006             }
12007         }
12008         return false;
12009     },
12010
12011     /**
12012      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12013      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12014      * @param {Number} index The zero-based index of the list item to select
12015      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12016      * selected item if it is not currently in view (defaults to true)
12017      */
12018     select : function(index, scrollIntoView){
12019         this.selectedIndex = index;
12020         this.view.select(index);
12021         if(scrollIntoView !== false){
12022             var el = this.view.getNode(index);
12023             /*
12024              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12025              */
12026             if(el){
12027                 this.list.scrollChildIntoView(el, false);
12028             }
12029         }
12030     },
12031
12032     // private
12033     selectNext : function(){
12034         var ct = this.store.getCount();
12035         if(ct > 0){
12036             if(this.selectedIndex == -1){
12037                 this.select(0);
12038             }else if(this.selectedIndex < ct-1){
12039                 this.select(this.selectedIndex+1);
12040             }
12041         }
12042     },
12043
12044     // private
12045     selectPrev : function(){
12046         var ct = this.store.getCount();
12047         if(ct > 0){
12048             if(this.selectedIndex == -1){
12049                 this.select(0);
12050             }else if(this.selectedIndex != 0){
12051                 this.select(this.selectedIndex-1);
12052             }
12053         }
12054     },
12055
12056     // private
12057     onKeyUp : function(e){
12058         if(this.editable !== false && !e.isSpecialKey()){
12059             this.lastKey = e.getKey();
12060             this.dqTask.delay(this.queryDelay);
12061         }
12062     },
12063
12064     // private
12065     validateBlur : function(){
12066         return !this.list || !this.list.isVisible();   
12067     },
12068
12069     // private
12070     initQuery : function(){
12071         
12072         var v = this.getRawValue();
12073         
12074         if(this.tickable && this.editable){
12075             v = this.tickableInputEl().getValue();
12076         }
12077         
12078         this.doQuery(v);
12079     },
12080
12081     // private
12082     doForce : function(){
12083         if(this.inputEl().dom.value.length > 0){
12084             this.inputEl().dom.value =
12085                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12086              
12087         }
12088     },
12089
12090     /**
12091      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12092      * query allowing the query action to be canceled if needed.
12093      * @param {String} query The SQL query to execute
12094      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12095      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12096      * saved in the current store (defaults to false)
12097      */
12098     doQuery : function(q, forceAll){
12099         
12100         if(q === undefined || q === null){
12101             q = '';
12102         }
12103         var qe = {
12104             query: q,
12105             forceAll: forceAll,
12106             combo: this,
12107             cancel:false
12108         };
12109         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12110             return false;
12111         }
12112         q = qe.query;
12113         
12114         forceAll = qe.forceAll;
12115         if(forceAll === true || (q.length >= this.minChars)){
12116             
12117             this.hasQuery = true;
12118             
12119             if(this.lastQuery != q || this.alwaysQuery){
12120                 this.lastQuery = q;
12121                 if(this.mode == 'local'){
12122                     this.selectedIndex = -1;
12123                     if(forceAll){
12124                         this.store.clearFilter();
12125                     }else{
12126                         
12127                         if(this.specialFilter){
12128                             this.fireEvent('specialfilter', this);
12129                             this.onLoad();
12130                             return;
12131                         }
12132                         
12133                         this.store.filter(this.displayField, q);
12134                     }
12135                     
12136                     this.store.fireEvent("datachanged", this.store);
12137                     
12138                     this.onLoad();
12139                     
12140                     
12141                 }else{
12142                     
12143                     this.store.baseParams[this.queryParam] = q;
12144                     
12145                     var options = {params : this.getParams(q)};
12146                     
12147                     if(this.loadNext){
12148                         options.add = true;
12149                         options.params.start = this.page * this.pageSize;
12150                     }
12151                     
12152                     this.store.load(options);
12153                     
12154                     /*
12155                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12156                      *  we should expand the list on onLoad
12157                      *  so command out it
12158                      */
12159 //                    this.expand();
12160                 }
12161             }else{
12162                 this.selectedIndex = -1;
12163                 this.onLoad();   
12164             }
12165         }
12166         
12167         this.loadNext = false;
12168     },
12169     
12170     // private
12171     getParams : function(q){
12172         var p = {};
12173         //p[this.queryParam] = q;
12174         
12175         if(this.pageSize){
12176             p.start = 0;
12177             p.limit = this.pageSize;
12178         }
12179         return p;
12180     },
12181
12182     /**
12183      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12184      */
12185     collapse : function(){
12186         if(!this.isExpanded()){
12187             return;
12188         }
12189         
12190         this.list.hide();
12191         
12192         if(this.tickable){
12193             this.hasFocus = false;
12194             this.okBtn.hide();
12195             this.cancelBtn.hide();
12196             this.trigger.show();
12197             
12198             if(this.editable){
12199                 this.tickableInputEl().dom.value = '';
12200                 this.tickableInputEl().blur();
12201             }
12202             
12203         }
12204         
12205         Roo.get(document).un('mousedown', this.collapseIf, this);
12206         Roo.get(document).un('mousewheel', this.collapseIf, this);
12207         if (!this.editable) {
12208             Roo.get(document).un('keydown', this.listKeyPress, this);
12209         }
12210         this.fireEvent('collapse', this);
12211     },
12212
12213     // private
12214     collapseIf : function(e){
12215         var in_combo  = e.within(this.el);
12216         var in_list =  e.within(this.list);
12217         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12218         
12219         if (in_combo || in_list || is_list) {
12220             //e.stopPropagation();
12221             return;
12222         }
12223         
12224         if(this.tickable){
12225             this.onTickableFooterButtonClick(e, false, false);
12226         }
12227
12228         this.collapse();
12229         
12230     },
12231
12232     /**
12233      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12234      */
12235     expand : function(){
12236        
12237         if(this.isExpanded() || !this.hasFocus){
12238             return;
12239         }
12240         
12241         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12242         this.list.setWidth(lw);
12243         
12244         
12245          Roo.log('expand');
12246         
12247         this.list.show();
12248         
12249         this.restrictHeight();
12250         
12251         if(this.tickable){
12252             
12253             this.tickItems = Roo.apply([], this.item);
12254             
12255             this.okBtn.show();
12256             this.cancelBtn.show();
12257             this.trigger.hide();
12258             
12259             if(this.editable){
12260                 this.tickableInputEl().focus();
12261             }
12262             
12263         }
12264         
12265         Roo.get(document).on('mousedown', this.collapseIf, this);
12266         Roo.get(document).on('mousewheel', this.collapseIf, this);
12267         if (!this.editable) {
12268             Roo.get(document).on('keydown', this.listKeyPress, this);
12269         }
12270         
12271         this.fireEvent('expand', this);
12272     },
12273
12274     // private
12275     // Implements the default empty TriggerField.onTriggerClick function
12276     onTriggerClick : function(e)
12277     {
12278         Roo.log('trigger click');
12279         
12280         if(this.disabled || !this.triggerList){
12281             return;
12282         }
12283         
12284         this.page = 0;
12285         this.loadNext = false;
12286         
12287         if(this.isExpanded()){
12288             this.collapse();
12289             if (!this.blockFocus) {
12290                 this.inputEl().focus();
12291             }
12292             
12293         }else {
12294             this.hasFocus = true;
12295             if(this.triggerAction == 'all') {
12296                 this.doQuery(this.allQuery, true);
12297             } else {
12298                 this.doQuery(this.getRawValue());
12299             }
12300             if (!this.blockFocus) {
12301                 this.inputEl().focus();
12302             }
12303         }
12304     },
12305     
12306     onTickableTriggerClick : function(e)
12307     {
12308         if(this.disabled){
12309             return;
12310         }
12311         
12312         this.page = 0;
12313         this.loadNext = false;
12314         this.hasFocus = true;
12315         
12316         if(this.triggerAction == 'all') {
12317             this.doQuery(this.allQuery, true);
12318         } else {
12319             this.doQuery(this.getRawValue());
12320         }
12321     },
12322     
12323     onSearchFieldClick : function(e)
12324     {
12325         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12326             this.onTickableFooterButtonClick(e, false, false);
12327             return;
12328         }
12329         
12330         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12331             return;
12332         }
12333         
12334         this.page = 0;
12335         this.loadNext = false;
12336         this.hasFocus = true;
12337         
12338         if(this.triggerAction == 'all') {
12339             this.doQuery(this.allQuery, true);
12340         } else {
12341             this.doQuery(this.getRawValue());
12342         }
12343     },
12344     
12345     listKeyPress : function(e)
12346     {
12347         //Roo.log('listkeypress');
12348         // scroll to first matching element based on key pres..
12349         if (e.isSpecialKey()) {
12350             return false;
12351         }
12352         var k = String.fromCharCode(e.getKey()).toUpperCase();
12353         //Roo.log(k);
12354         var match  = false;
12355         var csel = this.view.getSelectedNodes();
12356         var cselitem = false;
12357         if (csel.length) {
12358             var ix = this.view.indexOf(csel[0]);
12359             cselitem  = this.store.getAt(ix);
12360             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12361                 cselitem = false;
12362             }
12363             
12364         }
12365         
12366         this.store.each(function(v) { 
12367             if (cselitem) {
12368                 // start at existing selection.
12369                 if (cselitem.id == v.id) {
12370                     cselitem = false;
12371                 }
12372                 return true;
12373             }
12374                 
12375             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12376                 match = this.store.indexOf(v);
12377                 return false;
12378             }
12379             return true;
12380         }, this);
12381         
12382         if (match === false) {
12383             return true; // no more action?
12384         }
12385         // scroll to?
12386         this.view.select(match);
12387         var sn = Roo.get(this.view.getSelectedNodes()[0])
12388         sn.scrollIntoView(sn.dom.parentNode, false);
12389     },
12390     
12391     onViewScroll : function(e, t){
12392         
12393         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
12394             return;
12395         }
12396         
12397         this.hasQuery = true;
12398         
12399         this.loading = this.list.select('.loading', true).first();
12400         
12401         if(this.loading === null){
12402             this.list.createChild({
12403                 tag: 'div',
12404                 cls: 'loading select2-more-results select2-active',
12405                 html: 'Loading more results...'
12406             })
12407             
12408             this.loading = this.list.select('.loading', true).first();
12409             
12410             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12411             
12412             this.loading.hide();
12413         }
12414         
12415         this.loading.show();
12416         
12417         var _combo = this;
12418         
12419         this.page++;
12420         this.loadNext = true;
12421         
12422         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12423         
12424         return;
12425     },
12426     
12427     addItem : function(o)
12428     {   
12429         var dv = ''; // display value
12430         
12431         if (this.displayField) {
12432             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12433         } else {
12434             // this is an error condition!!!
12435             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12436         }
12437         
12438         if(!dv.length){
12439             return;
12440         }
12441         
12442         var choice = this.choices.createChild({
12443             tag: 'li',
12444             cls: 'select2-search-choice',
12445             cn: [
12446                 {
12447                     tag: 'div',
12448                     html: dv
12449                 },
12450                 {
12451                     tag: 'a',
12452                     href: '#',
12453                     cls: 'select2-search-choice-close',
12454                     tabindex: '-1'
12455                 }
12456             ]
12457             
12458         }, this.searchField);
12459         
12460         var close = choice.select('a.select2-search-choice-close', true).first()
12461         
12462         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12463         
12464         this.item.push(o);
12465         
12466         this.lastData = o;
12467         
12468         this.syncValue();
12469         
12470         this.inputEl().dom.value = '';
12471         
12472         this.validate();
12473     },
12474     
12475     onRemoveItem : function(e, _self, o)
12476     {
12477         e.preventDefault();
12478         
12479         this.lastItem = Roo.apply([], this.item);
12480         
12481         var index = this.item.indexOf(o.data) * 1;
12482         
12483         if( index < 0){
12484             Roo.log('not this item?!');
12485             return;
12486         }
12487         
12488         this.item.splice(index, 1);
12489         o.item.remove();
12490         
12491         this.syncValue();
12492         
12493         this.fireEvent('remove', this, e);
12494         
12495         this.validate();
12496         
12497     },
12498     
12499     syncValue : function()
12500     {
12501         if(!this.item.length){
12502             this.clearValue();
12503             return;
12504         }
12505             
12506         var value = [];
12507         var _this = this;
12508         Roo.each(this.item, function(i){
12509             if(_this.valueField){
12510                 value.push(i[_this.valueField]);
12511                 return;
12512             }
12513
12514             value.push(i);
12515         });
12516
12517         this.value = value.join(',');
12518
12519         if(this.hiddenField){
12520             this.hiddenField.dom.value = this.value;
12521         }
12522         
12523         this.store.fireEvent("datachanged", this.store);
12524     },
12525     
12526     clearItem : function()
12527     {
12528         if(!this.multiple){
12529             return;
12530         }
12531         
12532         this.item = [];
12533         
12534         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12535            c.remove();
12536         });
12537         
12538         this.syncValue();
12539         
12540         this.validate();
12541     },
12542     
12543     inputEl: function ()
12544     {
12545         if(this.tickable){
12546             return this.searchField;
12547         }
12548         return this.el.select('input.form-control',true).first();
12549     },
12550     
12551     
12552     onTickableFooterButtonClick : function(e, btn, el)
12553     {
12554         e.preventDefault();
12555         
12556         this.lastItem = Roo.apply([], this.item);
12557         
12558         if(btn && btn.name == 'cancel'){
12559             this.tickItems = Roo.apply([], this.item);
12560             this.collapse();
12561             return;
12562         }
12563         
12564         this.clearItem();
12565         
12566         var _this = this;
12567         
12568         Roo.each(this.tickItems, function(o){
12569             _this.addItem(o);
12570         });
12571         
12572         this.collapse();
12573         
12574     },
12575     
12576     validate : function()
12577     {
12578         var v = this.getRawValue();
12579         
12580         if(this.multiple){
12581             v = this.getValue();
12582         }
12583         
12584         if(this.disabled || this.allowBlank || v.length){
12585             this.markValid();
12586             return true;
12587         }
12588         
12589         this.markInvalid();
12590         return false;
12591     },
12592     
12593     tickableInputEl : function()
12594     {
12595         if(!this.tickable || !this.editable){
12596             return this.inputEl();
12597         }
12598         
12599         return this.inputEl().select('.select2-search-field-input', true).first();
12600     }
12601     
12602     
12603
12604     /** 
12605     * @cfg {Boolean} grow 
12606     * @hide 
12607     */
12608     /** 
12609     * @cfg {Number} growMin 
12610     * @hide 
12611     */
12612     /** 
12613     * @cfg {Number} growMax 
12614     * @hide 
12615     */
12616     /**
12617      * @hide
12618      * @method autoSize
12619      */
12620 });
12621 /*
12622  * Based on:
12623  * Ext JS Library 1.1.1
12624  * Copyright(c) 2006-2007, Ext JS, LLC.
12625  *
12626  * Originally Released Under LGPL - original licence link has changed is not relivant.
12627  *
12628  * Fork - LGPL
12629  * <script type="text/javascript">
12630  */
12631
12632 /**
12633  * @class Roo.View
12634  * @extends Roo.util.Observable
12635  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12636  * This class also supports single and multi selection modes. <br>
12637  * Create a data model bound view:
12638  <pre><code>
12639  var store = new Roo.data.Store(...);
12640
12641  var view = new Roo.View({
12642     el : "my-element",
12643     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12644  
12645     singleSelect: true,
12646     selectedClass: "ydataview-selected",
12647     store: store
12648  });
12649
12650  // listen for node click?
12651  view.on("click", function(vw, index, node, e){
12652  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12653  });
12654
12655  // load XML data
12656  dataModel.load("foobar.xml");
12657  </code></pre>
12658  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12659  * <br><br>
12660  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12661  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12662  * 
12663  * Note: old style constructor is still suported (container, template, config)
12664  * 
12665  * @constructor
12666  * Create a new View
12667  * @param {Object} config The config object
12668  * 
12669  */
12670 Roo.View = function(config, depreciated_tpl, depreciated_config){
12671     
12672     this.parent = false;
12673     
12674     if (typeof(depreciated_tpl) == 'undefined') {
12675         // new way.. - universal constructor.
12676         Roo.apply(this, config);
12677         this.el  = Roo.get(this.el);
12678     } else {
12679         // old format..
12680         this.el  = Roo.get(config);
12681         this.tpl = depreciated_tpl;
12682         Roo.apply(this, depreciated_config);
12683     }
12684     this.wrapEl  = this.el.wrap().wrap();
12685     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12686     
12687     
12688     if(typeof(this.tpl) == "string"){
12689         this.tpl = new Roo.Template(this.tpl);
12690     } else {
12691         // support xtype ctors..
12692         this.tpl = new Roo.factory(this.tpl, Roo);
12693     }
12694     
12695     
12696     this.tpl.compile();
12697     
12698     /** @private */
12699     this.addEvents({
12700         /**
12701          * @event beforeclick
12702          * Fires before a click is processed. Returns false to cancel the default action.
12703          * @param {Roo.View} this
12704          * @param {Number} index The index of the target node
12705          * @param {HTMLElement} node The target node
12706          * @param {Roo.EventObject} e The raw event object
12707          */
12708             "beforeclick" : true,
12709         /**
12710          * @event click
12711          * Fires when a template node is clicked.
12712          * @param {Roo.View} this
12713          * @param {Number} index The index of the target node
12714          * @param {HTMLElement} node The target node
12715          * @param {Roo.EventObject} e The raw event object
12716          */
12717             "click" : true,
12718         /**
12719          * @event dblclick
12720          * Fires when a template node is double clicked.
12721          * @param {Roo.View} this
12722          * @param {Number} index The index of the target node
12723          * @param {HTMLElement} node The target node
12724          * @param {Roo.EventObject} e The raw event object
12725          */
12726             "dblclick" : true,
12727         /**
12728          * @event contextmenu
12729          * Fires when a template node is right clicked.
12730          * @param {Roo.View} this
12731          * @param {Number} index The index of the target node
12732          * @param {HTMLElement} node The target node
12733          * @param {Roo.EventObject} e The raw event object
12734          */
12735             "contextmenu" : true,
12736         /**
12737          * @event selectionchange
12738          * Fires when the selected nodes change.
12739          * @param {Roo.View} this
12740          * @param {Array} selections Array of the selected nodes
12741          */
12742             "selectionchange" : true,
12743     
12744         /**
12745          * @event beforeselect
12746          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12747          * @param {Roo.View} this
12748          * @param {HTMLElement} node The node to be selected
12749          * @param {Array} selections Array of currently selected nodes
12750          */
12751             "beforeselect" : true,
12752         /**
12753          * @event preparedata
12754          * Fires on every row to render, to allow you to change the data.
12755          * @param {Roo.View} this
12756          * @param {Object} data to be rendered (change this)
12757          */
12758           "preparedata" : true
12759           
12760           
12761         });
12762
12763
12764
12765     this.el.on({
12766         "click": this.onClick,
12767         "dblclick": this.onDblClick,
12768         "contextmenu": this.onContextMenu,
12769         scope:this
12770     });
12771
12772     this.selections = [];
12773     this.nodes = [];
12774     this.cmp = new Roo.CompositeElementLite([]);
12775     if(this.store){
12776         this.store = Roo.factory(this.store, Roo.data);
12777         this.setStore(this.store, true);
12778     }
12779     
12780     if ( this.footer && this.footer.xtype) {
12781            
12782          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12783         
12784         this.footer.dataSource = this.store
12785         this.footer.container = fctr;
12786         this.footer = Roo.factory(this.footer, Roo);
12787         fctr.insertFirst(this.el);
12788         
12789         // this is a bit insane - as the paging toolbar seems to detach the el..
12790 //        dom.parentNode.parentNode.parentNode
12791          // they get detached?
12792     }
12793     
12794     
12795     Roo.View.superclass.constructor.call(this);
12796     
12797     
12798 };
12799
12800 Roo.extend(Roo.View, Roo.util.Observable, {
12801     
12802      /**
12803      * @cfg {Roo.data.Store} store Data store to load data from.
12804      */
12805     store : false,
12806     
12807     /**
12808      * @cfg {String|Roo.Element} el The container element.
12809      */
12810     el : '',
12811     
12812     /**
12813      * @cfg {String|Roo.Template} tpl The template used by this View 
12814      */
12815     tpl : false,
12816     /**
12817      * @cfg {String} dataName the named area of the template to use as the data area
12818      *                          Works with domtemplates roo-name="name"
12819      */
12820     dataName: false,
12821     /**
12822      * @cfg {String} selectedClass The css class to add to selected nodes
12823      */
12824     selectedClass : "x-view-selected",
12825      /**
12826      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12827      */
12828     emptyText : "",
12829     
12830     /**
12831      * @cfg {String} text to display on mask (default Loading)
12832      */
12833     mask : false,
12834     /**
12835      * @cfg {Boolean} multiSelect Allow multiple selection
12836      */
12837     multiSelect : false,
12838     /**
12839      * @cfg {Boolean} singleSelect Allow single selection
12840      */
12841     singleSelect:  false,
12842     
12843     /**
12844      * @cfg {Boolean} toggleSelect - selecting 
12845      */
12846     toggleSelect : false,
12847     
12848     /**
12849      * @cfg {Boolean} tickable - selecting 
12850      */
12851     tickable : false,
12852     
12853     /**
12854      * Returns the element this view is bound to.
12855      * @return {Roo.Element}
12856      */
12857     getEl : function(){
12858         return this.wrapEl;
12859     },
12860     
12861     
12862
12863     /**
12864      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12865      */
12866     refresh : function(){
12867         //Roo.log('refresh');
12868         var t = this.tpl;
12869         
12870         // if we are using something like 'domtemplate', then
12871         // the what gets used is:
12872         // t.applySubtemplate(NAME, data, wrapping data..)
12873         // the outer template then get' applied with
12874         //     the store 'extra data'
12875         // and the body get's added to the
12876         //      roo-name="data" node?
12877         //      <span class='roo-tpl-{name}'></span> ?????
12878         
12879         
12880         
12881         this.clearSelections();
12882         this.el.update("");
12883         var html = [];
12884         var records = this.store.getRange();
12885         if(records.length < 1) {
12886             
12887             // is this valid??  = should it render a template??
12888             
12889             this.el.update(this.emptyText);
12890             return;
12891         }
12892         var el = this.el;
12893         if (this.dataName) {
12894             this.el.update(t.apply(this.store.meta)); //????
12895             el = this.el.child('.roo-tpl-' + this.dataName);
12896         }
12897         
12898         for(var i = 0, len = records.length; i < len; i++){
12899             var data = this.prepareData(records[i].data, i, records[i]);
12900             this.fireEvent("preparedata", this, data, i, records[i]);
12901             
12902             var d = Roo.apply({}, data);
12903             
12904             if(this.tickable){
12905                 Roo.apply(d, {'roo-id' : Roo.id()});
12906                 
12907                 var _this = this;
12908             
12909                 Roo.each(this.parent.item, function(item){
12910                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12911                         return;
12912                     }
12913                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12914                 });
12915             }
12916             
12917             html[html.length] = Roo.util.Format.trim(
12918                 this.dataName ?
12919                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12920                     t.apply(d)
12921             );
12922         }
12923         
12924         
12925         
12926         el.update(html.join(""));
12927         this.nodes = el.dom.childNodes;
12928         this.updateIndexes(0);
12929     },
12930     
12931
12932     /**
12933      * Function to override to reformat the data that is sent to
12934      * the template for each node.
12935      * DEPRICATED - use the preparedata event handler.
12936      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12937      * a JSON object for an UpdateManager bound view).
12938      */
12939     prepareData : function(data, index, record)
12940     {
12941         this.fireEvent("preparedata", this, data, index, record);
12942         return data;
12943     },
12944
12945     onUpdate : function(ds, record){
12946         // Roo.log('on update');   
12947         this.clearSelections();
12948         var index = this.store.indexOf(record);
12949         var n = this.nodes[index];
12950         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12951         n.parentNode.removeChild(n);
12952         this.updateIndexes(index, index);
12953     },
12954
12955     
12956     
12957 // --------- FIXME     
12958     onAdd : function(ds, records, index)
12959     {
12960         //Roo.log(['on Add', ds, records, index] );        
12961         this.clearSelections();
12962         if(this.nodes.length == 0){
12963             this.refresh();
12964             return;
12965         }
12966         var n = this.nodes[index];
12967         for(var i = 0, len = records.length; i < len; i++){
12968             var d = this.prepareData(records[i].data, i, records[i]);
12969             if(n){
12970                 this.tpl.insertBefore(n, d);
12971             }else{
12972                 
12973                 this.tpl.append(this.el, d);
12974             }
12975         }
12976         this.updateIndexes(index);
12977     },
12978
12979     onRemove : function(ds, record, index){
12980        // Roo.log('onRemove');
12981         this.clearSelections();
12982         var el = this.dataName  ?
12983             this.el.child('.roo-tpl-' + this.dataName) :
12984             this.el; 
12985         
12986         el.dom.removeChild(this.nodes[index]);
12987         this.updateIndexes(index);
12988     },
12989
12990     /**
12991      * Refresh an individual node.
12992      * @param {Number} index
12993      */
12994     refreshNode : function(index){
12995         this.onUpdate(this.store, this.store.getAt(index));
12996     },
12997
12998     updateIndexes : function(startIndex, endIndex){
12999         var ns = this.nodes;
13000         startIndex = startIndex || 0;
13001         endIndex = endIndex || ns.length - 1;
13002         for(var i = startIndex; i <= endIndex; i++){
13003             ns[i].nodeIndex = i;
13004         }
13005     },
13006
13007     /**
13008      * Changes the data store this view uses and refresh the view.
13009      * @param {Store} store
13010      */
13011     setStore : function(store, initial){
13012         if(!initial && this.store){
13013             this.store.un("datachanged", this.refresh);
13014             this.store.un("add", this.onAdd);
13015             this.store.un("remove", this.onRemove);
13016             this.store.un("update", this.onUpdate);
13017             this.store.un("clear", this.refresh);
13018             this.store.un("beforeload", this.onBeforeLoad);
13019             this.store.un("load", this.onLoad);
13020             this.store.un("loadexception", this.onLoad);
13021         }
13022         if(store){
13023           
13024             store.on("datachanged", this.refresh, this);
13025             store.on("add", this.onAdd, this);
13026             store.on("remove", this.onRemove, this);
13027             store.on("update", this.onUpdate, this);
13028             store.on("clear", this.refresh, this);
13029             store.on("beforeload", this.onBeforeLoad, this);
13030             store.on("load", this.onLoad, this);
13031             store.on("loadexception", this.onLoad, this);
13032         }
13033         
13034         if(store){
13035             this.refresh();
13036         }
13037     },
13038     /**
13039      * onbeforeLoad - masks the loading area.
13040      *
13041      */
13042     onBeforeLoad : function(store,opts)
13043     {
13044          //Roo.log('onBeforeLoad');   
13045         if (!opts.add) {
13046             this.el.update("");
13047         }
13048         this.el.mask(this.mask ? this.mask : "Loading" ); 
13049     },
13050     onLoad : function ()
13051     {
13052         this.el.unmask();
13053     },
13054     
13055
13056     /**
13057      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13058      * @param {HTMLElement} node
13059      * @return {HTMLElement} The template node
13060      */
13061     findItemFromChild : function(node){
13062         var el = this.dataName  ?
13063             this.el.child('.roo-tpl-' + this.dataName,true) :
13064             this.el.dom; 
13065         
13066         if(!node || node.parentNode == el){
13067                     return node;
13068             }
13069             var p = node.parentNode;
13070             while(p && p != el){
13071             if(p.parentNode == el){
13072                 return p;
13073             }
13074             p = p.parentNode;
13075         }
13076             return null;
13077     },
13078
13079     /** @ignore */
13080     onClick : function(e){
13081         var item = this.findItemFromChild(e.getTarget());
13082         if(item){
13083             var index = this.indexOf(item);
13084             if(this.onItemClick(item, index, e) !== false){
13085                 this.fireEvent("click", this, index, item, e);
13086             }
13087         }else{
13088             this.clearSelections();
13089         }
13090     },
13091
13092     /** @ignore */
13093     onContextMenu : function(e){
13094         var item = this.findItemFromChild(e.getTarget());
13095         if(item){
13096             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
13097         }
13098     },
13099
13100     /** @ignore */
13101     onDblClick : function(e){
13102         var item = this.findItemFromChild(e.getTarget());
13103         if(item){
13104             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
13105         }
13106     },
13107
13108     onItemClick : function(item, index, e)
13109     {
13110         if(this.fireEvent("beforeclick", this, index, item, e) === false){
13111             return false;
13112         }
13113         if (this.toggleSelect) {
13114             var m = this.isSelected(item) ? 'unselect' : 'select';
13115             //Roo.log(m);
13116             var _t = this;
13117             _t[m](item, true, false);
13118             return true;
13119         }
13120         if(this.multiSelect || this.singleSelect){
13121             if(this.multiSelect && e.shiftKey && this.lastSelection){
13122                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
13123             }else{
13124                 this.select(item, this.multiSelect && e.ctrlKey);
13125                 this.lastSelection = item;
13126             }
13127             
13128             if(!this.tickable){
13129                 e.preventDefault();
13130             }
13131             
13132         }
13133         return true;
13134     },
13135
13136     /**
13137      * Get the number of selected nodes.
13138      * @return {Number}
13139      */
13140     getSelectionCount : function(){
13141         return this.selections.length;
13142     },
13143
13144     /**
13145      * Get the currently selected nodes.
13146      * @return {Array} An array of HTMLElements
13147      */
13148     getSelectedNodes : function(){
13149         return this.selections;
13150     },
13151
13152     /**
13153      * Get the indexes of the selected nodes.
13154      * @return {Array}
13155      */
13156     getSelectedIndexes : function(){
13157         var indexes = [], s = this.selections;
13158         for(var i = 0, len = s.length; i < len; i++){
13159             indexes.push(s[i].nodeIndex);
13160         }
13161         return indexes;
13162     },
13163
13164     /**
13165      * Clear all selections
13166      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
13167      */
13168     clearSelections : function(suppressEvent){
13169         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
13170             this.cmp.elements = this.selections;
13171             this.cmp.removeClass(this.selectedClass);
13172             this.selections = [];
13173             if(!suppressEvent){
13174                 this.fireEvent("selectionchange", this, this.selections);
13175             }
13176         }
13177     },
13178
13179     /**
13180      * Returns true if the passed node is selected
13181      * @param {HTMLElement/Number} node The node or node index
13182      * @return {Boolean}
13183      */
13184     isSelected : function(node){
13185         var s = this.selections;
13186         if(s.length < 1){
13187             return false;
13188         }
13189         node = this.getNode(node);
13190         return s.indexOf(node) !== -1;
13191     },
13192
13193     /**
13194      * Selects nodes.
13195      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
13196      * @param {Boolean} keepExisting (optional) true to keep existing selections
13197      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13198      */
13199     select : function(nodeInfo, keepExisting, suppressEvent){
13200         if(nodeInfo instanceof Array){
13201             if(!keepExisting){
13202                 this.clearSelections(true);
13203             }
13204             for(var i = 0, len = nodeInfo.length; i < len; i++){
13205                 this.select(nodeInfo[i], true, true);
13206             }
13207             return;
13208         } 
13209         var node = this.getNode(nodeInfo);
13210         if(!node || this.isSelected(node)){
13211             return; // already selected.
13212         }
13213         if(!keepExisting){
13214             this.clearSelections(true);
13215         }
13216         
13217         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
13218             Roo.fly(node).addClass(this.selectedClass);
13219             this.selections.push(node);
13220             if(!suppressEvent){
13221                 this.fireEvent("selectionchange", this, this.selections);
13222             }
13223         }
13224         
13225         
13226     },
13227       /**
13228      * Unselects nodes.
13229      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
13230      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
13231      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13232      */
13233     unselect : function(nodeInfo, keepExisting, suppressEvent)
13234     {
13235         if(nodeInfo instanceof Array){
13236             Roo.each(this.selections, function(s) {
13237                 this.unselect(s, nodeInfo);
13238             }, this);
13239             return;
13240         }
13241         var node = this.getNode(nodeInfo);
13242         if(!node || !this.isSelected(node)){
13243             //Roo.log("not selected");
13244             return; // not selected.
13245         }
13246         // fireevent???
13247         var ns = [];
13248         Roo.each(this.selections, function(s) {
13249             if (s == node ) {
13250                 Roo.fly(node).removeClass(this.selectedClass);
13251
13252                 return;
13253             }
13254             ns.push(s);
13255         },this);
13256         
13257         this.selections= ns;
13258         this.fireEvent("selectionchange", this, this.selections);
13259     },
13260
13261     /**
13262      * Gets a template node.
13263      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13264      * @return {HTMLElement} The node or null if it wasn't found
13265      */
13266     getNode : function(nodeInfo){
13267         if(typeof nodeInfo == "string"){
13268             return document.getElementById(nodeInfo);
13269         }else if(typeof nodeInfo == "number"){
13270             return this.nodes[nodeInfo];
13271         }
13272         return nodeInfo;
13273     },
13274
13275     /**
13276      * Gets a range template nodes.
13277      * @param {Number} startIndex
13278      * @param {Number} endIndex
13279      * @return {Array} An array of nodes
13280      */
13281     getNodes : function(start, end){
13282         var ns = this.nodes;
13283         start = start || 0;
13284         end = typeof end == "undefined" ? ns.length - 1 : end;
13285         var nodes = [];
13286         if(start <= end){
13287             for(var i = start; i <= end; i++){
13288                 nodes.push(ns[i]);
13289             }
13290         } else{
13291             for(var i = start; i >= end; i--){
13292                 nodes.push(ns[i]);
13293             }
13294         }
13295         return nodes;
13296     },
13297
13298     /**
13299      * Finds the index of the passed node
13300      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13301      * @return {Number} The index of the node or -1
13302      */
13303     indexOf : function(node){
13304         node = this.getNode(node);
13305         if(typeof node.nodeIndex == "number"){
13306             return node.nodeIndex;
13307         }
13308         var ns = this.nodes;
13309         for(var i = 0, len = ns.length; i < len; i++){
13310             if(ns[i] == node){
13311                 return i;
13312             }
13313         }
13314         return -1;
13315     }
13316 });
13317 /*
13318  * - LGPL
13319  *
13320  * based on jquery fullcalendar
13321  * 
13322  */
13323
13324 Roo.bootstrap = Roo.bootstrap || {};
13325 /**
13326  * @class Roo.bootstrap.Calendar
13327  * @extends Roo.bootstrap.Component
13328  * Bootstrap Calendar class
13329  * @cfg {Boolean} loadMask (true|false) default false
13330  * @cfg {Object} header generate the user specific header of the calendar, default false
13331
13332  * @constructor
13333  * Create a new Container
13334  * @param {Object} config The config object
13335  */
13336
13337
13338
13339 Roo.bootstrap.Calendar = function(config){
13340     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
13341      this.addEvents({
13342         /**
13343              * @event select
13344              * Fires when a date is selected
13345              * @param {DatePicker} this
13346              * @param {Date} date The selected date
13347              */
13348         'select': true,
13349         /**
13350              * @event monthchange
13351              * Fires when the displayed month changes 
13352              * @param {DatePicker} this
13353              * @param {Date} date The selected month
13354              */
13355         'monthchange': true,
13356         /**
13357              * @event evententer
13358              * Fires when mouse over an event
13359              * @param {Calendar} this
13360              * @param {event} Event
13361              */
13362         'evententer': true,
13363         /**
13364              * @event eventleave
13365              * Fires when the mouse leaves an
13366              * @param {Calendar} this
13367              * @param {event}
13368              */
13369         'eventleave': true,
13370         /**
13371              * @event eventclick
13372              * Fires when the mouse click an
13373              * @param {Calendar} this
13374              * @param {event}
13375              */
13376         'eventclick': true
13377         
13378     });
13379
13380 };
13381
13382 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
13383     
13384      /**
13385      * @cfg {Number} startDay
13386      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
13387      */
13388     startDay : 0,
13389     
13390     loadMask : false,
13391     
13392     header : false,
13393       
13394     getAutoCreate : function(){
13395         
13396         
13397         var fc_button = function(name, corner, style, content ) {
13398             return Roo.apply({},{
13399                 tag : 'span',
13400                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
13401                          (corner.length ?
13402                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
13403                             ''
13404                         ),
13405                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
13406                 unselectable: 'on'
13407             });
13408         };
13409         
13410         var header = {};
13411         
13412         if(!this.header){
13413             header = {
13414                 tag : 'table',
13415                 cls : 'fc-header',
13416                 style : 'width:100%',
13417                 cn : [
13418                     {
13419                         tag: 'tr',
13420                         cn : [
13421                             {
13422                                 tag : 'td',
13423                                 cls : 'fc-header-left',
13424                                 cn : [
13425                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
13426                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
13427                                     { tag: 'span', cls: 'fc-header-space' },
13428                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
13429
13430
13431                                 ]
13432                             },
13433
13434                             {
13435                                 tag : 'td',
13436                                 cls : 'fc-header-center',
13437                                 cn : [
13438                                     {
13439                                         tag: 'span',
13440                                         cls: 'fc-header-title',
13441                                         cn : {
13442                                             tag: 'H2',
13443                                             html : 'month / year'
13444                                         }
13445                                     }
13446
13447                                 ]
13448                             },
13449                             {
13450                                 tag : 'td',
13451                                 cls : 'fc-header-right',
13452                                 cn : [
13453                               /*      fc_button('month', 'left', '', 'month' ),
13454                                     fc_button('week', '', '', 'week' ),
13455                                     fc_button('day', 'right', '', 'day' )
13456                                 */    
13457
13458                                 ]
13459                             }
13460
13461                         ]
13462                     }
13463                 ]
13464             };
13465         }
13466         
13467         header = this.header;
13468         
13469        
13470         var cal_heads = function() {
13471             var ret = [];
13472             // fixme - handle this.
13473             
13474             for (var i =0; i < Date.dayNames.length; i++) {
13475                 var d = Date.dayNames[i];
13476                 ret.push({
13477                     tag: 'th',
13478                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13479                     html : d.substring(0,3)
13480                 });
13481                 
13482             }
13483             ret[0].cls += ' fc-first';
13484             ret[6].cls += ' fc-last';
13485             return ret;
13486         };
13487         var cal_cell = function(n) {
13488             return  {
13489                 tag: 'td',
13490                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13491                 cn : [
13492                     {
13493                         cn : [
13494                             {
13495                                 cls: 'fc-day-number',
13496                                 html: 'D'
13497                             },
13498                             {
13499                                 cls: 'fc-day-content',
13500                              
13501                                 cn : [
13502                                      {
13503                                         style: 'position: relative;' // height: 17px;
13504                                     }
13505                                 ]
13506                             }
13507                             
13508                             
13509                         ]
13510                     }
13511                 ]
13512                 
13513             }
13514         };
13515         var cal_rows = function() {
13516             
13517             var ret = [];
13518             for (var r = 0; r < 6; r++) {
13519                 var row= {
13520                     tag : 'tr',
13521                     cls : 'fc-week',
13522                     cn : []
13523                 };
13524                 
13525                 for (var i =0; i < Date.dayNames.length; i++) {
13526                     var d = Date.dayNames[i];
13527                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13528
13529                 }
13530                 row.cn[0].cls+=' fc-first';
13531                 row.cn[0].cn[0].style = 'min-height:90px';
13532                 row.cn[6].cls+=' fc-last';
13533                 ret.push(row);
13534                 
13535             }
13536             ret[0].cls += ' fc-first';
13537             ret[4].cls += ' fc-prev-last';
13538             ret[5].cls += ' fc-last';
13539             return ret;
13540             
13541         };
13542         
13543         var cal_table = {
13544             tag: 'table',
13545             cls: 'fc-border-separate',
13546             style : 'width:100%',
13547             cellspacing  : 0,
13548             cn : [
13549                 { 
13550                     tag: 'thead',
13551                     cn : [
13552                         { 
13553                             tag: 'tr',
13554                             cls : 'fc-first fc-last',
13555                             cn : cal_heads()
13556                         }
13557                     ]
13558                 },
13559                 { 
13560                     tag: 'tbody',
13561                     cn : cal_rows()
13562                 }
13563                   
13564             ]
13565         };
13566          
13567          var cfg = {
13568             cls : 'fc fc-ltr',
13569             cn : [
13570                 header,
13571                 {
13572                     cls : 'fc-content',
13573                     style : "position: relative;",
13574                     cn : [
13575                         {
13576                             cls : 'fc-view fc-view-month fc-grid',
13577                             style : 'position: relative',
13578                             unselectable : 'on',
13579                             cn : [
13580                                 {
13581                                     cls : 'fc-event-container',
13582                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13583                                 },
13584                                 cal_table
13585                             ]
13586                         }
13587                     ]
13588     
13589                 }
13590            ] 
13591             
13592         };
13593         
13594          
13595         
13596         return cfg;
13597     },
13598     
13599     
13600     initEvents : function()
13601     {
13602         if(!this.store){
13603             throw "can not find store for calendar";
13604         }
13605         
13606         var mark = {
13607             tag: "div",
13608             cls:"x-dlg-mask",
13609             style: "text-align:center",
13610             cn: [
13611                 {
13612                     tag: "div",
13613                     style: "background-color:white;width:50%;margin:250 auto",
13614                     cn: [
13615                         {
13616                             tag: "img",
13617                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13618                         },
13619                         {
13620                             tag: "span",
13621                             html: "Loading"
13622                         }
13623                         
13624                     ]
13625                 }
13626             ]
13627         }
13628         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13629         
13630         var size = this.el.select('.fc-content', true).first().getSize();
13631         this.maskEl.setSize(size.width, size.height);
13632         this.maskEl.enableDisplayMode("block");
13633         if(!this.loadMask){
13634             this.maskEl.hide();
13635         }
13636         
13637         this.store = Roo.factory(this.store, Roo.data);
13638         this.store.on('load', this.onLoad, this);
13639         this.store.on('beforeload', this.onBeforeLoad, this);
13640         
13641         this.resize();
13642         
13643         this.cells = this.el.select('.fc-day',true);
13644         //Roo.log(this.cells);
13645         this.textNodes = this.el.query('.fc-day-number');
13646         this.cells.addClassOnOver('fc-state-hover');
13647         
13648         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13649         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13650         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13651         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13652         
13653         this.on('monthchange', this.onMonthChange, this);
13654         
13655         this.update(new Date().clearTime());
13656     },
13657     
13658     resize : function() {
13659         var sz  = this.el.getSize();
13660         
13661         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13662         this.el.select('.fc-day-content div',true).setHeight(34);
13663     },
13664     
13665     
13666     // private
13667     showPrevMonth : function(e){
13668         this.update(this.activeDate.add("mo", -1));
13669     },
13670     showToday : function(e){
13671         this.update(new Date().clearTime());
13672     },
13673     // private
13674     showNextMonth : function(e){
13675         this.update(this.activeDate.add("mo", 1));
13676     },
13677
13678     // private
13679     showPrevYear : function(){
13680         this.update(this.activeDate.add("y", -1));
13681     },
13682
13683     // private
13684     showNextYear : function(){
13685         this.update(this.activeDate.add("y", 1));
13686     },
13687
13688     
13689    // private
13690     update : function(date)
13691     {
13692         var vd = this.activeDate;
13693         this.activeDate = date;
13694 //        if(vd && this.el){
13695 //            var t = date.getTime();
13696 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13697 //                Roo.log('using add remove');
13698 //                
13699 //                this.fireEvent('monthchange', this, date);
13700 //                
13701 //                this.cells.removeClass("fc-state-highlight");
13702 //                this.cells.each(function(c){
13703 //                   if(c.dateValue == t){
13704 //                       c.addClass("fc-state-highlight");
13705 //                       setTimeout(function(){
13706 //                            try{c.dom.firstChild.focus();}catch(e){}
13707 //                       }, 50);
13708 //                       return false;
13709 //                   }
13710 //                   return true;
13711 //                });
13712 //                return;
13713 //            }
13714 //        }
13715         
13716         var days = date.getDaysInMonth();
13717         
13718         var firstOfMonth = date.getFirstDateOfMonth();
13719         var startingPos = firstOfMonth.getDay()-this.startDay;
13720         
13721         if(startingPos < this.startDay){
13722             startingPos += 7;
13723         }
13724         
13725         var pm = date.add(Date.MONTH, -1);
13726         var prevStart = pm.getDaysInMonth()-startingPos;
13727 //        
13728         this.cells = this.el.select('.fc-day',true);
13729         this.textNodes = this.el.query('.fc-day-number');
13730         this.cells.addClassOnOver('fc-state-hover');
13731         
13732         var cells = this.cells.elements;
13733         var textEls = this.textNodes;
13734         
13735         Roo.each(cells, function(cell){
13736             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13737         });
13738         
13739         days += startingPos;
13740
13741         // convert everything to numbers so it's fast
13742         var day = 86400000;
13743         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13744         //Roo.log(d);
13745         //Roo.log(pm);
13746         //Roo.log(prevStart);
13747         
13748         var today = new Date().clearTime().getTime();
13749         var sel = date.clearTime().getTime();
13750         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13751         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13752         var ddMatch = this.disabledDatesRE;
13753         var ddText = this.disabledDatesText;
13754         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13755         var ddaysText = this.disabledDaysText;
13756         var format = this.format;
13757         
13758         var setCellClass = function(cal, cell){
13759             cell.row = 0;
13760             cell.events = [];
13761             cell.more = [];
13762             //Roo.log('set Cell Class');
13763             cell.title = "";
13764             var t = d.getTime();
13765             
13766             //Roo.log(d);
13767             
13768             cell.dateValue = t;
13769             if(t == today){
13770                 cell.className += " fc-today";
13771                 cell.className += " fc-state-highlight";
13772                 cell.title = cal.todayText;
13773             }
13774             if(t == sel){
13775                 // disable highlight in other month..
13776                 //cell.className += " fc-state-highlight";
13777                 
13778             }
13779             // disabling
13780             if(t < min) {
13781                 cell.className = " fc-state-disabled";
13782                 cell.title = cal.minText;
13783                 return;
13784             }
13785             if(t > max) {
13786                 cell.className = " fc-state-disabled";
13787                 cell.title = cal.maxText;
13788                 return;
13789             }
13790             if(ddays){
13791                 if(ddays.indexOf(d.getDay()) != -1){
13792                     cell.title = ddaysText;
13793                     cell.className = " fc-state-disabled";
13794                 }
13795             }
13796             if(ddMatch && format){
13797                 var fvalue = d.dateFormat(format);
13798                 if(ddMatch.test(fvalue)){
13799                     cell.title = ddText.replace("%0", fvalue);
13800                     cell.className = " fc-state-disabled";
13801                 }
13802             }
13803             
13804             if (!cell.initialClassName) {
13805                 cell.initialClassName = cell.dom.className;
13806             }
13807             
13808             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13809         };
13810
13811         var i = 0;
13812         
13813         for(; i < startingPos; i++) {
13814             textEls[i].innerHTML = (++prevStart);
13815             d.setDate(d.getDate()+1);
13816             
13817             cells[i].className = "fc-past fc-other-month";
13818             setCellClass(this, cells[i]);
13819         }
13820         
13821         var intDay = 0;
13822         
13823         for(; i < days; i++){
13824             intDay = i - startingPos + 1;
13825             textEls[i].innerHTML = (intDay);
13826             d.setDate(d.getDate()+1);
13827             
13828             cells[i].className = ''; // "x-date-active";
13829             setCellClass(this, cells[i]);
13830         }
13831         var extraDays = 0;
13832         
13833         for(; i < 42; i++) {
13834             textEls[i].innerHTML = (++extraDays);
13835             d.setDate(d.getDate()+1);
13836             
13837             cells[i].className = "fc-future fc-other-month";
13838             setCellClass(this, cells[i]);
13839         }
13840         
13841         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13842         
13843         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13844         
13845         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13846         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13847         
13848         if(totalRows != 6){
13849             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13850             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13851         }
13852         
13853         this.fireEvent('monthchange', this, date);
13854         
13855         
13856         /*
13857         if(!this.internalRender){
13858             var main = this.el.dom.firstChild;
13859             var w = main.offsetWidth;
13860             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13861             Roo.fly(main).setWidth(w);
13862             this.internalRender = true;
13863             // opera does not respect the auto grow header center column
13864             // then, after it gets a width opera refuses to recalculate
13865             // without a second pass
13866             if(Roo.isOpera && !this.secondPass){
13867                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13868                 this.secondPass = true;
13869                 this.update.defer(10, this, [date]);
13870             }
13871         }
13872         */
13873         
13874     },
13875     
13876     findCell : function(dt) {
13877         dt = dt.clearTime().getTime();
13878         var ret = false;
13879         this.cells.each(function(c){
13880             //Roo.log("check " +c.dateValue + '?=' + dt);
13881             if(c.dateValue == dt){
13882                 ret = c;
13883                 return false;
13884             }
13885             return true;
13886         });
13887         
13888         return ret;
13889     },
13890     
13891     findCells : function(ev) {
13892         var s = ev.start.clone().clearTime().getTime();
13893        // Roo.log(s);
13894         var e= ev.end.clone().clearTime().getTime();
13895        // Roo.log(e);
13896         var ret = [];
13897         this.cells.each(function(c){
13898              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13899             
13900             if(c.dateValue > e){
13901                 return ;
13902             }
13903             if(c.dateValue < s){
13904                 return ;
13905             }
13906             ret.push(c);
13907         });
13908         
13909         return ret;    
13910     },
13911     
13912 //    findBestRow: function(cells)
13913 //    {
13914 //        var ret = 0;
13915 //        
13916 //        for (var i =0 ; i < cells.length;i++) {
13917 //            ret  = Math.max(cells[i].rows || 0,ret);
13918 //        }
13919 //        return ret;
13920 //        
13921 //    },
13922     
13923     
13924     addItem : function(ev)
13925     {
13926         // look for vertical location slot in
13927         var cells = this.findCells(ev);
13928         
13929 //        ev.row = this.findBestRow(cells);
13930         
13931         // work out the location.
13932         
13933         var crow = false;
13934         var rows = [];
13935         for(var i =0; i < cells.length; i++) {
13936             
13937             cells[i].row = cells[0].row;
13938             
13939             if(i == 0){
13940                 cells[i].row = cells[i].row + 1;
13941             }
13942             
13943             if (!crow) {
13944                 crow = {
13945                     start : cells[i],
13946                     end :  cells[i]
13947                 };
13948                 continue;
13949             }
13950             if (crow.start.getY() == cells[i].getY()) {
13951                 // on same row.
13952                 crow.end = cells[i];
13953                 continue;
13954             }
13955             // different row.
13956             rows.push(crow);
13957             crow = {
13958                 start: cells[i],
13959                 end : cells[i]
13960             };
13961             
13962         }
13963         
13964         rows.push(crow);
13965         ev.els = [];
13966         ev.rows = rows;
13967         ev.cells = cells;
13968         
13969         cells[0].events.push(ev);
13970         
13971         this.calevents.push(ev);
13972     },
13973     
13974     clearEvents: function() {
13975         
13976         if(!this.calevents){
13977             return;
13978         }
13979         
13980         Roo.each(this.cells.elements, function(c){
13981             c.row = 0;
13982             c.events = [];
13983             c.more = [];
13984         });
13985         
13986         Roo.each(this.calevents, function(e) {
13987             Roo.each(e.els, function(el) {
13988                 el.un('mouseenter' ,this.onEventEnter, this);
13989                 el.un('mouseleave' ,this.onEventLeave, this);
13990                 el.remove();
13991             },this);
13992         },this);
13993         
13994         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13995             e.remove();
13996         });
13997         
13998     },
13999     
14000     renderEvents: function()
14001     {   
14002         var _this = this;
14003         
14004         this.cells.each(function(c) {
14005             
14006             if(c.row < 5){
14007                 return;
14008             }
14009             
14010             var ev = c.events;
14011             
14012             var r = 4;
14013             if(c.row != c.events.length){
14014                 r = 4 - (4 - (c.row - c.events.length));
14015             }
14016             
14017             c.events = ev.slice(0, r);
14018             c.more = ev.slice(r);
14019             
14020             if(c.more.length && c.more.length == 1){
14021                 c.events.push(c.more.pop());
14022             }
14023             
14024             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14025             
14026         });
14027             
14028         this.cells.each(function(c) {
14029             
14030             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14031             
14032             
14033             for (var e = 0; e < c.events.length; e++){
14034                 var ev = c.events[e];
14035                 var rows = ev.rows;
14036                 
14037                 for(var i = 0; i < rows.length; i++) {
14038                 
14039                     // how many rows should it span..
14040
14041                     var  cfg = {
14042                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14043                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14044
14045                         unselectable : "on",
14046                         cn : [
14047                             {
14048                                 cls: 'fc-event-inner',
14049                                 cn : [
14050     //                                {
14051     //                                  tag:'span',
14052     //                                  cls: 'fc-event-time',
14053     //                                  html : cells.length > 1 ? '' : ev.time
14054     //                                },
14055                                     {
14056                                       tag:'span',
14057                                       cls: 'fc-event-title',
14058                                       html : String.format('{0}', ev.title)
14059                                     }
14060
14061
14062                                 ]
14063                             },
14064                             {
14065                                 cls: 'ui-resizable-handle ui-resizable-e',
14066                                 html : '&nbsp;&nbsp;&nbsp'
14067                             }
14068
14069                         ]
14070                     };
14071
14072                     if (i == 0) {
14073                         cfg.cls += ' fc-event-start';
14074                     }
14075                     if ((i+1) == rows.length) {
14076                         cfg.cls += ' fc-event-end';
14077                     }
14078
14079                     var ctr = _this.el.select('.fc-event-container',true).first();
14080                     var cg = ctr.createChild(cfg);
14081
14082                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
14083                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
14084
14085                     var r = (c.more.length) ? 1 : 0;
14086                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
14087                     cg.setWidth(ebox.right - sbox.x -2);
14088
14089                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
14090                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
14091                     cg.on('click', _this.onEventClick, _this, ev);
14092
14093                     ev.els.push(cg);
14094                     
14095                 }
14096                 
14097             }
14098             
14099             
14100             if(c.more.length){
14101                 var  cfg = {
14102                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
14103                     style : 'position: absolute',
14104                     unselectable : "on",
14105                     cn : [
14106                         {
14107                             cls: 'fc-event-inner',
14108                             cn : [
14109                                 {
14110                                   tag:'span',
14111                                   cls: 'fc-event-title',
14112                                   html : 'More'
14113                                 }
14114
14115
14116                             ]
14117                         },
14118                         {
14119                             cls: 'ui-resizable-handle ui-resizable-e',
14120                             html : '&nbsp;&nbsp;&nbsp'
14121                         }
14122
14123                     ]
14124                 };
14125
14126                 var ctr = _this.el.select('.fc-event-container',true).first();
14127                 var cg = ctr.createChild(cfg);
14128
14129                 var sbox = c.select('.fc-day-content',true).first().getBox();
14130                 var ebox = c.select('.fc-day-content',true).first().getBox();
14131                 //Roo.log(cg);
14132                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
14133                 cg.setWidth(ebox.right - sbox.x -2);
14134
14135                 cg.on('click', _this.onMoreEventClick, _this, c.more);
14136                 
14137             }
14138             
14139         });
14140         
14141         
14142         
14143     },
14144     
14145     onEventEnter: function (e, el,event,d) {
14146         this.fireEvent('evententer', this, el, event);
14147     },
14148     
14149     onEventLeave: function (e, el,event,d) {
14150         this.fireEvent('eventleave', this, el, event);
14151     },
14152     
14153     onEventClick: function (e, el,event,d) {
14154         this.fireEvent('eventclick', this, el, event);
14155     },
14156     
14157     onMonthChange: function () {
14158         this.store.load();
14159     },
14160     
14161     onMoreEventClick: function(e, el, more)
14162     {
14163         var _this = this;
14164         
14165         this.calpopover.placement = 'right';
14166         this.calpopover.setTitle('More');
14167         
14168         this.calpopover.setContent('');
14169         
14170         var ctr = this.calpopover.el.select('.popover-content', true).first();
14171         
14172         Roo.each(more, function(m){
14173             var cfg = {
14174                 cls : 'fc-event-hori fc-event-draggable',
14175                 html : m.title
14176             }
14177             var cg = ctr.createChild(cfg);
14178             
14179             cg.on('click', _this.onEventClick, _this, m);
14180         });
14181         
14182         this.calpopover.show(el);
14183         
14184         
14185     },
14186     
14187     onLoad: function () 
14188     {   
14189         this.calevents = [];
14190         var cal = this;
14191         
14192         if(this.store.getCount() > 0){
14193             this.store.data.each(function(d){
14194                cal.addItem({
14195                     id : d.data.id,
14196                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
14197                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
14198                     time : d.data.start_time,
14199                     title : d.data.title,
14200                     description : d.data.description,
14201                     venue : d.data.venue
14202                 });
14203             });
14204         }
14205         
14206         this.renderEvents();
14207         
14208         if(this.calevents.length && this.loadMask){
14209             this.maskEl.hide();
14210         }
14211     },
14212     
14213     onBeforeLoad: function()
14214     {
14215         this.clearEvents();
14216         if(this.loadMask){
14217             this.maskEl.show();
14218         }
14219     }
14220 });
14221
14222  
14223  /*
14224  * - LGPL
14225  *
14226  * element
14227  * 
14228  */
14229
14230 /**
14231  * @class Roo.bootstrap.Popover
14232  * @extends Roo.bootstrap.Component
14233  * Bootstrap Popover class
14234  * @cfg {String} html contents of the popover   (or false to use children..)
14235  * @cfg {String} title of popover (or false to hide)
14236  * @cfg {String} placement how it is placed
14237  * @cfg {String} trigger click || hover (or false to trigger manually)
14238  * @cfg {String} over what (parent or false to trigger manually.)
14239  * @cfg {Number} delay - delay before showing
14240  
14241  * @constructor
14242  * Create a new Popover
14243  * @param {Object} config The config object
14244  */
14245
14246 Roo.bootstrap.Popover = function(config){
14247     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
14248 };
14249
14250 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
14251     
14252     title: 'Fill in a title',
14253     html: false,
14254     
14255     placement : 'right',
14256     trigger : 'hover', // hover
14257     
14258     delay : 0,
14259     
14260     over: 'parent',
14261     
14262     can_build_overlaid : false,
14263     
14264     getChildContainer : function()
14265     {
14266         return this.el.select('.popover-content',true).first();
14267     },
14268     
14269     getAutoCreate : function(){
14270          Roo.log('make popover?');
14271         var cfg = {
14272            cls : 'popover roo-dynamic',
14273            style: 'display:block',
14274            cn : [
14275                 {
14276                     cls : 'arrow'
14277                 },
14278                 {
14279                     cls : 'popover-inner',
14280                     cn : [
14281                         {
14282                             tag: 'h3',
14283                             cls: 'popover-title',
14284                             html : this.title
14285                         },
14286                         {
14287                             cls : 'popover-content',
14288                             html : this.html
14289                         }
14290                     ]
14291                     
14292                 }
14293            ]
14294         };
14295         
14296         return cfg;
14297     },
14298     setTitle: function(str)
14299     {
14300         this.el.select('.popover-title',true).first().dom.innerHTML = str;
14301     },
14302     setContent: function(str)
14303     {
14304         this.el.select('.popover-content',true).first().dom.innerHTML = str;
14305     },
14306     // as it get's added to the bottom of the page.
14307     onRender : function(ct, position)
14308     {
14309         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
14310         if(!this.el){
14311             var cfg = Roo.apply({},  this.getAutoCreate());
14312             cfg.id = Roo.id();
14313             
14314             if (this.cls) {
14315                 cfg.cls += ' ' + this.cls;
14316             }
14317             if (this.style) {
14318                 cfg.style = this.style;
14319             }
14320             Roo.log("adding to ")
14321             this.el = Roo.get(document.body).createChild(cfg, position);
14322             Roo.log(this.el);
14323         }
14324         this.initEvents();
14325     },
14326     
14327     initEvents : function()
14328     {
14329         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
14330         this.el.enableDisplayMode('block');
14331         this.el.hide();
14332         if (this.over === false) {
14333             return; 
14334         }
14335         if (this.triggers === false) {
14336             return;
14337         }
14338         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14339         var triggers = this.trigger ? this.trigger.split(' ') : [];
14340         Roo.each(triggers, function(trigger) {
14341         
14342             if (trigger == 'click') {
14343                 on_el.on('click', this.toggle, this);
14344             } else if (trigger != 'manual') {
14345                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
14346                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
14347       
14348                 on_el.on(eventIn  ,this.enter, this);
14349                 on_el.on(eventOut, this.leave, this);
14350             }
14351         }, this);
14352         
14353     },
14354     
14355     
14356     // private
14357     timeout : null,
14358     hoverState : null,
14359     
14360     toggle : function () {
14361         this.hoverState == 'in' ? this.leave() : this.enter();
14362     },
14363     
14364     enter : function () {
14365        
14366     
14367         clearTimeout(this.timeout);
14368     
14369         this.hoverState = 'in';
14370     
14371         if (!this.delay || !this.delay.show) {
14372             this.show();
14373             return;
14374         }
14375         var _t = this;
14376         this.timeout = setTimeout(function () {
14377             if (_t.hoverState == 'in') {
14378                 _t.show();
14379             }
14380         }, this.delay.show)
14381     },
14382     leave : function() {
14383         clearTimeout(this.timeout);
14384     
14385         this.hoverState = 'out';
14386     
14387         if (!this.delay || !this.delay.hide) {
14388             this.hide();
14389             return;
14390         }
14391         var _t = this;
14392         this.timeout = setTimeout(function () {
14393             if (_t.hoverState == 'out') {
14394                 _t.hide();
14395             }
14396         }, this.delay.hide)
14397     },
14398     
14399     show : function (on_el)
14400     {
14401         if (!on_el) {
14402             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14403         }
14404         // set content.
14405         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
14406         if (this.html !== false) {
14407             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
14408         }
14409         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
14410         if (!this.title.length) {
14411             this.el.select('.popover-title',true).hide();
14412         }
14413         
14414         var placement = typeof this.placement == 'function' ?
14415             this.placement.call(this, this.el, on_el) :
14416             this.placement;
14417             
14418         var autoToken = /\s?auto?\s?/i;
14419         var autoPlace = autoToken.test(placement);
14420         if (autoPlace) {
14421             placement = placement.replace(autoToken, '') || 'top';
14422         }
14423         
14424         //this.el.detach()
14425         //this.el.setXY([0,0]);
14426         this.el.show();
14427         this.el.dom.style.display='block';
14428         this.el.addClass(placement);
14429         
14430         //this.el.appendTo(on_el);
14431         
14432         var p = this.getPosition();
14433         var box = this.el.getBox();
14434         
14435         if (autoPlace) {
14436             // fixme..
14437         }
14438         var align = Roo.bootstrap.Popover.alignment[placement];
14439         this.el.alignTo(on_el, align[0],align[1]);
14440         //var arrow = this.el.select('.arrow',true).first();
14441         //arrow.set(align[2], 
14442         
14443         this.el.addClass('in');
14444         this.hoverState = null;
14445         
14446         if (this.el.hasClass('fade')) {
14447             // fade it?
14448         }
14449         
14450     },
14451     hide : function()
14452     {
14453         this.el.setXY([0,0]);
14454         this.el.removeClass('in');
14455         this.el.hide();
14456         
14457     }
14458     
14459 });
14460
14461 Roo.bootstrap.Popover.alignment = {
14462     'left' : ['r-l', [-10,0], 'right'],
14463     'right' : ['l-r', [10,0], 'left'],
14464     'bottom' : ['t-b', [0,10], 'top'],
14465     'top' : [ 'b-t', [0,-10], 'bottom']
14466 };
14467
14468  /*
14469  * - LGPL
14470  *
14471  * Progress
14472  * 
14473  */
14474
14475 /**
14476  * @class Roo.bootstrap.Progress
14477  * @extends Roo.bootstrap.Component
14478  * Bootstrap Progress class
14479  * @cfg {Boolean} striped striped of the progress bar
14480  * @cfg {Boolean} active animated of the progress bar
14481  * 
14482  * 
14483  * @constructor
14484  * Create a new Progress
14485  * @param {Object} config The config object
14486  */
14487
14488 Roo.bootstrap.Progress = function(config){
14489     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14490 };
14491
14492 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14493     
14494     striped : false,
14495     active: false,
14496     
14497     getAutoCreate : function(){
14498         var cfg = {
14499             tag: 'div',
14500             cls: 'progress'
14501         };
14502         
14503         
14504         if(this.striped){
14505             cfg.cls += ' progress-striped';
14506         }
14507       
14508         if(this.active){
14509             cfg.cls += ' active';
14510         }
14511         
14512         
14513         return cfg;
14514     }
14515    
14516 });
14517
14518  
14519
14520  /*
14521  * - LGPL
14522  *
14523  * ProgressBar
14524  * 
14525  */
14526
14527 /**
14528  * @class Roo.bootstrap.ProgressBar
14529  * @extends Roo.bootstrap.Component
14530  * Bootstrap ProgressBar class
14531  * @cfg {Number} aria_valuenow aria-value now
14532  * @cfg {Number} aria_valuemin aria-value min
14533  * @cfg {Number} aria_valuemax aria-value max
14534  * @cfg {String} label label for the progress bar
14535  * @cfg {String} panel (success | info | warning | danger )
14536  * @cfg {String} role role of the progress bar
14537  * @cfg {String} sr_only text
14538  * 
14539  * 
14540  * @constructor
14541  * Create a new ProgressBar
14542  * @param {Object} config The config object
14543  */
14544
14545 Roo.bootstrap.ProgressBar = function(config){
14546     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14547 };
14548
14549 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14550     
14551     aria_valuenow : 0,
14552     aria_valuemin : 0,
14553     aria_valuemax : 100,
14554     label : false,
14555     panel : false,
14556     role : false,
14557     sr_only: false,
14558     
14559     getAutoCreate : function()
14560     {
14561         
14562         var cfg = {
14563             tag: 'div',
14564             cls: 'progress-bar',
14565             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14566         };
14567         
14568         if(this.sr_only){
14569             cfg.cn = {
14570                 tag: 'span',
14571                 cls: 'sr-only',
14572                 html: this.sr_only
14573             }
14574         }
14575         
14576         if(this.role){
14577             cfg.role = this.role;
14578         }
14579         
14580         if(this.aria_valuenow){
14581             cfg['aria-valuenow'] = this.aria_valuenow;
14582         }
14583         
14584         if(this.aria_valuemin){
14585             cfg['aria-valuemin'] = this.aria_valuemin;
14586         }
14587         
14588         if(this.aria_valuemax){
14589             cfg['aria-valuemax'] = this.aria_valuemax;
14590         }
14591         
14592         if(this.label && !this.sr_only){
14593             cfg.html = this.label;
14594         }
14595         
14596         if(this.panel){
14597             cfg.cls += ' progress-bar-' + this.panel;
14598         }
14599         
14600         return cfg;
14601     },
14602     
14603     update : function(aria_valuenow)
14604     {
14605         this.aria_valuenow = aria_valuenow;
14606         
14607         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14608     }
14609    
14610 });
14611
14612  
14613
14614  /*
14615  * - LGPL
14616  *
14617  * column
14618  * 
14619  */
14620
14621 /**
14622  * @class Roo.bootstrap.TabGroup
14623  * @extends Roo.bootstrap.Column
14624  * Bootstrap Column class
14625  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14626  * @cfg {Boolean} carousel true to make the group behave like a carousel
14627  * @cfg {Number} bullets show the panel pointer.. default 0
14628  * @cfg {Boolena} autoslide (true|false) auto slide .. default false
14629  * @cfg {Number} timer auto slide timer .. default 0 millisecond
14630  * 
14631  * @constructor
14632  * Create a new TabGroup
14633  * @param {Object} config The config object
14634  */
14635
14636 Roo.bootstrap.TabGroup = function(config){
14637     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14638     if (!this.navId) {
14639         this.navId = Roo.id();
14640     }
14641     this.tabs = [];
14642     Roo.bootstrap.TabGroup.register(this);
14643     
14644 };
14645
14646 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14647     
14648     carousel : false,
14649     transition : false,
14650     bullets : 0,
14651     timer : 0,
14652     autoslide : false,
14653     slideFn : false,
14654     
14655     getAutoCreate : function()
14656     {
14657         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14658         
14659         cfg.cls += ' tab-content';
14660         
14661         Roo.log('get auto create...............');
14662         
14663         if (this.carousel) {
14664             cfg.cls += ' carousel slide';
14665             
14666             cfg.cn = [{
14667                cls : 'carousel-inner'
14668             }];
14669         
14670             if(this.bullets > 0){
14671                 
14672                 var bullets = {
14673                     cls : 'carousel-bullets hidden-xs',
14674                     cn : []
14675                 };
14676                 
14677                 if(this.bullets_cls){
14678                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
14679                 }
14680                 
14681                 for (var i = 0; i < this.bullets; i++){
14682                     bullets.cn.push({
14683                         cls : 'bullet bullet-' + i
14684                     });
14685                 }
14686                 
14687                 bullets.cn.push({
14688                     cls : 'clear'
14689                 });
14690                 
14691                 cfg.cn[0].cn = bullets;
14692             }
14693         }
14694         
14695         return cfg;
14696     },
14697     
14698     initEvents:  function()
14699     {
14700         Roo.log('-------- init events on tab group ---------');
14701         
14702         var _this = this;
14703         
14704         if(this.bullets > 0){
14705             
14706             for (var i = 0; i < this.bullets; i++){
14707                 var bullet = this.el.select('.bullet-' + i, true).first();
14708                 
14709                 if(!bullet){
14710                     continue;
14711                 }
14712                 
14713                 bullet.on('click', (function(e, el, o, ii, t){
14714                     
14715                     e.preventDefault();
14716                     
14717                     _this.showPanel(ii);
14718                     
14719                     if(_this.autoslide && _this.slideFn){
14720                         clearInterval(_this.slideFn);
14721                         _this.slideFn = window.setInterval(function() {
14722                             _this.showPanelNext();
14723                         }, _this.timer);
14724                     }
14725                     
14726                 }).createDelegate(this, [i, bullet], true));
14727             }
14728         }
14729         
14730         if(this.autoslide){
14731             this.slideFn = window.setInterval(function() {
14732                 _this.showPanelNext();
14733             }, this.timer);
14734         }
14735     },
14736     
14737     getChildContainer : function()
14738     {
14739         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14740     },
14741     
14742     /**
14743     * register a Navigation item
14744     * @param {Roo.bootstrap.NavItem} the navitem to add
14745     */
14746     register : function(item)
14747     {
14748         this.tabs.push( item);
14749         item.navId = this.navId; // not really needed..
14750     
14751     },
14752     
14753     getActivePanel : function()
14754     {
14755         var r = false;
14756         Roo.each(this.tabs, function(t) {
14757             if (t.active) {
14758                 r = t;
14759                 return false;
14760             }
14761             return null;
14762         });
14763         return r;
14764         
14765     },
14766     getPanelByName : function(n)
14767     {
14768         var r = false;
14769         Roo.each(this.tabs, function(t) {
14770             if (t.tabId == n) {
14771                 r = t;
14772                 return false;
14773             }
14774             return null;
14775         });
14776         return r;
14777     },
14778     indexOfPanel : function(p)
14779     {
14780         var r = false;
14781         Roo.each(this.tabs, function(t,i) {
14782             if (t.tabId == p.tabId) {
14783                 r = i;
14784                 return false;
14785             }
14786             return null;
14787         });
14788         return r;
14789     },
14790     /**
14791      * show a specific panel
14792      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14793      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14794      */
14795     showPanel : function (pan)
14796     {
14797         if(this.transition){
14798             Roo.log("waiting for the transitionend");
14799             return;
14800         }
14801         
14802         if (typeof(pan) == 'number') {
14803             pan = this.tabs[pan];
14804         }
14805         if (typeof(pan) == 'string') {
14806             pan = this.getPanelByName(pan);
14807         }
14808         if (pan.tabId == this.getActivePanel().tabId) {
14809             return true;
14810         }
14811         var cur = this.getActivePanel();
14812         
14813         if (false === cur.fireEvent('beforedeactivate')) {
14814             return false;
14815         }
14816         
14817         if(this.bullets > 0){
14818             this.setActiveBullet(this.indexOfPanel(pan));
14819         }
14820         
14821         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
14822             
14823             this.transition = true;
14824             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14825             var lr = dir == 'next' ? 'left' : 'right';
14826             pan.el.addClass(dir); // or prev
14827             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14828             cur.el.addClass(lr); // or right
14829             pan.el.addClass(lr);
14830             
14831             var _this = this;
14832             cur.el.on('transitionend', function() {
14833                 Roo.log("trans end?");
14834                 
14835                 pan.el.removeClass([lr,dir]);
14836                 pan.setActive(true);
14837                 
14838                 cur.el.removeClass([lr]);
14839                 cur.setActive(false);
14840                 
14841                 _this.transition = false;
14842                 
14843             }, this, { single:  true } );
14844             
14845             return true;
14846         }
14847         
14848         cur.setActive(false);
14849         pan.setActive(true);
14850         
14851         return true;
14852         
14853     },
14854     showPanelNext : function()
14855     {
14856         var i = this.indexOfPanel(this.getActivePanel());
14857         
14858         if (i >= this.tabs.length - 1 && !this.autoslide) {
14859             return;
14860         }
14861         
14862         if (i >= this.tabs.length - 1 && this.autoslide) {
14863             i = -1;
14864         }
14865         
14866         this.showPanel(this.tabs[i+1]);
14867     },
14868     
14869     showPanelPrev : function()
14870     {
14871         var i = this.indexOfPanel(this.getActivePanel());
14872         
14873         if (i  < 1 && !this.autoslide) {
14874             return;
14875         }
14876         
14877         if (i < 1 && this.autoslide) {
14878             i = this.tabs.length;
14879         }
14880         
14881         this.showPanel(this.tabs[i-1]);
14882     },
14883     
14884     setActiveBullet : function(i)
14885     {
14886         Roo.each(this.el.select('.bullet', true).elements, function(el){
14887             el.removeClass('selected');
14888         });
14889
14890         var bullet = this.el.select('.bullet-' + i, true).first();
14891         
14892         if(!bullet){
14893             return;
14894         }
14895         
14896         bullet.addClass('selected');
14897     }
14898     
14899     
14900   
14901 });
14902
14903  
14904
14905  
14906  
14907 Roo.apply(Roo.bootstrap.TabGroup, {
14908     
14909     groups: {},
14910      /**
14911     * register a Navigation Group
14912     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14913     */
14914     register : function(navgrp)
14915     {
14916         this.groups[navgrp.navId] = navgrp;
14917         
14918     },
14919     /**
14920     * fetch a Navigation Group based on the navigation ID
14921     * if one does not exist , it will get created.
14922     * @param {string} the navgroup to add
14923     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14924     */
14925     get: function(navId) {
14926         if (typeof(this.groups[navId]) == 'undefined') {
14927             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14928         }
14929         return this.groups[navId] ;
14930     }
14931     
14932     
14933     
14934 });
14935
14936  /*
14937  * - LGPL
14938  *
14939  * TabPanel
14940  * 
14941  */
14942
14943 /**
14944  * @class Roo.bootstrap.TabPanel
14945  * @extends Roo.bootstrap.Component
14946  * Bootstrap TabPanel class
14947  * @cfg {Boolean} active panel active
14948  * @cfg {String} html panel content
14949  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14950  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14951  * 
14952  * 
14953  * @constructor
14954  * Create a new TabPanel
14955  * @param {Object} config The config object
14956  */
14957
14958 Roo.bootstrap.TabPanel = function(config){
14959     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14960     this.addEvents({
14961         /**
14962              * @event changed
14963              * Fires when the active status changes
14964              * @param {Roo.bootstrap.TabPanel} this
14965              * @param {Boolean} state the new state
14966             
14967          */
14968         'changed': true,
14969         /**
14970              * @event beforedeactivate
14971              * Fires before a tab is de-activated - can be used to do validation on a form.
14972              * @param {Roo.bootstrap.TabPanel} this
14973              * @return {Boolean} false if there is an error
14974             
14975          */
14976         'beforedeactivate': true
14977      });
14978     
14979     this.tabId = this.tabId || Roo.id();
14980   
14981 };
14982
14983 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14984     
14985     active: false,
14986     html: false,
14987     tabId: false,
14988     navId : false,
14989     
14990     getAutoCreate : function(){
14991         var cfg = {
14992             tag: 'div',
14993             // item is needed for carousel - not sure if it has any effect otherwise
14994             cls: 'tab-pane item',
14995             html: this.html || ''
14996         };
14997         
14998         if(this.active){
14999             cfg.cls += ' active';
15000         }
15001         
15002         if(this.tabId){
15003             cfg.tabId = this.tabId;
15004         }
15005         
15006         
15007         return cfg;
15008     },
15009     
15010     initEvents:  function()
15011     {
15012         Roo.log('-------- init events on tab panel ---------');
15013         
15014         var p = this.parent();
15015         this.navId = this.navId || p.navId;
15016         
15017         if (typeof(this.navId) != 'undefined') {
15018             // not really needed.. but just in case.. parent should be a NavGroup.
15019             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15020             Roo.log(['register', tg, this]);
15021             tg.register(this);
15022             
15023             var i = tg.tabs.length - 1;
15024             
15025             if(this.active && tg.bullets > 0 && i < tg.bullets){
15026                 tg.setActiveBullet(i);
15027             }
15028         }
15029         
15030     },
15031     
15032     
15033     onRender : function(ct, position)
15034     {
15035        // Roo.log("Call onRender: " + this.xtype);
15036         
15037         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
15038         
15039         
15040         
15041         
15042         
15043     },
15044     
15045     setActive: function(state)
15046     {
15047         Roo.log("panel - set active " + this.tabId + "=" + state);
15048         
15049         this.active = state;
15050         if (!state) {
15051             this.el.removeClass('active');
15052             
15053         } else  if (!this.el.hasClass('active')) {
15054             this.el.addClass('active');
15055         }
15056         
15057         this.fireEvent('changed', this, state);
15058     }
15059     
15060     
15061 });
15062  
15063
15064  
15065
15066  /*
15067  * - LGPL
15068  *
15069  * DateField
15070  * 
15071  */
15072
15073 /**
15074  * @class Roo.bootstrap.DateField
15075  * @extends Roo.bootstrap.Input
15076  * Bootstrap DateField class
15077  * @cfg {Number} weekStart default 0
15078  * @cfg {String} viewMode default empty, (months|years)
15079  * @cfg {String} minViewMode default empty, (months|years)
15080  * @cfg {Number} startDate default -Infinity
15081  * @cfg {Number} endDate default Infinity
15082  * @cfg {Boolean} todayHighlight default false
15083  * @cfg {Boolean} todayBtn default false
15084  * @cfg {Boolean} calendarWeeks default false
15085  * @cfg {Object} daysOfWeekDisabled default empty
15086  * @cfg {Boolean} singleMode default false (true | false)
15087  * 
15088  * @cfg {Boolean} keyboardNavigation default true
15089  * @cfg {String} language default en
15090  * 
15091  * @constructor
15092  * Create a new DateField
15093  * @param {Object} config The config object
15094  */
15095
15096 Roo.bootstrap.DateField = function(config){
15097     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
15098      this.addEvents({
15099             /**
15100              * @event show
15101              * Fires when this field show.
15102              * @param {Roo.bootstrap.DateField} this
15103              * @param {Mixed} date The date value
15104              */
15105             show : true,
15106             /**
15107              * @event show
15108              * Fires when this field hide.
15109              * @param {Roo.bootstrap.DateField} this
15110              * @param {Mixed} date The date value
15111              */
15112             hide : true,
15113             /**
15114              * @event select
15115              * Fires when select a date.
15116              * @param {Roo.bootstrap.DateField} this
15117              * @param {Mixed} date The date value
15118              */
15119             select : true
15120         });
15121 };
15122
15123 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
15124     
15125     /**
15126      * @cfg {String} format
15127      * The default date format string which can be overriden for localization support.  The format must be
15128      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
15129      */
15130     format : "m/d/y",
15131     /**
15132      * @cfg {String} altFormats
15133      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
15134      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
15135      */
15136     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
15137     
15138     weekStart : 0,
15139     
15140     viewMode : '',
15141     
15142     minViewMode : '',
15143     
15144     todayHighlight : false,
15145     
15146     todayBtn: false,
15147     
15148     language: 'en',
15149     
15150     keyboardNavigation: true,
15151     
15152     calendarWeeks: false,
15153     
15154     startDate: -Infinity,
15155     
15156     endDate: Infinity,
15157     
15158     daysOfWeekDisabled: [],
15159     
15160     _events: [],
15161     
15162     singleMode : false,
15163     
15164     UTCDate: function()
15165     {
15166         return new Date(Date.UTC.apply(Date, arguments));
15167     },
15168     
15169     UTCToday: function()
15170     {
15171         var today = new Date();
15172         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
15173     },
15174     
15175     getDate: function() {
15176             var d = this.getUTCDate();
15177             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
15178     },
15179     
15180     getUTCDate: function() {
15181             return this.date;
15182     },
15183     
15184     setDate: function(d) {
15185             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
15186     },
15187     
15188     setUTCDate: function(d) {
15189             this.date = d;
15190             this.setValue(this.formatDate(this.date));
15191     },
15192         
15193     onRender: function(ct, position)
15194     {
15195         
15196         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
15197         
15198         this.language = this.language || 'en';
15199         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
15200         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
15201         
15202         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
15203         this.format = this.format || 'm/d/y';
15204         this.isInline = false;
15205         this.isInput = true;
15206         this.component = this.el.select('.add-on', true).first() || false;
15207         this.component = (this.component && this.component.length === 0) ? false : this.component;
15208         this.hasInput = this.component && this.inputEL().length;
15209         
15210         if (typeof(this.minViewMode === 'string')) {
15211             switch (this.minViewMode) {
15212                 case 'months':
15213                     this.minViewMode = 1;
15214                     break;
15215                 case 'years':
15216                     this.minViewMode = 2;
15217                     break;
15218                 default:
15219                     this.minViewMode = 0;
15220                     break;
15221             }
15222         }
15223         
15224         if (typeof(this.viewMode === 'string')) {
15225             switch (this.viewMode) {
15226                 case 'months':
15227                     this.viewMode = 1;
15228                     break;
15229                 case 'years':
15230                     this.viewMode = 2;
15231                     break;
15232                 default:
15233                     this.viewMode = 0;
15234                     break;
15235             }
15236         }
15237                 
15238         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
15239         
15240 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
15241         
15242         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15243         
15244         this.picker().on('mousedown', this.onMousedown, this);
15245         this.picker().on('click', this.onClick, this);
15246         
15247         this.picker().addClass('datepicker-dropdown');
15248         
15249         this.startViewMode = this.viewMode;
15250         
15251         if(this.singleMode){
15252             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
15253                 v.setVisibilityMode(Roo.Element.DISPLAY)
15254                 v.hide();
15255             });
15256             
15257             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
15258                 v.setStyle('width', '189px');
15259             });
15260         }
15261         
15262         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
15263             if(!this.calendarWeeks){
15264                 v.remove();
15265                 return;
15266             }
15267             
15268             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15269             v.attr('colspan', function(i, val){
15270                 return parseInt(val) + 1;
15271             });
15272         })
15273                         
15274         
15275         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
15276         
15277         this.setStartDate(this.startDate);
15278         this.setEndDate(this.endDate);
15279         
15280         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
15281         
15282         this.fillDow();
15283         this.fillMonths();
15284         this.update();
15285         this.showMode();
15286         
15287         if(this.isInline) {
15288             this.show();
15289         }
15290     },
15291     
15292     picker : function()
15293     {
15294         return this.pickerEl;
15295 //        return this.el.select('.datepicker', true).first();
15296     },
15297     
15298     fillDow: function()
15299     {
15300         var dowCnt = this.weekStart;
15301         
15302         var dow = {
15303             tag: 'tr',
15304             cn: [
15305                 
15306             ]
15307         };
15308         
15309         if(this.calendarWeeks){
15310             dow.cn.push({
15311                 tag: 'th',
15312                 cls: 'cw',
15313                 html: '&nbsp;'
15314             })
15315         }
15316         
15317         while (dowCnt < this.weekStart + 7) {
15318             dow.cn.push({
15319                 tag: 'th',
15320                 cls: 'dow',
15321                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
15322             });
15323         }
15324         
15325         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
15326     },
15327     
15328     fillMonths: function()
15329     {    
15330         var i = 0;
15331         var months = this.picker().select('>.datepicker-months td', true).first();
15332         
15333         months.dom.innerHTML = '';
15334         
15335         while (i < 12) {
15336             var month = {
15337                 tag: 'span',
15338                 cls: 'month',
15339                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
15340             }
15341             
15342             months.createChild(month);
15343         }
15344         
15345     },
15346     
15347     update: function()
15348     {
15349         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;
15350         
15351         if (this.date < this.startDate) {
15352             this.viewDate = new Date(this.startDate);
15353         } else if (this.date > this.endDate) {
15354             this.viewDate = new Date(this.endDate);
15355         } else {
15356             this.viewDate = new Date(this.date);
15357         }
15358         
15359         this.fill();
15360     },
15361     
15362     fill: function() 
15363     {
15364         var d = new Date(this.viewDate),
15365                 year = d.getUTCFullYear(),
15366                 month = d.getUTCMonth(),
15367                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
15368                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
15369                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
15370                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
15371                 currentDate = this.date && this.date.valueOf(),
15372                 today = this.UTCToday();
15373         
15374         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
15375         
15376 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15377         
15378 //        this.picker.select('>tfoot th.today').
15379 //                                              .text(dates[this.language].today)
15380 //                                              .toggle(this.todayBtn !== false);
15381     
15382         this.updateNavArrows();
15383         this.fillMonths();
15384                                                 
15385         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
15386         
15387         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
15388          
15389         prevMonth.setUTCDate(day);
15390         
15391         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
15392         
15393         var nextMonth = new Date(prevMonth);
15394         
15395         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
15396         
15397         nextMonth = nextMonth.valueOf();
15398         
15399         var fillMonths = false;
15400         
15401         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
15402         
15403         while(prevMonth.valueOf() < nextMonth) {
15404             var clsName = '';
15405             
15406             if (prevMonth.getUTCDay() === this.weekStart) {
15407                 if(fillMonths){
15408                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
15409                 }
15410                     
15411                 fillMonths = {
15412                     tag: 'tr',
15413                     cn: []
15414                 };
15415                 
15416                 if(this.calendarWeeks){
15417                     // ISO 8601: First week contains first thursday.
15418                     // ISO also states week starts on Monday, but we can be more abstract here.
15419                     var
15420                     // Start of current week: based on weekstart/current date
15421                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
15422                     // Thursday of this week
15423                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
15424                     // First Thursday of year, year from thursday
15425                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
15426                     // Calendar week: ms between thursdays, div ms per day, div 7 days
15427                     calWeek =  (th - yth) / 864e5 / 7 + 1;
15428                     
15429                     fillMonths.cn.push({
15430                         tag: 'td',
15431                         cls: 'cw',
15432                         html: calWeek
15433                     });
15434                 }
15435             }
15436             
15437             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
15438                 clsName += ' old';
15439             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
15440                 clsName += ' new';
15441             }
15442             if (this.todayHighlight &&
15443                 prevMonth.getUTCFullYear() == today.getFullYear() &&
15444                 prevMonth.getUTCMonth() == today.getMonth() &&
15445                 prevMonth.getUTCDate() == today.getDate()) {
15446                 clsName += ' today';
15447             }
15448             
15449             if (currentDate && prevMonth.valueOf() === currentDate) {
15450                 clsName += ' active';
15451             }
15452             
15453             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
15454                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
15455                     clsName += ' disabled';
15456             }
15457             
15458             fillMonths.cn.push({
15459                 tag: 'td',
15460                 cls: 'day ' + clsName,
15461                 html: prevMonth.getDate()
15462             })
15463             
15464             prevMonth.setDate(prevMonth.getDate()+1);
15465         }
15466           
15467         var currentYear = this.date && this.date.getUTCFullYear();
15468         var currentMonth = this.date && this.date.getUTCMonth();
15469         
15470         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
15471         
15472         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
15473             v.removeClass('active');
15474             
15475             if(currentYear === year && k === currentMonth){
15476                 v.addClass('active');
15477             }
15478             
15479             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
15480                 v.addClass('disabled');
15481             }
15482             
15483         });
15484         
15485         
15486         year = parseInt(year/10, 10) * 10;
15487         
15488         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
15489         
15490         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
15491         
15492         year -= 1;
15493         for (var i = -1; i < 11; i++) {
15494             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
15495                 tag: 'span',
15496                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
15497                 html: year
15498             })
15499             
15500             year += 1;
15501         }
15502     },
15503     
15504     showMode: function(dir) 
15505     {
15506         if (dir) {
15507             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15508         }
15509         
15510         Roo.each(this.picker().select('>div',true).elements, function(v){
15511             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15512             v.hide();
15513         });
15514         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15515     },
15516     
15517     place: function()
15518     {
15519         if(this.isInline) return;
15520         
15521         this.picker().removeClass(['bottom', 'top']);
15522         
15523         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15524             /*
15525              * place to the top of element!
15526              *
15527              */
15528             
15529             this.picker().addClass('top');
15530             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15531             
15532             return;
15533         }
15534         
15535         this.picker().addClass('bottom');
15536         
15537         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15538     },
15539     
15540     parseDate : function(value)
15541     {
15542         if(!value || value instanceof Date){
15543             return value;
15544         }
15545         var v = Date.parseDate(value, this.format);
15546         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15547             v = Date.parseDate(value, 'Y-m-d');
15548         }
15549         if(!v && this.altFormats){
15550             if(!this.altFormatsArray){
15551                 this.altFormatsArray = this.altFormats.split("|");
15552             }
15553             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15554                 v = Date.parseDate(value, this.altFormatsArray[i]);
15555             }
15556         }
15557         return v;
15558     },
15559     
15560     formatDate : function(date, fmt)
15561     {   
15562         return (!date || !(date instanceof Date)) ?
15563         date : date.dateFormat(fmt || this.format);
15564     },
15565     
15566     onFocus : function()
15567     {
15568         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15569         this.show();
15570     },
15571     
15572     onBlur : function()
15573     {
15574         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15575         
15576         var d = this.inputEl().getValue();
15577         
15578         this.setValue(d);
15579                 
15580         this.hide();
15581     },
15582     
15583     show : function()
15584     {
15585         this.picker().show();
15586         this.update();
15587         this.place();
15588         
15589         this.fireEvent('show', this, this.date);
15590     },
15591     
15592     hide : function()
15593     {
15594         if(this.isInline) return;
15595         this.picker().hide();
15596         this.viewMode = this.startViewMode;
15597         this.showMode();
15598         
15599         this.fireEvent('hide', this, this.date);
15600         
15601     },
15602     
15603     onMousedown: function(e)
15604     {
15605         e.stopPropagation();
15606         e.preventDefault();
15607     },
15608     
15609     keyup: function(e)
15610     {
15611         Roo.bootstrap.DateField.superclass.keyup.call(this);
15612         this.update();
15613     },
15614
15615     setValue: function(v)
15616     {
15617         
15618         // v can be a string or a date..
15619         
15620         
15621         var d = new Date(this.parseDate(v) ).clearTime();
15622         
15623         if(isNaN(d.getTime())){
15624             this.date = this.viewDate = '';
15625             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15626             return;
15627         }
15628         
15629         v = this.formatDate(d);
15630         
15631         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15632         
15633         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15634      
15635         this.update();
15636
15637         this.fireEvent('select', this, this.date);
15638         
15639     },
15640     
15641     getValue: function()
15642     {
15643         return this.formatDate(this.date);
15644     },
15645     
15646     fireKey: function(e)
15647     {
15648         if (!this.picker().isVisible()){
15649             if (e.keyCode == 27) // allow escape to hide and re-show picker
15650                 this.show();
15651             return;
15652         }
15653         
15654         var dateChanged = false,
15655         dir, day, month,
15656         newDate, newViewDate;
15657         
15658         switch(e.keyCode){
15659             case 27: // escape
15660                 this.hide();
15661                 e.preventDefault();
15662                 break;
15663             case 37: // left
15664             case 39: // right
15665                 if (!this.keyboardNavigation) break;
15666                 dir = e.keyCode == 37 ? -1 : 1;
15667                 
15668                 if (e.ctrlKey){
15669                     newDate = this.moveYear(this.date, dir);
15670                     newViewDate = this.moveYear(this.viewDate, dir);
15671                 } else if (e.shiftKey){
15672                     newDate = this.moveMonth(this.date, dir);
15673                     newViewDate = this.moveMonth(this.viewDate, dir);
15674                 } else {
15675                     newDate = new Date(this.date);
15676                     newDate.setUTCDate(this.date.getUTCDate() + dir);
15677                     newViewDate = new Date(this.viewDate);
15678                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15679                 }
15680                 if (this.dateWithinRange(newDate)){
15681                     this.date = newDate;
15682                     this.viewDate = newViewDate;
15683                     this.setValue(this.formatDate(this.date));
15684 //                    this.update();
15685                     e.preventDefault();
15686                     dateChanged = true;
15687                 }
15688                 break;
15689             case 38: // up
15690             case 40: // down
15691                 if (!this.keyboardNavigation) break;
15692                 dir = e.keyCode == 38 ? -1 : 1;
15693                 if (e.ctrlKey){
15694                     newDate = this.moveYear(this.date, dir);
15695                     newViewDate = this.moveYear(this.viewDate, dir);
15696                 } else if (e.shiftKey){
15697                     newDate = this.moveMonth(this.date, dir);
15698                     newViewDate = this.moveMonth(this.viewDate, dir);
15699                 } else {
15700                     newDate = new Date(this.date);
15701                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15702                     newViewDate = new Date(this.viewDate);
15703                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15704                 }
15705                 if (this.dateWithinRange(newDate)){
15706                     this.date = newDate;
15707                     this.viewDate = newViewDate;
15708                     this.setValue(this.formatDate(this.date));
15709 //                    this.update();
15710                     e.preventDefault();
15711                     dateChanged = true;
15712                 }
15713                 break;
15714             case 13: // enter
15715                 this.setValue(this.formatDate(this.date));
15716                 this.hide();
15717                 e.preventDefault();
15718                 break;
15719             case 9: // tab
15720                 this.setValue(this.formatDate(this.date));
15721                 this.hide();
15722                 break;
15723             case 16: // shift
15724             case 17: // ctrl
15725             case 18: // alt
15726                 break;
15727             default :
15728                 this.hide();
15729                 
15730         }
15731     },
15732     
15733     
15734     onClick: function(e) 
15735     {
15736         e.stopPropagation();
15737         e.preventDefault();
15738         
15739         var target = e.getTarget();
15740         
15741         if(target.nodeName.toLowerCase() === 'i'){
15742             target = Roo.get(target).dom.parentNode;
15743         }
15744         
15745         var nodeName = target.nodeName;
15746         var className = target.className;
15747         var html = target.innerHTML;
15748         //Roo.log(nodeName);
15749         
15750         switch(nodeName.toLowerCase()) {
15751             case 'th':
15752                 switch(className) {
15753                     case 'switch':
15754                         this.showMode(1);
15755                         break;
15756                     case 'prev':
15757                     case 'next':
15758                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15759                         switch(this.viewMode){
15760                                 case 0:
15761                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15762                                         break;
15763                                 case 1:
15764                                 case 2:
15765                                         this.viewDate = this.moveYear(this.viewDate, dir);
15766                                         break;
15767                         }
15768                         this.fill();
15769                         break;
15770                     case 'today':
15771                         var date = new Date();
15772                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15773 //                        this.fill()
15774                         this.setValue(this.formatDate(this.date));
15775                         
15776                         this.hide();
15777                         break;
15778                 }
15779                 break;
15780             case 'span':
15781                 if (className.indexOf('disabled') < 0) {
15782                     this.viewDate.setUTCDate(1);
15783                     if (className.indexOf('month') > -1) {
15784                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15785                     } else {
15786                         var year = parseInt(html, 10) || 0;
15787                         this.viewDate.setUTCFullYear(year);
15788                         
15789                     }
15790                     
15791                     if(this.singleMode){
15792                         this.setValue(this.formatDate(this.viewDate));
15793                         this.hide();
15794                         return;
15795                     }
15796                     
15797                     this.showMode(-1);
15798                     this.fill();
15799                 }
15800                 break;
15801                 
15802             case 'td':
15803                 //Roo.log(className);
15804                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15805                     var day = parseInt(html, 10) || 1;
15806                     var year = this.viewDate.getUTCFullYear(),
15807                         month = this.viewDate.getUTCMonth();
15808
15809                     if (className.indexOf('old') > -1) {
15810                         if(month === 0 ){
15811                             month = 11;
15812                             year -= 1;
15813                         }else{
15814                             month -= 1;
15815                         }
15816                     } else if (className.indexOf('new') > -1) {
15817                         if (month == 11) {
15818                             month = 0;
15819                             year += 1;
15820                         } else {
15821                             month += 1;
15822                         }
15823                     }
15824                     //Roo.log([year,month,day]);
15825                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15826                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15827 //                    this.fill();
15828                     //Roo.log(this.formatDate(this.date));
15829                     this.setValue(this.formatDate(this.date));
15830                     this.hide();
15831                 }
15832                 break;
15833         }
15834     },
15835     
15836     setStartDate: function(startDate)
15837     {
15838         this.startDate = startDate || -Infinity;
15839         if (this.startDate !== -Infinity) {
15840             this.startDate = this.parseDate(this.startDate);
15841         }
15842         this.update();
15843         this.updateNavArrows();
15844     },
15845
15846     setEndDate: function(endDate)
15847     {
15848         this.endDate = endDate || Infinity;
15849         if (this.endDate !== Infinity) {
15850             this.endDate = this.parseDate(this.endDate);
15851         }
15852         this.update();
15853         this.updateNavArrows();
15854     },
15855     
15856     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15857     {
15858         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15859         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15860             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15861         }
15862         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15863             return parseInt(d, 10);
15864         });
15865         this.update();
15866         this.updateNavArrows();
15867     },
15868     
15869     updateNavArrows: function() 
15870     {
15871         if(this.singleMode){
15872             return;
15873         }
15874         
15875         var d = new Date(this.viewDate),
15876         year = d.getUTCFullYear(),
15877         month = d.getUTCMonth();
15878         
15879         Roo.each(this.picker().select('.prev', true).elements, function(v){
15880             v.show();
15881             switch (this.viewMode) {
15882                 case 0:
15883
15884                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15885                         v.hide();
15886                     }
15887                     break;
15888                 case 1:
15889                 case 2:
15890                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15891                         v.hide();
15892                     }
15893                     break;
15894             }
15895         });
15896         
15897         Roo.each(this.picker().select('.next', true).elements, function(v){
15898             v.show();
15899             switch (this.viewMode) {
15900                 case 0:
15901
15902                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15903                         v.hide();
15904                     }
15905                     break;
15906                 case 1:
15907                 case 2:
15908                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15909                         v.hide();
15910                     }
15911                     break;
15912             }
15913         })
15914     },
15915     
15916     moveMonth: function(date, dir)
15917     {
15918         if (!dir) return date;
15919         var new_date = new Date(date.valueOf()),
15920         day = new_date.getUTCDate(),
15921         month = new_date.getUTCMonth(),
15922         mag = Math.abs(dir),
15923         new_month, test;
15924         dir = dir > 0 ? 1 : -1;
15925         if (mag == 1){
15926             test = dir == -1
15927             // If going back one month, make sure month is not current month
15928             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15929             ? function(){
15930                 return new_date.getUTCMonth() == month;
15931             }
15932             // If going forward one month, make sure month is as expected
15933             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15934             : function(){
15935                 return new_date.getUTCMonth() != new_month;
15936             };
15937             new_month = month + dir;
15938             new_date.setUTCMonth(new_month);
15939             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15940             if (new_month < 0 || new_month > 11)
15941                 new_month = (new_month + 12) % 12;
15942         } else {
15943             // For magnitudes >1, move one month at a time...
15944             for (var i=0; i<mag; i++)
15945                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15946                 new_date = this.moveMonth(new_date, dir);
15947             // ...then reset the day, keeping it in the new month
15948             new_month = new_date.getUTCMonth();
15949             new_date.setUTCDate(day);
15950             test = function(){
15951                 return new_month != new_date.getUTCMonth();
15952             };
15953         }
15954         // Common date-resetting loop -- if date is beyond end of month, make it
15955         // end of month
15956         while (test()){
15957             new_date.setUTCDate(--day);
15958             new_date.setUTCMonth(new_month);
15959         }
15960         return new_date;
15961     },
15962
15963     moveYear: function(date, dir)
15964     {
15965         return this.moveMonth(date, dir*12);
15966     },
15967
15968     dateWithinRange: function(date)
15969     {
15970         return date >= this.startDate && date <= this.endDate;
15971     },
15972
15973     
15974     remove: function() 
15975     {
15976         this.picker().remove();
15977     }
15978    
15979 });
15980
15981 Roo.apply(Roo.bootstrap.DateField,  {
15982     
15983     head : {
15984         tag: 'thead',
15985         cn: [
15986         {
15987             tag: 'tr',
15988             cn: [
15989             {
15990                 tag: 'th',
15991                 cls: 'prev',
15992                 html: '<i class="fa fa-arrow-left"/>'
15993             },
15994             {
15995                 tag: 'th',
15996                 cls: 'switch',
15997                 colspan: '5'
15998             },
15999             {
16000                 tag: 'th',
16001                 cls: 'next',
16002                 html: '<i class="fa fa-arrow-right"/>'
16003             }
16004
16005             ]
16006         }
16007         ]
16008     },
16009     
16010     content : {
16011         tag: 'tbody',
16012         cn: [
16013         {
16014             tag: 'tr',
16015             cn: [
16016             {
16017                 tag: 'td',
16018                 colspan: '7'
16019             }
16020             ]
16021         }
16022         ]
16023     },
16024     
16025     footer : {
16026         tag: 'tfoot',
16027         cn: [
16028         {
16029             tag: 'tr',
16030             cn: [
16031             {
16032                 tag: 'th',
16033                 colspan: '7',
16034                 cls: 'today'
16035             }
16036                     
16037             ]
16038         }
16039         ]
16040     },
16041     
16042     dates:{
16043         en: {
16044             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
16045             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
16046             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
16047             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16048             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
16049             today: "Today"
16050         }
16051     },
16052     
16053     modes: [
16054     {
16055         clsName: 'days',
16056         navFnc: 'Month',
16057         navStep: 1
16058     },
16059     {
16060         clsName: 'months',
16061         navFnc: 'FullYear',
16062         navStep: 1
16063     },
16064     {
16065         clsName: 'years',
16066         navFnc: 'FullYear',
16067         navStep: 10
16068     }]
16069 });
16070
16071 Roo.apply(Roo.bootstrap.DateField,  {
16072   
16073     template : {
16074         tag: 'div',
16075         cls: 'datepicker dropdown-menu roo-dynamic',
16076         cn: [
16077         {
16078             tag: 'div',
16079             cls: 'datepicker-days',
16080             cn: [
16081             {
16082                 tag: 'table',
16083                 cls: 'table-condensed',
16084                 cn:[
16085                 Roo.bootstrap.DateField.head,
16086                 {
16087                     tag: 'tbody'
16088                 },
16089                 Roo.bootstrap.DateField.footer
16090                 ]
16091             }
16092             ]
16093         },
16094         {
16095             tag: 'div',
16096             cls: 'datepicker-months',
16097             cn: [
16098             {
16099                 tag: 'table',
16100                 cls: 'table-condensed',
16101                 cn:[
16102                 Roo.bootstrap.DateField.head,
16103                 Roo.bootstrap.DateField.content,
16104                 Roo.bootstrap.DateField.footer
16105                 ]
16106             }
16107             ]
16108         },
16109         {
16110             tag: 'div',
16111             cls: 'datepicker-years',
16112             cn: [
16113             {
16114                 tag: 'table',
16115                 cls: 'table-condensed',
16116                 cn:[
16117                 Roo.bootstrap.DateField.head,
16118                 Roo.bootstrap.DateField.content,
16119                 Roo.bootstrap.DateField.footer
16120                 ]
16121             }
16122             ]
16123         }
16124         ]
16125     }
16126 });
16127
16128  
16129
16130  /*
16131  * - LGPL
16132  *
16133  * TimeField
16134  * 
16135  */
16136
16137 /**
16138  * @class Roo.bootstrap.TimeField
16139  * @extends Roo.bootstrap.Input
16140  * Bootstrap DateField class
16141  * 
16142  * 
16143  * @constructor
16144  * Create a new TimeField
16145  * @param {Object} config The config object
16146  */
16147
16148 Roo.bootstrap.TimeField = function(config){
16149     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
16150     this.addEvents({
16151             /**
16152              * @event show
16153              * Fires when this field show.
16154              * @param {Roo.bootstrap.DateField} thisthis
16155              * @param {Mixed} date The date value
16156              */
16157             show : true,
16158             /**
16159              * @event show
16160              * Fires when this field hide.
16161              * @param {Roo.bootstrap.DateField} this
16162              * @param {Mixed} date The date value
16163              */
16164             hide : true,
16165             /**
16166              * @event select
16167              * Fires when select a date.
16168              * @param {Roo.bootstrap.DateField} this
16169              * @param {Mixed} date The date value
16170              */
16171             select : true
16172         });
16173 };
16174
16175 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
16176     
16177     /**
16178      * @cfg {String} format
16179      * The default time format string which can be overriden for localization support.  The format must be
16180      * valid according to {@link Date#parseDate} (defaults to 'H:i').
16181      */
16182     format : "H:i",
16183        
16184     onRender: function(ct, position)
16185     {
16186         
16187         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
16188                 
16189         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
16190         
16191         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16192         
16193         this.pop = this.picker().select('>.datepicker-time',true).first();
16194         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16195         
16196         this.picker().on('mousedown', this.onMousedown, this);
16197         this.picker().on('click', this.onClick, this);
16198         
16199         this.picker().addClass('datepicker-dropdown');
16200     
16201         this.fillTime();
16202         this.update();
16203             
16204         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
16205         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
16206         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
16207         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
16208         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
16209         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
16210
16211     },
16212     
16213     fireKey: function(e){
16214         if (!this.picker().isVisible()){
16215             if (e.keyCode == 27) { // allow escape to hide and re-show picker
16216                 this.show();
16217             }
16218             return;
16219         }
16220
16221         e.preventDefault();
16222         
16223         switch(e.keyCode){
16224             case 27: // escape
16225                 this.hide();
16226                 break;
16227             case 37: // left
16228             case 39: // right
16229                 this.onTogglePeriod();
16230                 break;
16231             case 38: // up
16232                 this.onIncrementMinutes();
16233                 break;
16234             case 40: // down
16235                 this.onDecrementMinutes();
16236                 break;
16237             case 13: // enter
16238             case 9: // tab
16239                 this.setTime();
16240                 break;
16241         }
16242     },
16243     
16244     onClick: function(e) {
16245         e.stopPropagation();
16246         e.preventDefault();
16247     },
16248     
16249     picker : function()
16250     {
16251         return this.el.select('.datepicker', true).first();
16252     },
16253     
16254     fillTime: function()
16255     {    
16256         var time = this.pop.select('tbody', true).first();
16257         
16258         time.dom.innerHTML = '';
16259         
16260         time.createChild({
16261             tag: 'tr',
16262             cn: [
16263                 {
16264                     tag: 'td',
16265                     cn: [
16266                         {
16267                             tag: 'a',
16268                             href: '#',
16269                             cls: 'btn',
16270                             cn: [
16271                                 {
16272                                     tag: 'span',
16273                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
16274                                 }
16275                             ]
16276                         } 
16277                     ]
16278                 },
16279                 {
16280                     tag: 'td',
16281                     cls: 'separator'
16282                 },
16283                 {
16284                     tag: 'td',
16285                     cn: [
16286                         {
16287                             tag: 'a',
16288                             href: '#',
16289                             cls: 'btn',
16290                             cn: [
16291                                 {
16292                                     tag: 'span',
16293                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
16294                                 }
16295                             ]
16296                         }
16297                     ]
16298                 },
16299                 {
16300                     tag: 'td',
16301                     cls: 'separator'
16302                 }
16303             ]
16304         });
16305         
16306         time.createChild({
16307             tag: 'tr',
16308             cn: [
16309                 {
16310                     tag: 'td',
16311                     cn: [
16312                         {
16313                             tag: 'span',
16314                             cls: 'timepicker-hour',
16315                             html: '00'
16316                         }  
16317                     ]
16318                 },
16319                 {
16320                     tag: 'td',
16321                     cls: 'separator',
16322                     html: ':'
16323                 },
16324                 {
16325                     tag: 'td',
16326                     cn: [
16327                         {
16328                             tag: 'span',
16329                             cls: 'timepicker-minute',
16330                             html: '00'
16331                         }  
16332                     ]
16333                 },
16334                 {
16335                     tag: 'td',
16336                     cls: 'separator'
16337                 },
16338                 {
16339                     tag: 'td',
16340                     cn: [
16341                         {
16342                             tag: 'button',
16343                             type: 'button',
16344                             cls: 'btn btn-primary period',
16345                             html: 'AM'
16346                             
16347                         }
16348                     ]
16349                 }
16350             ]
16351         });
16352         
16353         time.createChild({
16354             tag: 'tr',
16355             cn: [
16356                 {
16357                     tag: 'td',
16358                     cn: [
16359                         {
16360                             tag: 'a',
16361                             href: '#',
16362                             cls: 'btn',
16363                             cn: [
16364                                 {
16365                                     tag: 'span',
16366                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
16367                                 }
16368                             ]
16369                         }
16370                     ]
16371                 },
16372                 {
16373                     tag: 'td',
16374                     cls: 'separator'
16375                 },
16376                 {
16377                     tag: 'td',
16378                     cn: [
16379                         {
16380                             tag: 'a',
16381                             href: '#',
16382                             cls: 'btn',
16383                             cn: [
16384                                 {
16385                                     tag: 'span',
16386                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
16387                                 }
16388                             ]
16389                         }
16390                     ]
16391                 },
16392                 {
16393                     tag: 'td',
16394                     cls: 'separator'
16395                 }
16396             ]
16397         });
16398         
16399     },
16400     
16401     update: function()
16402     {
16403         
16404         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
16405         
16406         this.fill();
16407     },
16408     
16409     fill: function() 
16410     {
16411         var hours = this.time.getHours();
16412         var minutes = this.time.getMinutes();
16413         var period = 'AM';
16414         
16415         if(hours > 11){
16416             period = 'PM';
16417         }
16418         
16419         if(hours == 0){
16420             hours = 12;
16421         }
16422         
16423         
16424         if(hours > 12){
16425             hours = hours - 12;
16426         }
16427         
16428         if(hours < 10){
16429             hours = '0' + hours;
16430         }
16431         
16432         if(minutes < 10){
16433             minutes = '0' + minutes;
16434         }
16435         
16436         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
16437         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
16438         this.pop.select('button', true).first().dom.innerHTML = period;
16439         
16440     },
16441     
16442     place: function()
16443     {   
16444         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
16445         
16446         var cls = ['bottom'];
16447         
16448         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
16449             cls.pop();
16450             cls.push('top');
16451         }
16452         
16453         cls.push('right');
16454         
16455         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
16456             cls.pop();
16457             cls.push('left');
16458         }
16459         
16460         this.picker().addClass(cls.join('-'));
16461         
16462         var _this = this;
16463         
16464         Roo.each(cls, function(c){
16465             if(c == 'bottom'){
16466                 _this.picker().setTop(_this.inputEl().getHeight());
16467                 return;
16468             }
16469             if(c == 'top'){
16470                 _this.picker().setTop(0 - _this.picker().getHeight());
16471                 return;
16472             }
16473             
16474             if(c == 'left'){
16475                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
16476                 return;
16477             }
16478             if(c == 'right'){
16479                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
16480                 return;
16481             }
16482         });
16483         
16484     },
16485   
16486     onFocus : function()
16487     {
16488         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
16489         this.show();
16490     },
16491     
16492     onBlur : function()
16493     {
16494         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
16495         this.hide();
16496     },
16497     
16498     show : function()
16499     {
16500         this.picker().show();
16501         this.pop.show();
16502         this.update();
16503         this.place();
16504         
16505         this.fireEvent('show', this, this.date);
16506     },
16507     
16508     hide : function()
16509     {
16510         this.picker().hide();
16511         this.pop.hide();
16512         
16513         this.fireEvent('hide', this, this.date);
16514     },
16515     
16516     setTime : function()
16517     {
16518         this.hide();
16519         this.setValue(this.time.format(this.format));
16520         
16521         this.fireEvent('select', this, this.date);
16522         
16523         
16524     },
16525     
16526     onMousedown: function(e){
16527         e.stopPropagation();
16528         e.preventDefault();
16529     },
16530     
16531     onIncrementHours: function()
16532     {
16533         Roo.log('onIncrementHours');
16534         this.time = this.time.add(Date.HOUR, 1);
16535         this.update();
16536         
16537     },
16538     
16539     onDecrementHours: function()
16540     {
16541         Roo.log('onDecrementHours');
16542         this.time = this.time.add(Date.HOUR, -1);
16543         this.update();
16544     },
16545     
16546     onIncrementMinutes: function()
16547     {
16548         Roo.log('onIncrementMinutes');
16549         this.time = this.time.add(Date.MINUTE, 1);
16550         this.update();
16551     },
16552     
16553     onDecrementMinutes: function()
16554     {
16555         Roo.log('onDecrementMinutes');
16556         this.time = this.time.add(Date.MINUTE, -1);
16557         this.update();
16558     },
16559     
16560     onTogglePeriod: function()
16561     {
16562         Roo.log('onTogglePeriod');
16563         this.time = this.time.add(Date.HOUR, 12);
16564         this.update();
16565     }
16566     
16567    
16568 });
16569
16570 Roo.apply(Roo.bootstrap.TimeField,  {
16571     
16572     content : {
16573         tag: 'tbody',
16574         cn: [
16575             {
16576                 tag: 'tr',
16577                 cn: [
16578                 {
16579                     tag: 'td',
16580                     colspan: '7'
16581                 }
16582                 ]
16583             }
16584         ]
16585     },
16586     
16587     footer : {
16588         tag: 'tfoot',
16589         cn: [
16590             {
16591                 tag: 'tr',
16592                 cn: [
16593                 {
16594                     tag: 'th',
16595                     colspan: '7',
16596                     cls: '',
16597                     cn: [
16598                         {
16599                             tag: 'button',
16600                             cls: 'btn btn-info ok',
16601                             html: 'OK'
16602                         }
16603                     ]
16604                 }
16605
16606                 ]
16607             }
16608         ]
16609     }
16610 });
16611
16612 Roo.apply(Roo.bootstrap.TimeField,  {
16613   
16614     template : {
16615         tag: 'div',
16616         cls: 'datepicker dropdown-menu',
16617         cn: [
16618             {
16619                 tag: 'div',
16620                 cls: 'datepicker-time',
16621                 cn: [
16622                 {
16623                     tag: 'table',
16624                     cls: 'table-condensed',
16625                     cn:[
16626                     Roo.bootstrap.TimeField.content,
16627                     Roo.bootstrap.TimeField.footer
16628                     ]
16629                 }
16630                 ]
16631             }
16632         ]
16633     }
16634 });
16635
16636  
16637
16638  /*
16639  * - LGPL
16640  *
16641  * MonthField
16642  * 
16643  */
16644
16645 /**
16646  * @class Roo.bootstrap.MonthField
16647  * @extends Roo.bootstrap.Input
16648  * Bootstrap MonthField class
16649  * 
16650  * @cfg {String} language default en
16651  * 
16652  * @constructor
16653  * Create a new MonthField
16654  * @param {Object} config The config object
16655  */
16656
16657 Roo.bootstrap.MonthField = function(config){
16658     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
16659     
16660     this.addEvents({
16661         /**
16662          * @event show
16663          * Fires when this field show.
16664          * @param {Roo.bootstrap.MonthField} this
16665          * @param {Mixed} date The date value
16666          */
16667         show : true,
16668         /**
16669          * @event show
16670          * Fires when this field hide.
16671          * @param {Roo.bootstrap.MonthField} this
16672          * @param {Mixed} date The date value
16673          */
16674         hide : true,
16675         /**
16676          * @event select
16677          * Fires when select a date.
16678          * @param {Roo.bootstrap.MonthField} this
16679          * @param {String} oldvalue The old value
16680          * @param {String} newvalue The new value
16681          */
16682         select : true
16683     });
16684 };
16685
16686 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
16687     
16688     onRender: function(ct, position)
16689     {
16690         
16691         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
16692         
16693         this.language = this.language || 'en';
16694         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
16695         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
16696         
16697         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
16698         this.isInline = false;
16699         this.isInput = true;
16700         this.component = this.el.select('.add-on', true).first() || false;
16701         this.component = (this.component && this.component.length === 0) ? false : this.component;
16702         this.hasInput = this.component && this.inputEL().length;
16703         
16704         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
16705         
16706         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16707         
16708         this.picker().on('mousedown', this.onMousedown, this);
16709         this.picker().on('click', this.onClick, this);
16710         
16711         this.picker().addClass('datepicker-dropdown');
16712         
16713         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16714             v.setStyle('width', '189px');
16715         });
16716         
16717         this.fillMonths();
16718         
16719         this.update();
16720         
16721         if(this.isInline) {
16722             this.show();
16723         }
16724         
16725     },
16726     
16727     setValue: function(v, suppressEvent)
16728     {   
16729         var o = this.getValue();
16730         
16731         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
16732         
16733         this.update();
16734
16735         if(suppressEvent !== true){
16736             this.fireEvent('select', this, o, v);
16737         }
16738         
16739     },
16740     
16741     getValue: function()
16742     {
16743         return this.value;
16744     },
16745     
16746     onClick: function(e) 
16747     {
16748         e.stopPropagation();
16749         e.preventDefault();
16750         
16751         var target = e.getTarget();
16752         
16753         if(target.nodeName.toLowerCase() === 'i'){
16754             target = Roo.get(target).dom.parentNode;
16755         }
16756         
16757         var nodeName = target.nodeName;
16758         var className = target.className;
16759         var html = target.innerHTML;
16760         
16761         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
16762             return;
16763         }
16764         
16765         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
16766         
16767         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16768         
16769         this.hide();
16770                         
16771     },
16772     
16773     picker : function()
16774     {
16775         return this.pickerEl;
16776     },
16777     
16778     fillMonths: function()
16779     {    
16780         var i = 0;
16781         var months = this.picker().select('>.datepicker-months td', true).first();
16782         
16783         months.dom.innerHTML = '';
16784         
16785         while (i < 12) {
16786             var month = {
16787                 tag: 'span',
16788                 cls: 'month',
16789                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
16790             }
16791             
16792             months.createChild(month);
16793         }
16794         
16795     },
16796     
16797     update: function()
16798     {
16799         var _this = this;
16800         
16801         if(typeof(this.vIndex) == 'undefined' && this.value.length){
16802             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
16803         }
16804         
16805         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
16806             e.removeClass('active');
16807             
16808             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
16809                 e.addClass('active');
16810             }
16811         })
16812     },
16813     
16814     place: function()
16815     {
16816         if(this.isInline) return;
16817         
16818         this.picker().removeClass(['bottom', 'top']);
16819         
16820         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16821             /*
16822              * place to the top of element!
16823              *
16824              */
16825             
16826             this.picker().addClass('top');
16827             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16828             
16829             return;
16830         }
16831         
16832         this.picker().addClass('bottom');
16833         
16834         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16835     },
16836     
16837     onFocus : function()
16838     {
16839         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
16840         this.show();
16841     },
16842     
16843     onBlur : function()
16844     {
16845         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
16846         
16847         var d = this.inputEl().getValue();
16848         
16849         this.setValue(d);
16850                 
16851         this.hide();
16852     },
16853     
16854     show : function()
16855     {
16856         this.picker().show();
16857         this.picker().select('>.datepicker-months', true).first().show();
16858         this.update();
16859         this.place();
16860         
16861         this.fireEvent('show', this, this.date);
16862     },
16863     
16864     hide : function()
16865     {
16866         if(this.isInline) return;
16867         this.picker().hide();
16868         this.fireEvent('hide', this, this.date);
16869         
16870     },
16871     
16872     onMousedown: function(e)
16873     {
16874         e.stopPropagation();
16875         e.preventDefault();
16876     },
16877     
16878     keyup: function(e)
16879     {
16880         Roo.bootstrap.MonthField.superclass.keyup.call(this);
16881         this.update();
16882     },
16883
16884     fireKey: function(e)
16885     {
16886         if (!this.picker().isVisible()){
16887             if (e.keyCode == 27) // allow escape to hide and re-show picker
16888                 this.show();
16889             return;
16890         }
16891         
16892         var dir;
16893         
16894         switch(e.keyCode){
16895             case 27: // escape
16896                 this.hide();
16897                 e.preventDefault();
16898                 break;
16899             case 37: // left
16900             case 39: // right
16901                 dir = e.keyCode == 37 ? -1 : 1;
16902                 
16903                 this.vIndex = this.vIndex + dir;
16904                 
16905                 if(this.vIndex < 0){
16906                     this.vIndex = 0;
16907                 }
16908                 
16909                 if(this.vIndex > 11){
16910                     this.vIndex = 11;
16911                 }
16912                 
16913                 if(isNaN(this.vIndex)){
16914                     this.vIndex = 0;
16915                 }
16916                 
16917                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16918                 
16919                 break;
16920             case 38: // up
16921             case 40: // down
16922                 
16923                 dir = e.keyCode == 38 ? -1 : 1;
16924                 
16925                 this.vIndex = this.vIndex + dir * 4;
16926                 
16927                 if(this.vIndex < 0){
16928                     this.vIndex = 0;
16929                 }
16930                 
16931                 if(this.vIndex > 11){
16932                     this.vIndex = 11;
16933                 }
16934                 
16935                 if(isNaN(this.vIndex)){
16936                     this.vIndex = 0;
16937                 }
16938                 
16939                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16940                 break;
16941                 
16942             case 13: // enter
16943                 
16944                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16945                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16946                 }
16947                 
16948                 this.hide();
16949                 e.preventDefault();
16950                 break;
16951             case 9: // tab
16952                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16953                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16954                 }
16955                 this.hide();
16956                 break;
16957             case 16: // shift
16958             case 17: // ctrl
16959             case 18: // alt
16960                 break;
16961             default :
16962                 this.hide();
16963                 
16964         }
16965     },
16966     
16967     remove: function() 
16968     {
16969         this.picker().remove();
16970     }
16971    
16972 });
16973
16974 Roo.apply(Roo.bootstrap.MonthField,  {
16975     
16976     content : {
16977         tag: 'tbody',
16978         cn: [
16979         {
16980             tag: 'tr',
16981             cn: [
16982             {
16983                 tag: 'td',
16984                 colspan: '7'
16985             }
16986             ]
16987         }
16988         ]
16989     },
16990     
16991     dates:{
16992         en: {
16993             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16994             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
16995         }
16996     }
16997 });
16998
16999 Roo.apply(Roo.bootstrap.MonthField,  {
17000   
17001     template : {
17002         tag: 'div',
17003         cls: 'datepicker dropdown-menu roo-dynamic',
17004         cn: [
17005             {
17006                 tag: 'div',
17007                 cls: 'datepicker-months',
17008                 cn: [
17009                 {
17010                     tag: 'table',
17011                     cls: 'table-condensed',
17012                     cn:[
17013                         Roo.bootstrap.DateField.content
17014                     ]
17015                 }
17016                 ]
17017             }
17018         ]
17019     }
17020 });
17021
17022  
17023
17024  
17025  /*
17026  * - LGPL
17027  *
17028  * CheckBox
17029  * 
17030  */
17031
17032 /**
17033  * @class Roo.bootstrap.CheckBox
17034  * @extends Roo.bootstrap.Input
17035  * Bootstrap CheckBox class
17036  * 
17037  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17038  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17039  * @cfg {String} boxLabel The text that appears beside the checkbox
17040  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
17041  * @cfg {Boolean} checked initnal the element
17042  * @cfg {Boolean} inline inline the element (default false)
17043  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
17044  * 
17045  * @constructor
17046  * Create a new CheckBox
17047  * @param {Object} config The config object
17048  */
17049
17050 Roo.bootstrap.CheckBox = function(config){
17051     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
17052    
17053     this.addEvents({
17054         /**
17055         * @event check
17056         * Fires when the element is checked or unchecked.
17057         * @param {Roo.bootstrap.CheckBox} this This input
17058         * @param {Boolean} checked The new checked value
17059         */
17060        check : true
17061     });
17062     
17063 };
17064
17065 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
17066   
17067     inputType: 'checkbox',
17068     inputValue: 1,
17069     valueOff: 0,
17070     boxLabel: false,
17071     checked: false,
17072     weight : false,
17073     inline: false,
17074     
17075     getAutoCreate : function()
17076     {
17077         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17078         
17079         var id = Roo.id();
17080         
17081         var cfg = {};
17082         
17083         cfg.cls = 'form-group ' + this.inputType; //input-group
17084         
17085         if(this.inline){
17086             cfg.cls += ' ' + this.inputType + '-inline';
17087         }
17088         
17089         var input =  {
17090             tag: 'input',
17091             id : id,
17092             type : this.inputType,
17093             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
17094             cls : 'roo-' + this.inputType, //'form-box',
17095             placeholder : this.placeholder || ''
17096             
17097         };
17098         
17099         if (this.weight) { // Validity check?
17100             cfg.cls += " " + this.inputType + "-" + this.weight;
17101         }
17102         
17103         if (this.disabled) {
17104             input.disabled=true;
17105         }
17106         
17107         if(this.checked){
17108             input.checked = this.checked;
17109         }
17110         
17111         if (this.name) {
17112             input.name = this.name;
17113         }
17114         
17115         if (this.size) {
17116             input.cls += ' input-' + this.size;
17117         }
17118         
17119         var settings=this;
17120         
17121         ['xs','sm','md','lg'].map(function(size){
17122             if (settings[size]) {
17123                 cfg.cls += ' col-' + size + '-' + settings[size];
17124             }
17125         });
17126         
17127         var inputblock = input;
17128          
17129         if (this.before || this.after) {
17130             
17131             inputblock = {
17132                 cls : 'input-group',
17133                 cn :  [] 
17134             };
17135             
17136             if (this.before) {
17137                 inputblock.cn.push({
17138                     tag :'span',
17139                     cls : 'input-group-addon',
17140                     html : this.before
17141                 });
17142             }
17143             
17144             inputblock.cn.push(input);
17145             
17146             if (this.after) {
17147                 inputblock.cn.push({
17148                     tag :'span',
17149                     cls : 'input-group-addon',
17150                     html : this.after
17151                 });
17152             }
17153             
17154         }
17155         
17156         if (align ==='left' && this.fieldLabel.length) {
17157                 Roo.log("left and has label");
17158                 cfg.cn = [
17159                     
17160                     {
17161                         tag: 'label',
17162                         'for' :  id,
17163                         cls : 'control-label col-md-' + this.labelWidth,
17164                         html : this.fieldLabel
17165                         
17166                     },
17167                     {
17168                         cls : "col-md-" + (12 - this.labelWidth), 
17169                         cn: [
17170                             inputblock
17171                         ]
17172                     }
17173                     
17174                 ];
17175         } else if ( this.fieldLabel.length) {
17176                 Roo.log(" label");
17177                 cfg.cn = [
17178                    
17179                     {
17180                         tag: this.boxLabel ? 'span' : 'label',
17181                         'for': id,
17182                         cls: 'control-label box-input-label',
17183                         //cls : 'input-group-addon',
17184                         html : this.fieldLabel
17185                         
17186                     },
17187                     
17188                     inputblock
17189                     
17190                 ];
17191
17192         } else {
17193             
17194                 Roo.log(" no label && no align");
17195                 cfg.cn = [  inputblock ] ;
17196                 
17197                 
17198         }
17199         if(this.boxLabel){
17200              var boxLabelCfg = {
17201                 tag: 'label',
17202                 //'for': id, // box label is handled by onclick - so no for...
17203                 cls: 'box-label',
17204                 html: this.boxLabel
17205             }
17206             
17207             if(this.tooltip){
17208                 boxLabelCfg.tooltip = this.tooltip;
17209             }
17210              
17211             cfg.cn.push(boxLabelCfg);
17212         }
17213         
17214         
17215        
17216         return cfg;
17217         
17218     },
17219     
17220     /**
17221      * return the real input element.
17222      */
17223     inputEl: function ()
17224     {
17225         return this.el.select('input.roo-' + this.inputType,true).first();
17226     },
17227     
17228     labelEl: function()
17229     {
17230         return this.el.select('label.control-label',true).first();
17231     },
17232     /* depricated... */
17233     
17234     label: function()
17235     {
17236         return this.labelEl();
17237     },
17238     
17239     initEvents : function()
17240     {
17241 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17242         
17243         this.inputEl().on('click', this.onClick,  this);
17244         
17245         if (this.boxLabel) { 
17246             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
17247         }
17248         
17249         this.startValue = this.getValue();
17250         
17251         if(this.groupId){
17252             Roo.bootstrap.CheckBox.register(this);
17253         }
17254     },
17255     
17256     onClick : function()
17257     {   
17258         this.setChecked(!this.checked);
17259     },
17260     
17261     setChecked : function(state,suppressEvent)
17262     {
17263         this.startValue = this.getValue();
17264         
17265         if(this.inputType == 'radio'){
17266             
17267             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17268                 e.dom.checked = false;
17269             });
17270             
17271             this.inputEl().dom.checked = true;
17272             
17273             this.inputEl().dom.value = this.inputValue;
17274             
17275             if(suppressEvent !== true){
17276                 this.fireEvent('check', this, true);
17277             }
17278             
17279             this.validate();
17280             
17281             return;
17282         }
17283         
17284         this.checked = state;
17285         
17286         this.inputEl().dom.checked = state;
17287         
17288         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17289         
17290         if(suppressEvent !== true){
17291             this.fireEvent('check', this, state);
17292         }
17293         
17294         this.validate();
17295     },
17296     
17297     getValue : function()
17298     {
17299         if(this.inputType == 'radio'){
17300             return this.getGroupValue();
17301         }
17302         
17303         return this.inputEl().getValue();
17304         
17305     },
17306     
17307     getGroupValue : function()
17308     {
17309         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
17310             return '';
17311         }
17312         
17313         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
17314     },
17315     
17316     setValue : function(v,suppressEvent)
17317     {
17318         if(this.inputType == 'radio'){
17319             this.setGroupValue(v, suppressEvent);
17320             return;
17321         }
17322         
17323         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
17324         
17325         this.validate();
17326     },
17327     
17328     setGroupValue : function(v, suppressEvent)
17329     {
17330         this.startValue = this.getValue();
17331         
17332         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17333             e.dom.checked = false;
17334             
17335             if(e.dom.value == v){
17336                 e.dom.checked = true;
17337             }
17338         });
17339         
17340         if(suppressEvent !== true){
17341             this.fireEvent('check', this, true);
17342         }
17343
17344         this.validate();
17345         
17346         return;
17347     },
17348     
17349     validate : function()
17350     {
17351         if(
17352                 this.disabled || 
17353                 (this.inputType == 'radio' && this.validateRadio()) ||
17354                 (this.inputType == 'checkbox' && this.validateCheckbox())
17355         ){
17356             this.markValid();
17357             return true;
17358         }
17359         
17360         this.markInvalid();
17361         return false;
17362     },
17363     
17364     validateRadio : function()
17365     {
17366         var valid = false;
17367         
17368         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17369             if(!e.dom.checked){
17370                 return;
17371             }
17372             
17373             valid = true;
17374             
17375             return false;
17376         });
17377         
17378         return valid;
17379     },
17380     
17381     validateCheckbox : function()
17382     {
17383         if(!this.groupId){
17384             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
17385         }
17386         
17387         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17388         
17389         if(!group){
17390             return false;
17391         }
17392         
17393         var r = false;
17394         
17395         for(var i in group){
17396             if(r){
17397                 break;
17398             }
17399             
17400             r = (group[i].getValue() == group[i].inputValue) ? true : false;
17401         }
17402         
17403         return r;
17404     },
17405     
17406     /**
17407      * Mark this field as valid
17408      */
17409     markValid : function()
17410     {
17411         if(this.allowBlank){
17412             return;
17413         }
17414         
17415         var _this = this;
17416         
17417         this.fireEvent('valid', this);
17418         
17419         if(this.inputType == 'radio'){
17420             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17421                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17422                 e.findParent('.form-group', false, true).addClass(_this.validClass);
17423             });
17424             
17425             return;
17426         }
17427         
17428         if(!this.groupId){
17429             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17430             this.el.findParent('.form-group', false, true).addClass(this.validClass);
17431             return;
17432         }
17433         
17434         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17435             
17436         if(!group){
17437             return;
17438         }
17439         
17440         for(var i in group){
17441             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17442             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
17443         }
17444     },
17445     
17446      /**
17447      * Mark this field as invalid
17448      * @param {String} msg The validation message
17449      */
17450     markInvalid : function(msg)
17451     {
17452         if(this.allowBlank){
17453             return;
17454         }
17455         
17456         var _this = this;
17457         
17458         this.fireEvent('invalid', this, msg);
17459         
17460         if(this.inputType == 'radio'){
17461             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17462                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17463                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
17464             });
17465             
17466             return;
17467         }
17468         
17469         if(!this.groupId){
17470             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17471             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
17472             return;
17473         }
17474         
17475         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17476             
17477         if(!group){
17478             return;
17479         }
17480         
17481         for(var i in group){
17482             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17483             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
17484         }
17485         
17486     }
17487     
17488 });
17489
17490 Roo.apply(Roo.bootstrap.CheckBox, {
17491     
17492     groups: {},
17493     
17494      /**
17495     * register a CheckBox Group
17496     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
17497     */
17498     register : function(checkbox)
17499     {
17500         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
17501             this.groups[checkbox.groupId] = {};
17502         }
17503         
17504         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
17505             return;
17506         }
17507         
17508         this.groups[checkbox.groupId][checkbox.name] = checkbox;
17509         
17510     },
17511     /**
17512     * fetch a CheckBox Group based on the group ID
17513     * @param {string} the group ID
17514     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
17515     */
17516     get: function(groupId) {
17517         if (typeof(this.groups[groupId]) == 'undefined') {
17518             return false;
17519         }
17520         
17521         return this.groups[groupId] ;
17522     }
17523     
17524     
17525 });
17526 /*
17527  * - LGPL
17528  *
17529  * Radio
17530  *
17531  *
17532  * not inline
17533  *<div class="radio">
17534   <label>
17535     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
17536     Option one is this and that&mdash;be sure to include why it's great
17537   </label>
17538 </div>
17539  *
17540  *
17541  *inline
17542  *<span>
17543  *<label class="radio-inline">fieldLabel</label>
17544  *<label class="radio-inline">
17545   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
17546 </label>
17547 <span>
17548  * 
17549  * 
17550  */
17551
17552 /**
17553  * @class Roo.bootstrap.Radio
17554  * @extends Roo.bootstrap.CheckBox
17555  * Bootstrap Radio class
17556
17557  * @constructor
17558  * Create a new Radio
17559  * @param {Object} config The config object
17560  */
17561
17562 Roo.bootstrap.Radio = function(config){
17563     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
17564    
17565 };
17566
17567 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
17568     
17569     inputType: 'radio',
17570     inputValue: '',
17571     valueOff: '',
17572     
17573     getAutoCreate : function()
17574     {
17575         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17576         align = align || 'left'; // default...
17577         
17578         
17579         
17580         var id = Roo.id();
17581         
17582         var cfg = {
17583                 tag : this.inline ? 'span' : 'div',
17584                 cls : '',
17585                 cn : []
17586         };
17587         
17588         var inline = this.inline ? ' radio-inline' : '';
17589         
17590         var lbl = {
17591                 tag: 'label' ,
17592                 // does not need for, as we wrap the input with it..
17593                 'for' : id,
17594                 cls : 'control-label box-label' + inline,
17595                 cn : []
17596         };
17597         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
17598         
17599         var fieldLabel = {
17600             tag: 'label' ,
17601             //cls : 'control-label' + inline,
17602             html : this.fieldLabel,
17603             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
17604         };
17605         
17606  
17607         
17608         
17609         var input =  {
17610             tag: 'input',
17611             id : id,
17612             type : this.inputType,
17613             //value : (!this.checked) ? this.valueOff : this.inputValue,
17614             value : this.inputValue,
17615             cls : 'roo-radio',
17616             placeholder : this.placeholder || '' // ?? needed????
17617             
17618         };
17619         if (this.weight) { // Validity check?
17620             input.cls += " radio-" + this.weight;
17621         }
17622         if (this.disabled) {
17623             input.disabled=true;
17624         }
17625         
17626         if(this.checked){
17627             input.checked = this.checked;
17628         }
17629         
17630         if (this.name) {
17631             input.name = this.name;
17632         }
17633         
17634         if (this.size) {
17635             input.cls += ' input-' + this.size;
17636         }
17637         
17638         //?? can span's inline have a width??
17639         
17640         var settings=this;
17641         ['xs','sm','md','lg'].map(function(size){
17642             if (settings[size]) {
17643                 cfg.cls += ' col-' + size + '-' + settings[size];
17644             }
17645         });
17646         
17647         var inputblock = input;
17648         
17649         if (this.before || this.after) {
17650             
17651             inputblock = {
17652                 cls : 'input-group',
17653                 tag : 'span',
17654                 cn :  [] 
17655             };
17656             if (this.before) {
17657                 inputblock.cn.push({
17658                     tag :'span',
17659                     cls : 'input-group-addon',
17660                     html : this.before
17661                 });
17662             }
17663             inputblock.cn.push(input);
17664             if (this.after) {
17665                 inputblock.cn.push({
17666                     tag :'span',
17667                     cls : 'input-group-addon',
17668                     html : this.after
17669                 });
17670             }
17671             
17672         };
17673         
17674         
17675         if (this.fieldLabel && this.fieldLabel.length) {
17676             cfg.cn.push(fieldLabel);
17677         }
17678        
17679         // normal bootstrap puts the input inside the label.
17680         // however with our styled version - it has to go after the input.
17681        
17682         //lbl.cn.push(inputblock);
17683         
17684         var lblwrap =  {
17685             tag: 'span',
17686             cls: 'radio' + inline,
17687             cn: [
17688                 inputblock,
17689                 lbl
17690             ]
17691         };
17692         
17693         cfg.cn.push( lblwrap);
17694         
17695         if(this.boxLabel){
17696             lbl.cn.push({
17697                 tag: 'span',
17698                 html: this.boxLabel
17699             })
17700         }
17701          
17702         
17703         return cfg;
17704         
17705     },
17706     
17707     initEvents : function()
17708     {
17709 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17710         
17711         this.inputEl().on('click', this.onClick,  this);
17712         if (this.boxLabel) {
17713             Roo.log('find label')
17714             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
17715         }
17716         
17717     },
17718     
17719     inputEl: function ()
17720     {
17721         return this.el.select('input.roo-radio',true).first();
17722     },
17723     onClick : function()
17724     {   
17725         Roo.log("click");
17726         this.setChecked(true);
17727     },
17728     
17729     setChecked : function(state,suppressEvent)
17730     {
17731         if(state){
17732             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17733                 v.dom.checked = false;
17734             });
17735         }
17736         Roo.log(this.inputEl().dom);
17737         this.checked = state;
17738         this.inputEl().dom.checked = state;
17739         
17740         if(suppressEvent !== true){
17741             this.fireEvent('check', this, state);
17742         }
17743         
17744         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17745         
17746     },
17747     
17748     getGroupValue : function()
17749     {
17750         var value = '';
17751         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17752             if(v.dom.checked == true){
17753                 value = v.dom.value;
17754             }
17755         });
17756         
17757         return value;
17758     },
17759     
17760     /**
17761      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
17762      * @return {Mixed} value The field value
17763      */
17764     getValue : function(){
17765         return this.getGroupValue();
17766     }
17767     
17768 });
17769
17770  
17771 //<script type="text/javascript">
17772
17773 /*
17774  * Based  Ext JS Library 1.1.1
17775  * Copyright(c) 2006-2007, Ext JS, LLC.
17776  * LGPL
17777  *
17778  */
17779  
17780 /**
17781  * @class Roo.HtmlEditorCore
17782  * @extends Roo.Component
17783  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
17784  *
17785  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
17786  */
17787
17788 Roo.HtmlEditorCore = function(config){
17789     
17790     
17791     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
17792     
17793     
17794     this.addEvents({
17795         /**
17796          * @event initialize
17797          * Fires when the editor is fully initialized (including the iframe)
17798          * @param {Roo.HtmlEditorCore} this
17799          */
17800         initialize: true,
17801         /**
17802          * @event activate
17803          * Fires when the editor is first receives the focus. Any insertion must wait
17804          * until after this event.
17805          * @param {Roo.HtmlEditorCore} this
17806          */
17807         activate: true,
17808          /**
17809          * @event beforesync
17810          * Fires before the textarea is updated with content from the editor iframe. Return false
17811          * to cancel the sync.
17812          * @param {Roo.HtmlEditorCore} this
17813          * @param {String} html
17814          */
17815         beforesync: true,
17816          /**
17817          * @event beforepush
17818          * Fires before the iframe editor is updated with content from the textarea. Return false
17819          * to cancel the push.
17820          * @param {Roo.HtmlEditorCore} this
17821          * @param {String} html
17822          */
17823         beforepush: true,
17824          /**
17825          * @event sync
17826          * Fires when the textarea is updated with content from the editor iframe.
17827          * @param {Roo.HtmlEditorCore} this
17828          * @param {String} html
17829          */
17830         sync: true,
17831          /**
17832          * @event push
17833          * Fires when the iframe editor is updated with content from the textarea.
17834          * @param {Roo.HtmlEditorCore} this
17835          * @param {String} html
17836          */
17837         push: true,
17838         
17839         /**
17840          * @event editorevent
17841          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17842          * @param {Roo.HtmlEditorCore} this
17843          */
17844         editorevent: true
17845         
17846     });
17847     
17848     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
17849     
17850     // defaults : white / black...
17851     this.applyBlacklists();
17852     
17853     
17854     
17855 };
17856
17857
17858 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
17859
17860
17861      /**
17862      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
17863      */
17864     
17865     owner : false,
17866     
17867      /**
17868      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17869      *                        Roo.resizable.
17870      */
17871     resizable : false,
17872      /**
17873      * @cfg {Number} height (in pixels)
17874      */   
17875     height: 300,
17876    /**
17877      * @cfg {Number} width (in pixels)
17878      */   
17879     width: 500,
17880     
17881     /**
17882      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17883      * 
17884      */
17885     stylesheets: false,
17886     
17887     // id of frame..
17888     frameId: false,
17889     
17890     // private properties
17891     validationEvent : false,
17892     deferHeight: true,
17893     initialized : false,
17894     activated : false,
17895     sourceEditMode : false,
17896     onFocus : Roo.emptyFn,
17897     iframePad:3,
17898     hideMode:'offsets',
17899     
17900     clearUp: true,
17901     
17902     // blacklist + whitelisted elements..
17903     black: false,
17904     white: false,
17905      
17906     
17907
17908     /**
17909      * Protected method that will not generally be called directly. It
17910      * is called when the editor initializes the iframe with HTML contents. Override this method if you
17911      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
17912      */
17913     getDocMarkup : function(){
17914         // body styles..
17915         var st = '';
17916         
17917         // inherit styels from page...?? 
17918         if (this.stylesheets === false) {
17919             
17920             Roo.get(document.head).select('style').each(function(node) {
17921                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17922             });
17923             
17924             Roo.get(document.head).select('link').each(function(node) { 
17925                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17926             });
17927             
17928         } else if (!this.stylesheets.length) {
17929                 // simple..
17930                 st = '<style type="text/css">' +
17931                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17932                    '</style>';
17933         } else { 
17934             
17935         }
17936         
17937         st +=  '<style type="text/css">' +
17938             'IMG { cursor: pointer } ' +
17939         '</style>';
17940
17941         
17942         return '<html><head>' + st  +
17943             //<style type="text/css">' +
17944             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17945             //'</style>' +
17946             ' </head><body class="roo-htmleditor-body"></body></html>';
17947     },
17948
17949     // private
17950     onRender : function(ct, position)
17951     {
17952         var _t = this;
17953         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
17954         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
17955         
17956         
17957         this.el.dom.style.border = '0 none';
17958         this.el.dom.setAttribute('tabIndex', -1);
17959         this.el.addClass('x-hidden hide');
17960         
17961         
17962         
17963         if(Roo.isIE){ // fix IE 1px bogus margin
17964             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
17965         }
17966        
17967         
17968         this.frameId = Roo.id();
17969         
17970          
17971         
17972         var iframe = this.owner.wrap.createChild({
17973             tag: 'iframe',
17974             cls: 'form-control', // bootstrap..
17975             id: this.frameId,
17976             name: this.frameId,
17977             frameBorder : 'no',
17978             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
17979         }, this.el
17980         );
17981         
17982         
17983         this.iframe = iframe.dom;
17984
17985          this.assignDocWin();
17986         
17987         this.doc.designMode = 'on';
17988        
17989         this.doc.open();
17990         this.doc.write(this.getDocMarkup());
17991         this.doc.close();
17992
17993         
17994         var task = { // must defer to wait for browser to be ready
17995             run : function(){
17996                 //console.log("run task?" + this.doc.readyState);
17997                 this.assignDocWin();
17998                 if(this.doc.body || this.doc.readyState == 'complete'){
17999                     try {
18000                         this.doc.designMode="on";
18001                     } catch (e) {
18002                         return;
18003                     }
18004                     Roo.TaskMgr.stop(task);
18005                     this.initEditor.defer(10, this);
18006                 }
18007             },
18008             interval : 10,
18009             duration: 10000,
18010             scope: this
18011         };
18012         Roo.TaskMgr.start(task);
18013
18014     },
18015
18016     // private
18017     onResize : function(w, h)
18018     {
18019          Roo.log('resize: ' +w + ',' + h );
18020         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18021         if(!this.iframe){
18022             return;
18023         }
18024         if(typeof w == 'number'){
18025             
18026             this.iframe.style.width = w + 'px';
18027         }
18028         if(typeof h == 'number'){
18029             
18030             this.iframe.style.height = h + 'px';
18031             if(this.doc){
18032                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18033             }
18034         }
18035         
18036     },
18037
18038     /**
18039      * Toggles the editor between standard and source edit mode.
18040      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18041      */
18042     toggleSourceEdit : function(sourceEditMode){
18043         
18044         this.sourceEditMode = sourceEditMode === true;
18045         
18046         if(this.sourceEditMode){
18047  
18048             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
18049             
18050         }else{
18051             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
18052             //this.iframe.className = '';
18053             this.deferFocus();
18054         }
18055         //this.setSize(this.owner.wrap.getSize());
18056         //this.fireEvent('editmodechange', this, this.sourceEditMode);
18057     },
18058
18059     
18060   
18061
18062     /**
18063      * Protected method that will not generally be called directly. If you need/want
18064      * custom HTML cleanup, this is the method you should override.
18065      * @param {String} html The HTML to be cleaned
18066      * return {String} The cleaned HTML
18067      */
18068     cleanHtml : function(html){
18069         html = String(html);
18070         if(html.length > 5){
18071             if(Roo.isSafari){ // strip safari nonsense
18072                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
18073             }
18074         }
18075         if(html == '&nbsp;'){
18076             html = '';
18077         }
18078         return html;
18079     },
18080
18081     /**
18082      * HTML Editor -> Textarea
18083      * Protected method that will not generally be called directly. Syncs the contents
18084      * of the editor iframe with the textarea.
18085      */
18086     syncValue : function(){
18087         if(this.initialized){
18088             var bd = (this.doc.body || this.doc.documentElement);
18089             //this.cleanUpPaste(); -- this is done else where and causes havoc..
18090             var html = bd.innerHTML;
18091             if(Roo.isSafari){
18092                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
18093                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
18094                 if(m && m[1]){
18095                     html = '<div style="'+m[0]+'">' + html + '</div>';
18096                 }
18097             }
18098             html = this.cleanHtml(html);
18099             // fix up the special chars.. normaly like back quotes in word...
18100             // however we do not want to do this with chinese..
18101             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
18102                 var cc = b.charCodeAt();
18103                 if (
18104                     (cc >= 0x4E00 && cc < 0xA000 ) ||
18105                     (cc >= 0x3400 && cc < 0x4E00 ) ||
18106                     (cc >= 0xf900 && cc < 0xfb00 )
18107                 ) {
18108                         return b;
18109                 }
18110                 return "&#"+cc+";" 
18111             });
18112             if(this.owner.fireEvent('beforesync', this, html) !== false){
18113                 this.el.dom.value = html;
18114                 this.owner.fireEvent('sync', this, html);
18115             }
18116         }
18117     },
18118
18119     /**
18120      * Protected method that will not generally be called directly. Pushes the value of the textarea
18121      * into the iframe editor.
18122      */
18123     pushValue : function(){
18124         if(this.initialized){
18125             var v = this.el.dom.value.trim();
18126             
18127 //            if(v.length < 1){
18128 //                v = '&#160;';
18129 //            }
18130             
18131             if(this.owner.fireEvent('beforepush', this, v) !== false){
18132                 var d = (this.doc.body || this.doc.documentElement);
18133                 d.innerHTML = v;
18134                 this.cleanUpPaste();
18135                 this.el.dom.value = d.innerHTML;
18136                 this.owner.fireEvent('push', this, v);
18137             }
18138         }
18139     },
18140
18141     // private
18142     deferFocus : function(){
18143         this.focus.defer(10, this);
18144     },
18145
18146     // doc'ed in Field
18147     focus : function(){
18148         if(this.win && !this.sourceEditMode){
18149             this.win.focus();
18150         }else{
18151             this.el.focus();
18152         }
18153     },
18154     
18155     assignDocWin: function()
18156     {
18157         var iframe = this.iframe;
18158         
18159          if(Roo.isIE){
18160             this.doc = iframe.contentWindow.document;
18161             this.win = iframe.contentWindow;
18162         } else {
18163 //            if (!Roo.get(this.frameId)) {
18164 //                return;
18165 //            }
18166 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18167 //            this.win = Roo.get(this.frameId).dom.contentWindow;
18168             
18169             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
18170                 return;
18171             }
18172             
18173             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18174             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
18175         }
18176     },
18177     
18178     // private
18179     initEditor : function(){
18180         //console.log("INIT EDITOR");
18181         this.assignDocWin();
18182         
18183         
18184         
18185         this.doc.designMode="on";
18186         this.doc.open();
18187         this.doc.write(this.getDocMarkup());
18188         this.doc.close();
18189         
18190         var dbody = (this.doc.body || this.doc.documentElement);
18191         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
18192         // this copies styles from the containing element into thsi one..
18193         // not sure why we need all of this..
18194         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
18195         
18196         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
18197         //ss['background-attachment'] = 'fixed'; // w3c
18198         dbody.bgProperties = 'fixed'; // ie
18199         //Roo.DomHelper.applyStyles(dbody, ss);
18200         Roo.EventManager.on(this.doc, {
18201             //'mousedown': this.onEditorEvent,
18202             'mouseup': this.onEditorEvent,
18203             'dblclick': this.onEditorEvent,
18204             'click': this.onEditorEvent,
18205             'keyup': this.onEditorEvent,
18206             buffer:100,
18207             scope: this
18208         });
18209         if(Roo.isGecko){
18210             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
18211         }
18212         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
18213             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
18214         }
18215         this.initialized = true;
18216
18217         this.owner.fireEvent('initialize', this);
18218         this.pushValue();
18219     },
18220
18221     // private
18222     onDestroy : function(){
18223         
18224         
18225         
18226         if(this.rendered){
18227             
18228             //for (var i =0; i < this.toolbars.length;i++) {
18229             //    // fixme - ask toolbars for heights?
18230             //    this.toolbars[i].onDestroy();
18231            // }
18232             
18233             //this.wrap.dom.innerHTML = '';
18234             //this.wrap.remove();
18235         }
18236     },
18237
18238     // private
18239     onFirstFocus : function(){
18240         
18241         this.assignDocWin();
18242         
18243         
18244         this.activated = true;
18245          
18246     
18247         if(Roo.isGecko){ // prevent silly gecko errors
18248             this.win.focus();
18249             var s = this.win.getSelection();
18250             if(!s.focusNode || s.focusNode.nodeType != 3){
18251                 var r = s.getRangeAt(0);
18252                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
18253                 r.collapse(true);
18254                 this.deferFocus();
18255             }
18256             try{
18257                 this.execCmd('useCSS', true);
18258                 this.execCmd('styleWithCSS', false);
18259             }catch(e){}
18260         }
18261         this.owner.fireEvent('activate', this);
18262     },
18263
18264     // private
18265     adjustFont: function(btn){
18266         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
18267         //if(Roo.isSafari){ // safari
18268         //    adjust *= 2;
18269        // }
18270         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
18271         if(Roo.isSafari){ // safari
18272             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
18273             v =  (v < 10) ? 10 : v;
18274             v =  (v > 48) ? 48 : v;
18275             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
18276             
18277         }
18278         
18279         
18280         v = Math.max(1, v+adjust);
18281         
18282         this.execCmd('FontSize', v  );
18283     },
18284
18285     onEditorEvent : function(e){
18286         this.owner.fireEvent('editorevent', this, e);
18287       //  this.updateToolbar();
18288         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
18289     },
18290
18291     insertTag : function(tg)
18292     {
18293         // could be a bit smarter... -> wrap the current selected tRoo..
18294         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
18295             
18296             range = this.createRange(this.getSelection());
18297             var wrappingNode = this.doc.createElement(tg.toLowerCase());
18298             wrappingNode.appendChild(range.extractContents());
18299             range.insertNode(wrappingNode);
18300
18301             return;
18302             
18303             
18304             
18305         }
18306         this.execCmd("formatblock",   tg);
18307         
18308     },
18309     
18310     insertText : function(txt)
18311     {
18312         
18313         
18314         var range = this.createRange();
18315         range.deleteContents();
18316                //alert(Sender.getAttribute('label'));
18317                
18318         range.insertNode(this.doc.createTextNode(txt));
18319     } ,
18320     
18321      
18322
18323     /**
18324      * Executes a Midas editor command on the editor document and performs necessary focus and
18325      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
18326      * @param {String} cmd The Midas command
18327      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18328      */
18329     relayCmd : function(cmd, value){
18330         this.win.focus();
18331         this.execCmd(cmd, value);
18332         this.owner.fireEvent('editorevent', this);
18333         //this.updateToolbar();
18334         this.owner.deferFocus();
18335     },
18336
18337     /**
18338      * Executes a Midas editor command directly on the editor document.
18339      * For visual commands, you should use {@link #relayCmd} instead.
18340      * <b>This should only be called after the editor is initialized.</b>
18341      * @param {String} cmd The Midas command
18342      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18343      */
18344     execCmd : function(cmd, value){
18345         this.doc.execCommand(cmd, false, value === undefined ? null : value);
18346         this.syncValue();
18347     },
18348  
18349  
18350    
18351     /**
18352      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
18353      * to insert tRoo.
18354      * @param {String} text | dom node.. 
18355      */
18356     insertAtCursor : function(text)
18357     {
18358         
18359         
18360         
18361         if(!this.activated){
18362             return;
18363         }
18364         /*
18365         if(Roo.isIE){
18366             this.win.focus();
18367             var r = this.doc.selection.createRange();
18368             if(r){
18369                 r.collapse(true);
18370                 r.pasteHTML(text);
18371                 this.syncValue();
18372                 this.deferFocus();
18373             
18374             }
18375             return;
18376         }
18377         */
18378         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
18379             this.win.focus();
18380             
18381             
18382             // from jquery ui (MIT licenced)
18383             var range, node;
18384             var win = this.win;
18385             
18386             if (win.getSelection && win.getSelection().getRangeAt) {
18387                 range = win.getSelection().getRangeAt(0);
18388                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
18389                 range.insertNode(node);
18390             } else if (win.document.selection && win.document.selection.createRange) {
18391                 // no firefox support
18392                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18393                 win.document.selection.createRange().pasteHTML(txt);
18394             } else {
18395                 // no firefox support
18396                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18397                 this.execCmd('InsertHTML', txt);
18398             } 
18399             
18400             this.syncValue();
18401             
18402             this.deferFocus();
18403         }
18404     },
18405  // private
18406     mozKeyPress : function(e){
18407         if(e.ctrlKey){
18408             var c = e.getCharCode(), cmd;
18409           
18410             if(c > 0){
18411                 c = String.fromCharCode(c).toLowerCase();
18412                 switch(c){
18413                     case 'b':
18414                         cmd = 'bold';
18415                         break;
18416                     case 'i':
18417                         cmd = 'italic';
18418                         break;
18419                     
18420                     case 'u':
18421                         cmd = 'underline';
18422                         break;
18423                     
18424                     case 'v':
18425                         this.cleanUpPaste.defer(100, this);
18426                         return;
18427                         
18428                 }
18429                 if(cmd){
18430                     this.win.focus();
18431                     this.execCmd(cmd);
18432                     this.deferFocus();
18433                     e.preventDefault();
18434                 }
18435                 
18436             }
18437         }
18438     },
18439
18440     // private
18441     fixKeys : function(){ // load time branching for fastest keydown performance
18442         if(Roo.isIE){
18443             return function(e){
18444                 var k = e.getKey(), r;
18445                 if(k == e.TAB){
18446                     e.stopEvent();
18447                     r = this.doc.selection.createRange();
18448                     if(r){
18449                         r.collapse(true);
18450                         r.pasteHTML('&#160;&#160;&#160;&#160;');
18451                         this.deferFocus();
18452                     }
18453                     return;
18454                 }
18455                 
18456                 if(k == e.ENTER){
18457                     r = this.doc.selection.createRange();
18458                     if(r){
18459                         var target = r.parentElement();
18460                         if(!target || target.tagName.toLowerCase() != 'li'){
18461                             e.stopEvent();
18462                             r.pasteHTML('<br />');
18463                             r.collapse(false);
18464                             r.select();
18465                         }
18466                     }
18467                 }
18468                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18469                     this.cleanUpPaste.defer(100, this);
18470                     return;
18471                 }
18472                 
18473                 
18474             };
18475         }else if(Roo.isOpera){
18476             return function(e){
18477                 var k = e.getKey();
18478                 if(k == e.TAB){
18479                     e.stopEvent();
18480                     this.win.focus();
18481                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
18482                     this.deferFocus();
18483                 }
18484                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18485                     this.cleanUpPaste.defer(100, this);
18486                     return;
18487                 }
18488                 
18489             };
18490         }else if(Roo.isSafari){
18491             return function(e){
18492                 var k = e.getKey();
18493                 
18494                 if(k == e.TAB){
18495                     e.stopEvent();
18496                     this.execCmd('InsertText','\t');
18497                     this.deferFocus();
18498                     return;
18499                 }
18500                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18501                     this.cleanUpPaste.defer(100, this);
18502                     return;
18503                 }
18504                 
18505              };
18506         }
18507     }(),
18508     
18509     getAllAncestors: function()
18510     {
18511         var p = this.getSelectedNode();
18512         var a = [];
18513         if (!p) {
18514             a.push(p); // push blank onto stack..
18515             p = this.getParentElement();
18516         }
18517         
18518         
18519         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
18520             a.push(p);
18521             p = p.parentNode;
18522         }
18523         a.push(this.doc.body);
18524         return a;
18525     },
18526     lastSel : false,
18527     lastSelNode : false,
18528     
18529     
18530     getSelection : function() 
18531     {
18532         this.assignDocWin();
18533         return Roo.isIE ? this.doc.selection : this.win.getSelection();
18534     },
18535     
18536     getSelectedNode: function() 
18537     {
18538         // this may only work on Gecko!!!
18539         
18540         // should we cache this!!!!
18541         
18542         
18543         
18544          
18545         var range = this.createRange(this.getSelection()).cloneRange();
18546         
18547         if (Roo.isIE) {
18548             var parent = range.parentElement();
18549             while (true) {
18550                 var testRange = range.duplicate();
18551                 testRange.moveToElementText(parent);
18552                 if (testRange.inRange(range)) {
18553                     break;
18554                 }
18555                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
18556                     break;
18557                 }
18558                 parent = parent.parentElement;
18559             }
18560             return parent;
18561         }
18562         
18563         // is ancestor a text element.
18564         var ac =  range.commonAncestorContainer;
18565         if (ac.nodeType == 3) {
18566             ac = ac.parentNode;
18567         }
18568         
18569         var ar = ac.childNodes;
18570          
18571         var nodes = [];
18572         var other_nodes = [];
18573         var has_other_nodes = false;
18574         for (var i=0;i<ar.length;i++) {
18575             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
18576                 continue;
18577             }
18578             // fullly contained node.
18579             
18580             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
18581                 nodes.push(ar[i]);
18582                 continue;
18583             }
18584             
18585             // probably selected..
18586             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
18587                 other_nodes.push(ar[i]);
18588                 continue;
18589             }
18590             // outer..
18591             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
18592                 continue;
18593             }
18594             
18595             
18596             has_other_nodes = true;
18597         }
18598         if (!nodes.length && other_nodes.length) {
18599             nodes= other_nodes;
18600         }
18601         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
18602             return false;
18603         }
18604         
18605         return nodes[0];
18606     },
18607     createRange: function(sel)
18608     {
18609         // this has strange effects when using with 
18610         // top toolbar - not sure if it's a great idea.
18611         //this.editor.contentWindow.focus();
18612         if (typeof sel != "undefined") {
18613             try {
18614                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
18615             } catch(e) {
18616                 return this.doc.createRange();
18617             }
18618         } else {
18619             return this.doc.createRange();
18620         }
18621     },
18622     getParentElement: function()
18623     {
18624         
18625         this.assignDocWin();
18626         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
18627         
18628         var range = this.createRange(sel);
18629          
18630         try {
18631             var p = range.commonAncestorContainer;
18632             while (p.nodeType == 3) { // text node
18633                 p = p.parentNode;
18634             }
18635             return p;
18636         } catch (e) {
18637             return null;
18638         }
18639     
18640     },
18641     /***
18642      *
18643      * Range intersection.. the hard stuff...
18644      *  '-1' = before
18645      *  '0' = hits..
18646      *  '1' = after.
18647      *         [ -- selected range --- ]
18648      *   [fail]                        [fail]
18649      *
18650      *    basically..
18651      *      if end is before start or  hits it. fail.
18652      *      if start is after end or hits it fail.
18653      *
18654      *   if either hits (but other is outside. - then it's not 
18655      *   
18656      *    
18657      **/
18658     
18659     
18660     // @see http://www.thismuchiknow.co.uk/?p=64.
18661     rangeIntersectsNode : function(range, node)
18662     {
18663         var nodeRange = node.ownerDocument.createRange();
18664         try {
18665             nodeRange.selectNode(node);
18666         } catch (e) {
18667             nodeRange.selectNodeContents(node);
18668         }
18669     
18670         var rangeStartRange = range.cloneRange();
18671         rangeStartRange.collapse(true);
18672     
18673         var rangeEndRange = range.cloneRange();
18674         rangeEndRange.collapse(false);
18675     
18676         var nodeStartRange = nodeRange.cloneRange();
18677         nodeStartRange.collapse(true);
18678     
18679         var nodeEndRange = nodeRange.cloneRange();
18680         nodeEndRange.collapse(false);
18681     
18682         return rangeStartRange.compareBoundaryPoints(
18683                  Range.START_TO_START, nodeEndRange) == -1 &&
18684                rangeEndRange.compareBoundaryPoints(
18685                  Range.START_TO_START, nodeStartRange) == 1;
18686         
18687          
18688     },
18689     rangeCompareNode : function(range, node)
18690     {
18691         var nodeRange = node.ownerDocument.createRange();
18692         try {
18693             nodeRange.selectNode(node);
18694         } catch (e) {
18695             nodeRange.selectNodeContents(node);
18696         }
18697         
18698         
18699         range.collapse(true);
18700     
18701         nodeRange.collapse(true);
18702      
18703         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
18704         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
18705          
18706         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
18707         
18708         var nodeIsBefore   =  ss == 1;
18709         var nodeIsAfter    = ee == -1;
18710         
18711         if (nodeIsBefore && nodeIsAfter)
18712             return 0; // outer
18713         if (!nodeIsBefore && nodeIsAfter)
18714             return 1; //right trailed.
18715         
18716         if (nodeIsBefore && !nodeIsAfter)
18717             return 2;  // left trailed.
18718         // fully contined.
18719         return 3;
18720     },
18721
18722     // private? - in a new class?
18723     cleanUpPaste :  function()
18724     {
18725         // cleans up the whole document..
18726         Roo.log('cleanuppaste');
18727         
18728         this.cleanUpChildren(this.doc.body);
18729         var clean = this.cleanWordChars(this.doc.body.innerHTML);
18730         if (clean != this.doc.body.innerHTML) {
18731             this.doc.body.innerHTML = clean;
18732         }
18733         
18734     },
18735     
18736     cleanWordChars : function(input) {// change the chars to hex code
18737         var he = Roo.HtmlEditorCore;
18738         
18739         var output = input;
18740         Roo.each(he.swapCodes, function(sw) { 
18741             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
18742             
18743             output = output.replace(swapper, sw[1]);
18744         });
18745         
18746         return output;
18747     },
18748     
18749     
18750     cleanUpChildren : function (n)
18751     {
18752         if (!n.childNodes.length) {
18753             return;
18754         }
18755         for (var i = n.childNodes.length-1; i > -1 ; i--) {
18756            this.cleanUpChild(n.childNodes[i]);
18757         }
18758     },
18759     
18760     
18761         
18762     
18763     cleanUpChild : function (node)
18764     {
18765         var ed = this;
18766         //console.log(node);
18767         if (node.nodeName == "#text") {
18768             // clean up silly Windows -- stuff?
18769             return; 
18770         }
18771         if (node.nodeName == "#comment") {
18772             node.parentNode.removeChild(node);
18773             // clean up silly Windows -- stuff?
18774             return; 
18775         }
18776         var lcname = node.tagName.toLowerCase();
18777         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
18778         // whitelist of tags..
18779         
18780         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
18781             // remove node.
18782             node.parentNode.removeChild(node);
18783             return;
18784             
18785         }
18786         
18787         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
18788         
18789         // remove <a name=....> as rendering on yahoo mailer is borked with this.
18790         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
18791         
18792         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
18793         //    remove_keep_children = true;
18794         //}
18795         
18796         if (remove_keep_children) {
18797             this.cleanUpChildren(node);
18798             // inserts everything just before this node...
18799             while (node.childNodes.length) {
18800                 var cn = node.childNodes[0];
18801                 node.removeChild(cn);
18802                 node.parentNode.insertBefore(cn, node);
18803             }
18804             node.parentNode.removeChild(node);
18805             return;
18806         }
18807         
18808         if (!node.attributes || !node.attributes.length) {
18809             this.cleanUpChildren(node);
18810             return;
18811         }
18812         
18813         function cleanAttr(n,v)
18814         {
18815             
18816             if (v.match(/^\./) || v.match(/^\//)) {
18817                 return;
18818             }
18819             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
18820                 return;
18821             }
18822             if (v.match(/^#/)) {
18823                 return;
18824             }
18825 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
18826             node.removeAttribute(n);
18827             
18828         }
18829         
18830         var cwhite = this.cwhite;
18831         var cblack = this.cblack;
18832             
18833         function cleanStyle(n,v)
18834         {
18835             if (v.match(/expression/)) { //XSS?? should we even bother..
18836                 node.removeAttribute(n);
18837                 return;
18838             }
18839             
18840             var parts = v.split(/;/);
18841             var clean = [];
18842             
18843             Roo.each(parts, function(p) {
18844                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
18845                 if (!p.length) {
18846                     return true;
18847                 }
18848                 var l = p.split(':').shift().replace(/\s+/g,'');
18849                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
18850                 
18851                 if ( cwhite.length && cblack.indexOf(l) > -1) {
18852 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18853                     //node.removeAttribute(n);
18854                     return true;
18855                 }
18856                 //Roo.log()
18857                 // only allow 'c whitelisted system attributes'
18858                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
18859 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18860                     //node.removeAttribute(n);
18861                     return true;
18862                 }
18863                 
18864                 
18865                  
18866                 
18867                 clean.push(p);
18868                 return true;
18869             });
18870             if (clean.length) { 
18871                 node.setAttribute(n, clean.join(';'));
18872             } else {
18873                 node.removeAttribute(n);
18874             }
18875             
18876         }
18877         
18878         
18879         for (var i = node.attributes.length-1; i > -1 ; i--) {
18880             var a = node.attributes[i];
18881             //console.log(a);
18882             
18883             if (a.name.toLowerCase().substr(0,2)=='on')  {
18884                 node.removeAttribute(a.name);
18885                 continue;
18886             }
18887             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
18888                 node.removeAttribute(a.name);
18889                 continue;
18890             }
18891             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
18892                 cleanAttr(a.name,a.value); // fixme..
18893                 continue;
18894             }
18895             if (a.name == 'style') {
18896                 cleanStyle(a.name,a.value);
18897                 continue;
18898             }
18899             /// clean up MS crap..
18900             // tecnically this should be a list of valid class'es..
18901             
18902             
18903             if (a.name == 'class') {
18904                 if (a.value.match(/^Mso/)) {
18905                     node.className = '';
18906                 }
18907                 
18908                 if (a.value.match(/body/)) {
18909                     node.className = '';
18910                 }
18911                 continue;
18912             }
18913             
18914             // style cleanup!?
18915             // class cleanup?
18916             
18917         }
18918         
18919         
18920         this.cleanUpChildren(node);
18921         
18922         
18923     },
18924     /**
18925      * Clean up MS wordisms...
18926      */
18927     cleanWord : function(node)
18928     {
18929         var _t = this;
18930         var cleanWordChildren = function()
18931         {
18932             if (!node.childNodes.length) {
18933                 return;
18934             }
18935             for (var i = node.childNodes.length-1; i > -1 ; i--) {
18936                _t.cleanWord(node.childNodes[i]);
18937             }
18938         }
18939         
18940         
18941         if (!node) {
18942             this.cleanWord(this.doc.body);
18943             return;
18944         }
18945         if (node.nodeName == "#text") {
18946             // clean up silly Windows -- stuff?
18947             return; 
18948         }
18949         if (node.nodeName == "#comment") {
18950             node.parentNode.removeChild(node);
18951             // clean up silly Windows -- stuff?
18952             return; 
18953         }
18954         
18955         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
18956             node.parentNode.removeChild(node);
18957             return;
18958         }
18959         
18960         // remove - but keep children..
18961         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
18962             while (node.childNodes.length) {
18963                 var cn = node.childNodes[0];
18964                 node.removeChild(cn);
18965                 node.parentNode.insertBefore(cn, node);
18966             }
18967             node.parentNode.removeChild(node);
18968             cleanWordChildren();
18969             return;
18970         }
18971         // clean styles
18972         if (node.className.length) {
18973             
18974             var cn = node.className.split(/\W+/);
18975             var cna = [];
18976             Roo.each(cn, function(cls) {
18977                 if (cls.match(/Mso[a-zA-Z]+/)) {
18978                     return;
18979                 }
18980                 cna.push(cls);
18981             });
18982             node.className = cna.length ? cna.join(' ') : '';
18983             if (!cna.length) {
18984                 node.removeAttribute("class");
18985             }
18986         }
18987         
18988         if (node.hasAttribute("lang")) {
18989             node.removeAttribute("lang");
18990         }
18991         
18992         if (node.hasAttribute("style")) {
18993             
18994             var styles = node.getAttribute("style").split(";");
18995             var nstyle = [];
18996             Roo.each(styles, function(s) {
18997                 if (!s.match(/:/)) {
18998                     return;
18999                 }
19000                 var kv = s.split(":");
19001                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
19002                     return;
19003                 }
19004                 // what ever is left... we allow.
19005                 nstyle.push(s);
19006             });
19007             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
19008             if (!nstyle.length) {
19009                 node.removeAttribute('style');
19010             }
19011         }
19012         
19013         cleanWordChildren();
19014         
19015         
19016     },
19017     domToHTML : function(currentElement, depth, nopadtext) {
19018         
19019         depth = depth || 0;
19020         nopadtext = nopadtext || false;
19021     
19022         if (!currentElement) {
19023             return this.domToHTML(this.doc.body);
19024         }
19025         
19026         //Roo.log(currentElement);
19027         var j;
19028         var allText = false;
19029         var nodeName = currentElement.nodeName;
19030         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
19031         
19032         if  (nodeName == '#text') {
19033             
19034             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
19035         }
19036         
19037         
19038         var ret = '';
19039         if (nodeName != 'BODY') {
19040              
19041             var i = 0;
19042             // Prints the node tagName, such as <A>, <IMG>, etc
19043             if (tagName) {
19044                 var attr = [];
19045                 for(i = 0; i < currentElement.attributes.length;i++) {
19046                     // quoting?
19047                     var aname = currentElement.attributes.item(i).name;
19048                     if (!currentElement.attributes.item(i).value.length) {
19049                         continue;
19050                     }
19051                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
19052                 }
19053                 
19054                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
19055             } 
19056             else {
19057                 
19058                 // eack
19059             }
19060         } else {
19061             tagName = false;
19062         }
19063         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
19064             return ret;
19065         }
19066         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
19067             nopadtext = true;
19068         }
19069         
19070         
19071         // Traverse the tree
19072         i = 0;
19073         var currentElementChild = currentElement.childNodes.item(i);
19074         var allText = true;
19075         var innerHTML  = '';
19076         lastnode = '';
19077         while (currentElementChild) {
19078             // Formatting code (indent the tree so it looks nice on the screen)
19079             var nopad = nopadtext;
19080             if (lastnode == 'SPAN') {
19081                 nopad  = true;
19082             }
19083             // text
19084             if  (currentElementChild.nodeName == '#text') {
19085                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
19086                 toadd = nopadtext ? toadd : toadd.trim();
19087                 if (!nopad && toadd.length > 80) {
19088                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
19089                 }
19090                 innerHTML  += toadd;
19091                 
19092                 i++;
19093                 currentElementChild = currentElement.childNodes.item(i);
19094                 lastNode = '';
19095                 continue;
19096             }
19097             allText = false;
19098             
19099             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
19100                 
19101             // Recursively traverse the tree structure of the child node
19102             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
19103             lastnode = currentElementChild.nodeName;
19104             i++;
19105             currentElementChild=currentElement.childNodes.item(i);
19106         }
19107         
19108         ret += innerHTML;
19109         
19110         if (!allText) {
19111                 // The remaining code is mostly for formatting the tree
19112             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
19113         }
19114         
19115         
19116         if (tagName) {
19117             ret+= "</"+tagName+">";
19118         }
19119         return ret;
19120         
19121     },
19122         
19123     applyBlacklists : function()
19124     {
19125         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
19126         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
19127         
19128         this.white = [];
19129         this.black = [];
19130         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
19131             if (b.indexOf(tag) > -1) {
19132                 return;
19133             }
19134             this.white.push(tag);
19135             
19136         }, this);
19137         
19138         Roo.each(w, function(tag) {
19139             if (b.indexOf(tag) > -1) {
19140                 return;
19141             }
19142             if (this.white.indexOf(tag) > -1) {
19143                 return;
19144             }
19145             this.white.push(tag);
19146             
19147         }, this);
19148         
19149         
19150         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
19151             if (w.indexOf(tag) > -1) {
19152                 return;
19153             }
19154             this.black.push(tag);
19155             
19156         }, this);
19157         
19158         Roo.each(b, function(tag) {
19159             if (w.indexOf(tag) > -1) {
19160                 return;
19161             }
19162             if (this.black.indexOf(tag) > -1) {
19163                 return;
19164             }
19165             this.black.push(tag);
19166             
19167         }, this);
19168         
19169         
19170         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
19171         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
19172         
19173         this.cwhite = [];
19174         this.cblack = [];
19175         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
19176             if (b.indexOf(tag) > -1) {
19177                 return;
19178             }
19179             this.cwhite.push(tag);
19180             
19181         }, this);
19182         
19183         Roo.each(w, function(tag) {
19184             if (b.indexOf(tag) > -1) {
19185                 return;
19186             }
19187             if (this.cwhite.indexOf(tag) > -1) {
19188                 return;
19189             }
19190             this.cwhite.push(tag);
19191             
19192         }, this);
19193         
19194         
19195         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
19196             if (w.indexOf(tag) > -1) {
19197                 return;
19198             }
19199             this.cblack.push(tag);
19200             
19201         }, this);
19202         
19203         Roo.each(b, function(tag) {
19204             if (w.indexOf(tag) > -1) {
19205                 return;
19206             }
19207             if (this.cblack.indexOf(tag) > -1) {
19208                 return;
19209             }
19210             this.cblack.push(tag);
19211             
19212         }, this);
19213     },
19214     
19215     setStylesheets : function(stylesheets)
19216     {
19217         if(typeof(stylesheets) == 'string'){
19218             Roo.get(this.iframe.contentDocument.head).createChild({
19219                 tag : 'link',
19220                 rel : 'stylesheet',
19221                 type : 'text/css',
19222                 href : stylesheets
19223             });
19224             
19225             return;
19226         }
19227         var _this = this;
19228      
19229         Roo.each(stylesheets, function(s) {
19230             if(!s.length){
19231                 return;
19232             }
19233             
19234             Roo.get(_this.iframe.contentDocument.head).createChild({
19235                 tag : 'link',
19236                 rel : 'stylesheet',
19237                 type : 'text/css',
19238                 href : s
19239             });
19240         });
19241
19242         
19243     },
19244     
19245     removeStylesheets : function()
19246     {
19247         var _this = this;
19248         
19249         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
19250             s.remove();
19251         });
19252     }
19253     
19254     // hide stuff that is not compatible
19255     /**
19256      * @event blur
19257      * @hide
19258      */
19259     /**
19260      * @event change
19261      * @hide
19262      */
19263     /**
19264      * @event focus
19265      * @hide
19266      */
19267     /**
19268      * @event specialkey
19269      * @hide
19270      */
19271     /**
19272      * @cfg {String} fieldClass @hide
19273      */
19274     /**
19275      * @cfg {String} focusClass @hide
19276      */
19277     /**
19278      * @cfg {String} autoCreate @hide
19279      */
19280     /**
19281      * @cfg {String} inputType @hide
19282      */
19283     /**
19284      * @cfg {String} invalidClass @hide
19285      */
19286     /**
19287      * @cfg {String} invalidText @hide
19288      */
19289     /**
19290      * @cfg {String} msgFx @hide
19291      */
19292     /**
19293      * @cfg {String} validateOnBlur @hide
19294      */
19295 });
19296
19297 Roo.HtmlEditorCore.white = [
19298         'area', 'br', 'img', 'input', 'hr', 'wbr',
19299         
19300        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
19301        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
19302        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
19303        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
19304        'table',   'ul',         'xmp', 
19305        
19306        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
19307       'thead',   'tr', 
19308      
19309       'dir', 'menu', 'ol', 'ul', 'dl',
19310        
19311       'embed',  'object'
19312 ];
19313
19314
19315 Roo.HtmlEditorCore.black = [
19316     //    'embed',  'object', // enable - backend responsiblity to clean thiese
19317         'applet', // 
19318         'base',   'basefont', 'bgsound', 'blink',  'body', 
19319         'frame',  'frameset', 'head',    'html',   'ilayer', 
19320         'iframe', 'layer',  'link',     'meta',    'object',   
19321         'script', 'style' ,'title',  'xml' // clean later..
19322 ];
19323 Roo.HtmlEditorCore.clean = [
19324     'script', 'style', 'title', 'xml'
19325 ];
19326 Roo.HtmlEditorCore.remove = [
19327     'font'
19328 ];
19329 // attributes..
19330
19331 Roo.HtmlEditorCore.ablack = [
19332     'on'
19333 ];
19334     
19335 Roo.HtmlEditorCore.aclean = [ 
19336     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
19337 ];
19338
19339 // protocols..
19340 Roo.HtmlEditorCore.pwhite= [
19341         'http',  'https',  'mailto'
19342 ];
19343
19344 // white listed style attributes.
19345 Roo.HtmlEditorCore.cwhite= [
19346       //  'text-align', /// default is to allow most things..
19347       
19348          
19349 //        'font-size'//??
19350 ];
19351
19352 // black listed style attributes.
19353 Roo.HtmlEditorCore.cblack= [
19354       //  'font-size' -- this can be set by the project 
19355 ];
19356
19357
19358 Roo.HtmlEditorCore.swapCodes   =[ 
19359     [    8211, "--" ], 
19360     [    8212, "--" ], 
19361     [    8216,  "'" ],  
19362     [    8217, "'" ],  
19363     [    8220, '"' ],  
19364     [    8221, '"' ],  
19365     [    8226, "*" ],  
19366     [    8230, "..." ]
19367 ]; 
19368
19369     /*
19370  * - LGPL
19371  *
19372  * HtmlEditor
19373  * 
19374  */
19375
19376 /**
19377  * @class Roo.bootstrap.HtmlEditor
19378  * @extends Roo.bootstrap.TextArea
19379  * Bootstrap HtmlEditor class
19380
19381  * @constructor
19382  * Create a new HtmlEditor
19383  * @param {Object} config The config object
19384  */
19385
19386 Roo.bootstrap.HtmlEditor = function(config){
19387     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
19388     if (!this.toolbars) {
19389         this.toolbars = [];
19390     }
19391     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
19392     this.addEvents({
19393             /**
19394              * @event initialize
19395              * Fires when the editor is fully initialized (including the iframe)
19396              * @param {HtmlEditor} this
19397              */
19398             initialize: true,
19399             /**
19400              * @event activate
19401              * Fires when the editor is first receives the focus. Any insertion must wait
19402              * until after this event.
19403              * @param {HtmlEditor} this
19404              */
19405             activate: true,
19406              /**
19407              * @event beforesync
19408              * Fires before the textarea is updated with content from the editor iframe. Return false
19409              * to cancel the sync.
19410              * @param {HtmlEditor} this
19411              * @param {String} html
19412              */
19413             beforesync: true,
19414              /**
19415              * @event beforepush
19416              * Fires before the iframe editor is updated with content from the textarea. Return false
19417              * to cancel the push.
19418              * @param {HtmlEditor} this
19419              * @param {String} html
19420              */
19421             beforepush: true,
19422              /**
19423              * @event sync
19424              * Fires when the textarea is updated with content from the editor iframe.
19425              * @param {HtmlEditor} this
19426              * @param {String} html
19427              */
19428             sync: true,
19429              /**
19430              * @event push
19431              * Fires when the iframe editor is updated with content from the textarea.
19432              * @param {HtmlEditor} this
19433              * @param {String} html
19434              */
19435             push: true,
19436              /**
19437              * @event editmodechange
19438              * Fires when the editor switches edit modes
19439              * @param {HtmlEditor} this
19440              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
19441              */
19442             editmodechange: true,
19443             /**
19444              * @event editorevent
19445              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19446              * @param {HtmlEditor} this
19447              */
19448             editorevent: true,
19449             /**
19450              * @event firstfocus
19451              * Fires when on first focus - needed by toolbars..
19452              * @param {HtmlEditor} this
19453              */
19454             firstfocus: true,
19455             /**
19456              * @event autosave
19457              * Auto save the htmlEditor value as a file into Events
19458              * @param {HtmlEditor} this
19459              */
19460             autosave: true,
19461             /**
19462              * @event savedpreview
19463              * preview the saved version of htmlEditor
19464              * @param {HtmlEditor} this
19465              */
19466             savedpreview: true
19467         });
19468 };
19469
19470
19471 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
19472     
19473     
19474       /**
19475      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
19476      */
19477     toolbars : false,
19478    
19479      /**
19480      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19481      *                        Roo.resizable.
19482      */
19483     resizable : false,
19484      /**
19485      * @cfg {Number} height (in pixels)
19486      */   
19487     height: 300,
19488    /**
19489      * @cfg {Number} width (in pixels)
19490      */   
19491     width: false,
19492     
19493     /**
19494      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19495      * 
19496      */
19497     stylesheets: false,
19498     
19499     // id of frame..
19500     frameId: false,
19501     
19502     // private properties
19503     validationEvent : false,
19504     deferHeight: true,
19505     initialized : false,
19506     activated : false,
19507     
19508     onFocus : Roo.emptyFn,
19509     iframePad:3,
19510     hideMode:'offsets',
19511     
19512     
19513     tbContainer : false,
19514     
19515     toolbarContainer :function() {
19516         return this.wrap.select('.x-html-editor-tb',true).first();
19517     },
19518
19519     /**
19520      * Protected method that will not generally be called directly. It
19521      * is called when the editor creates its toolbar. Override this method if you need to
19522      * add custom toolbar buttons.
19523      * @param {HtmlEditor} editor
19524      */
19525     createToolbar : function(){
19526         
19527         Roo.log("create toolbars");
19528         
19529         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
19530         this.toolbars[0].render(this.toolbarContainer());
19531         
19532         return;
19533         
19534 //        if (!editor.toolbars || !editor.toolbars.length) {
19535 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
19536 //        }
19537 //        
19538 //        for (var i =0 ; i < editor.toolbars.length;i++) {
19539 //            editor.toolbars[i] = Roo.factory(
19540 //                    typeof(editor.toolbars[i]) == 'string' ?
19541 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
19542 //                Roo.bootstrap.HtmlEditor);
19543 //            editor.toolbars[i].init(editor);
19544 //        }
19545     },
19546
19547      
19548     // private
19549     onRender : function(ct, position)
19550     {
19551        // Roo.log("Call onRender: " + this.xtype);
19552         var _t = this;
19553         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
19554       
19555         this.wrap = this.inputEl().wrap({
19556             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
19557         });
19558         
19559         this.editorcore.onRender(ct, position);
19560          
19561         if (this.resizable) {
19562             this.resizeEl = new Roo.Resizable(this.wrap, {
19563                 pinned : true,
19564                 wrap: true,
19565                 dynamic : true,
19566                 minHeight : this.height,
19567                 height: this.height,
19568                 handles : this.resizable,
19569                 width: this.width,
19570                 listeners : {
19571                     resize : function(r, w, h) {
19572                         _t.onResize(w,h); // -something
19573                     }
19574                 }
19575             });
19576             
19577         }
19578         this.createToolbar(this);
19579        
19580         
19581         if(!this.width && this.resizable){
19582             this.setSize(this.wrap.getSize());
19583         }
19584         if (this.resizeEl) {
19585             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
19586             // should trigger onReize..
19587         }
19588         
19589     },
19590
19591     // private
19592     onResize : function(w, h)
19593     {
19594         Roo.log('resize: ' +w + ',' + h );
19595         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
19596         var ew = false;
19597         var eh = false;
19598         
19599         if(this.inputEl() ){
19600             if(typeof w == 'number'){
19601                 var aw = w - this.wrap.getFrameWidth('lr');
19602                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
19603                 ew = aw;
19604             }
19605             if(typeof h == 'number'){
19606                  var tbh = -11;  // fixme it needs to tool bar size!
19607                 for (var i =0; i < this.toolbars.length;i++) {
19608                     // fixme - ask toolbars for heights?
19609                     tbh += this.toolbars[i].el.getHeight();
19610                     //if (this.toolbars[i].footer) {
19611                     //    tbh += this.toolbars[i].footer.el.getHeight();
19612                     //}
19613                 }
19614               
19615                 
19616                 
19617                 
19618                 
19619                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
19620                 ah -= 5; // knock a few pixes off for look..
19621                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
19622                 var eh = ah;
19623             }
19624         }
19625         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
19626         this.editorcore.onResize(ew,eh);
19627         
19628     },
19629
19630     /**
19631      * Toggles the editor between standard and source edit mode.
19632      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19633      */
19634     toggleSourceEdit : function(sourceEditMode)
19635     {
19636         this.editorcore.toggleSourceEdit(sourceEditMode);
19637         
19638         if(this.editorcore.sourceEditMode){
19639             Roo.log('editor - showing textarea');
19640             
19641 //            Roo.log('in');
19642 //            Roo.log(this.syncValue());
19643             this.syncValue();
19644             this.inputEl().removeClass(['hide', 'x-hidden']);
19645             this.inputEl().dom.removeAttribute('tabIndex');
19646             this.inputEl().focus();
19647         }else{
19648             Roo.log('editor - hiding textarea');
19649 //            Roo.log('out')
19650 //            Roo.log(this.pushValue()); 
19651             this.pushValue();
19652             
19653             this.inputEl().addClass(['hide', 'x-hidden']);
19654             this.inputEl().dom.setAttribute('tabIndex', -1);
19655             //this.deferFocus();
19656         }
19657          
19658         if(this.resizable){
19659             this.setSize(this.wrap.getSize());
19660         }
19661         
19662         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
19663     },
19664  
19665     // private (for BoxComponent)
19666     adjustSize : Roo.BoxComponent.prototype.adjustSize,
19667
19668     // private (for BoxComponent)
19669     getResizeEl : function(){
19670         return this.wrap;
19671     },
19672
19673     // private (for BoxComponent)
19674     getPositionEl : function(){
19675         return this.wrap;
19676     },
19677
19678     // private
19679     initEvents : function(){
19680         this.originalValue = this.getValue();
19681     },
19682
19683 //    /**
19684 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19685 //     * @method
19686 //     */
19687 //    markInvalid : Roo.emptyFn,
19688 //    /**
19689 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19690 //     * @method
19691 //     */
19692 //    clearInvalid : Roo.emptyFn,
19693
19694     setValue : function(v){
19695         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
19696         this.editorcore.pushValue();
19697     },
19698
19699      
19700     // private
19701     deferFocus : function(){
19702         this.focus.defer(10, this);
19703     },
19704
19705     // doc'ed in Field
19706     focus : function(){
19707         this.editorcore.focus();
19708         
19709     },
19710       
19711
19712     // private
19713     onDestroy : function(){
19714         
19715         
19716         
19717         if(this.rendered){
19718             
19719             for (var i =0; i < this.toolbars.length;i++) {
19720                 // fixme - ask toolbars for heights?
19721                 this.toolbars[i].onDestroy();
19722             }
19723             
19724             this.wrap.dom.innerHTML = '';
19725             this.wrap.remove();
19726         }
19727     },
19728
19729     // private
19730     onFirstFocus : function(){
19731         //Roo.log("onFirstFocus");
19732         this.editorcore.onFirstFocus();
19733          for (var i =0; i < this.toolbars.length;i++) {
19734             this.toolbars[i].onFirstFocus();
19735         }
19736         
19737     },
19738     
19739     // private
19740     syncValue : function()
19741     {   
19742         this.editorcore.syncValue();
19743     },
19744     
19745     pushValue : function()
19746     {   
19747         this.editorcore.pushValue();
19748     }
19749      
19750     
19751     // hide stuff that is not compatible
19752     /**
19753      * @event blur
19754      * @hide
19755      */
19756     /**
19757      * @event change
19758      * @hide
19759      */
19760     /**
19761      * @event focus
19762      * @hide
19763      */
19764     /**
19765      * @event specialkey
19766      * @hide
19767      */
19768     /**
19769      * @cfg {String} fieldClass @hide
19770      */
19771     /**
19772      * @cfg {String} focusClass @hide
19773      */
19774     /**
19775      * @cfg {String} autoCreate @hide
19776      */
19777     /**
19778      * @cfg {String} inputType @hide
19779      */
19780     /**
19781      * @cfg {String} invalidClass @hide
19782      */
19783     /**
19784      * @cfg {String} invalidText @hide
19785      */
19786     /**
19787      * @cfg {String} msgFx @hide
19788      */
19789     /**
19790      * @cfg {String} validateOnBlur @hide
19791      */
19792 });
19793  
19794     
19795    
19796    
19797    
19798       
19799 Roo.namespace('Roo.bootstrap.htmleditor');
19800 /**
19801  * @class Roo.bootstrap.HtmlEditorToolbar1
19802  * Basic Toolbar
19803  * 
19804  * Usage:
19805  *
19806  new Roo.bootstrap.HtmlEditor({
19807     ....
19808     toolbars : [
19809         new Roo.bootstrap.HtmlEditorToolbar1({
19810             disable : { fonts: 1 , format: 1, ..., ... , ...],
19811             btns : [ .... ]
19812         })
19813     }
19814      
19815  * 
19816  * @cfg {Object} disable List of elements to disable..
19817  * @cfg {Array} btns List of additional buttons.
19818  * 
19819  * 
19820  * NEEDS Extra CSS? 
19821  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
19822  */
19823  
19824 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
19825 {
19826     
19827     Roo.apply(this, config);
19828     
19829     // default disabled, based on 'good practice'..
19830     this.disable = this.disable || {};
19831     Roo.applyIf(this.disable, {
19832         fontSize : true,
19833         colors : true,
19834         specialElements : true
19835     });
19836     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
19837     
19838     this.editor = config.editor;
19839     this.editorcore = config.editor.editorcore;
19840     
19841     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
19842     
19843     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
19844     // dont call parent... till later.
19845 }
19846 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
19847      
19848     bar : true,
19849     
19850     editor : false,
19851     editorcore : false,
19852     
19853     
19854     formats : [
19855         "p" ,  
19856         "h1","h2","h3","h4","h5","h6", 
19857         "pre", "code", 
19858         "abbr", "acronym", "address", "cite", "samp", "var",
19859         'div','span'
19860     ],
19861     
19862     onRender : function(ct, position)
19863     {
19864        // Roo.log("Call onRender: " + this.xtype);
19865         
19866        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
19867        Roo.log(this.el);
19868        this.el.dom.style.marginBottom = '0';
19869        var _this = this;
19870        var editorcore = this.editorcore;
19871        var editor= this.editor;
19872        
19873        var children = [];
19874        var btn = function(id,cmd , toggle, handler){
19875        
19876             var  event = toggle ? 'toggle' : 'click';
19877        
19878             var a = {
19879                 size : 'sm',
19880                 xtype: 'Button',
19881                 xns: Roo.bootstrap,
19882                 glyphicon : id,
19883                 cmd : id || cmd,
19884                 enableToggle:toggle !== false,
19885                 //html : 'submit'
19886                 pressed : toggle ? false : null,
19887                 listeners : {}
19888             }
19889             a.listeners[toggle ? 'toggle' : 'click'] = function() {
19890                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
19891             }
19892             children.push(a);
19893             return a;
19894        }
19895         
19896         var style = {
19897                 xtype: 'Button',
19898                 size : 'sm',
19899                 xns: Roo.bootstrap,
19900                 glyphicon : 'font',
19901                 //html : 'submit'
19902                 menu : {
19903                     xtype: 'Menu',
19904                     xns: Roo.bootstrap,
19905                     items:  []
19906                 }
19907         };
19908         Roo.each(this.formats, function(f) {
19909             style.menu.items.push({
19910                 xtype :'MenuItem',
19911                 xns: Roo.bootstrap,
19912                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
19913                 tagname : f,
19914                 listeners : {
19915                     click : function()
19916                     {
19917                         editorcore.insertTag(this.tagname);
19918                         editor.focus();
19919                     }
19920                 }
19921                 
19922             });
19923         });
19924          children.push(style);   
19925             
19926             
19927         btn('bold',false,true);
19928         btn('italic',false,true);
19929         btn('align-left', 'justifyleft',true);
19930         btn('align-center', 'justifycenter',true);
19931         btn('align-right' , 'justifyright',true);
19932         btn('link', false, false, function(btn) {
19933             //Roo.log("create link?");
19934             var url = prompt(this.createLinkText, this.defaultLinkValue);
19935             if(url && url != 'http:/'+'/'){
19936                 this.editorcore.relayCmd('createlink', url);
19937             }
19938         }),
19939         btn('list','insertunorderedlist',true);
19940         btn('pencil', false,true, function(btn){
19941                 Roo.log(this);
19942                 
19943                 this.toggleSourceEdit(btn.pressed);
19944         });
19945         /*
19946         var cog = {
19947                 xtype: 'Button',
19948                 size : 'sm',
19949                 xns: Roo.bootstrap,
19950                 glyphicon : 'cog',
19951                 //html : 'submit'
19952                 menu : {
19953                     xtype: 'Menu',
19954                     xns: Roo.bootstrap,
19955                     items:  []
19956                 }
19957         };
19958         
19959         cog.menu.items.push({
19960             xtype :'MenuItem',
19961             xns: Roo.bootstrap,
19962             html : Clean styles,
19963             tagname : f,
19964             listeners : {
19965                 click : function()
19966                 {
19967                     editorcore.insertTag(this.tagname);
19968                     editor.focus();
19969                 }
19970             }
19971             
19972         });
19973        */
19974         
19975          
19976        this.xtype = 'NavSimplebar';
19977         
19978         for(var i=0;i< children.length;i++) {
19979             
19980             this.buttons.add(this.addxtypeChild(children[i]));
19981             
19982         }
19983         
19984         editor.on('editorevent', this.updateToolbar, this);
19985     },
19986     onBtnClick : function(id)
19987     {
19988        this.editorcore.relayCmd(id);
19989        this.editorcore.focus();
19990     },
19991     
19992     /**
19993      * Protected method that will not generally be called directly. It triggers
19994      * a toolbar update by reading the markup state of the current selection in the editor.
19995      */
19996     updateToolbar: function(){
19997
19998         if(!this.editorcore.activated){
19999             this.editor.onFirstFocus(); // is this neeed?
20000             return;
20001         }
20002
20003         var btns = this.buttons; 
20004         var doc = this.editorcore.doc;
20005         btns.get('bold').setActive(doc.queryCommandState('bold'));
20006         btns.get('italic').setActive(doc.queryCommandState('italic'));
20007         //btns.get('underline').setActive(doc.queryCommandState('underline'));
20008         
20009         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
20010         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
20011         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
20012         
20013         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
20014         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
20015          /*
20016         
20017         var ans = this.editorcore.getAllAncestors();
20018         if (this.formatCombo) {
20019             
20020             
20021             var store = this.formatCombo.store;
20022             this.formatCombo.setValue("");
20023             for (var i =0; i < ans.length;i++) {
20024                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
20025                     // select it..
20026                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
20027                     break;
20028                 }
20029             }
20030         }
20031         
20032         
20033         
20034         // hides menus... - so this cant be on a menu...
20035         Roo.bootstrap.MenuMgr.hideAll();
20036         */
20037         Roo.bootstrap.MenuMgr.hideAll();
20038         //this.editorsyncValue();
20039     },
20040     onFirstFocus: function() {
20041         this.buttons.each(function(item){
20042            item.enable();
20043         });
20044     },
20045     toggleSourceEdit : function(sourceEditMode){
20046         
20047           
20048         if(sourceEditMode){
20049             Roo.log("disabling buttons");
20050            this.buttons.each( function(item){
20051                 if(item.cmd != 'pencil'){
20052                     item.disable();
20053                 }
20054             });
20055           
20056         }else{
20057             Roo.log("enabling buttons");
20058             if(this.editorcore.initialized){
20059                 this.buttons.each( function(item){
20060                     item.enable();
20061                 });
20062             }
20063             
20064         }
20065         Roo.log("calling toggole on editor");
20066         // tell the editor that it's been pressed..
20067         this.editor.toggleSourceEdit(sourceEditMode);
20068        
20069     }
20070 });
20071
20072
20073
20074
20075
20076 /**
20077  * @class Roo.bootstrap.Table.AbstractSelectionModel
20078  * @extends Roo.util.Observable
20079  * Abstract base class for grid SelectionModels.  It provides the interface that should be
20080  * implemented by descendant classes.  This class should not be directly instantiated.
20081  * @constructor
20082  */
20083 Roo.bootstrap.Table.AbstractSelectionModel = function(){
20084     this.locked = false;
20085     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
20086 };
20087
20088
20089 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
20090     /** @ignore Called by the grid automatically. Do not call directly. */
20091     init : function(grid){
20092         this.grid = grid;
20093         this.initEvents();
20094     },
20095
20096     /**
20097      * Locks the selections.
20098      */
20099     lock : function(){
20100         this.locked = true;
20101     },
20102
20103     /**
20104      * Unlocks the selections.
20105      */
20106     unlock : function(){
20107         this.locked = false;
20108     },
20109
20110     /**
20111      * Returns true if the selections are locked.
20112      * @return {Boolean}
20113      */
20114     isLocked : function(){
20115         return this.locked;
20116     }
20117 });
20118 /**
20119  * @extends Roo.bootstrap.Table.AbstractSelectionModel
20120  * @class Roo.bootstrap.Table.RowSelectionModel
20121  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
20122  * It supports multiple selections and keyboard selection/navigation. 
20123  * @constructor
20124  * @param {Object} config
20125  */
20126
20127 Roo.bootstrap.Table.RowSelectionModel = function(config){
20128     Roo.apply(this, config);
20129     this.selections = new Roo.util.MixedCollection(false, function(o){
20130         return o.id;
20131     });
20132
20133     this.last = false;
20134     this.lastActive = false;
20135
20136     this.addEvents({
20137         /**
20138              * @event selectionchange
20139              * Fires when the selection changes
20140              * @param {SelectionModel} this
20141              */
20142             "selectionchange" : true,
20143         /**
20144              * @event afterselectionchange
20145              * Fires after the selection changes (eg. by key press or clicking)
20146              * @param {SelectionModel} this
20147              */
20148             "afterselectionchange" : true,
20149         /**
20150              * @event beforerowselect
20151              * Fires when a row is selected being selected, return false to cancel.
20152              * @param {SelectionModel} this
20153              * @param {Number} rowIndex The selected index
20154              * @param {Boolean} keepExisting False if other selections will be cleared
20155              */
20156             "beforerowselect" : true,
20157         /**
20158              * @event rowselect
20159              * Fires when a row is selected.
20160              * @param {SelectionModel} this
20161              * @param {Number} rowIndex The selected index
20162              * @param {Roo.data.Record} r The record
20163              */
20164             "rowselect" : true,
20165         /**
20166              * @event rowdeselect
20167              * Fires when a row is deselected.
20168              * @param {SelectionModel} this
20169              * @param {Number} rowIndex The selected index
20170              */
20171         "rowdeselect" : true
20172     });
20173     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
20174     this.locked = false;
20175 };
20176
20177 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
20178     /**
20179      * @cfg {Boolean} singleSelect
20180      * True to allow selection of only one row at a time (defaults to false)
20181      */
20182     singleSelect : false,
20183
20184     // private
20185     initEvents : function(){
20186
20187         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
20188             this.grid.on("mousedown", this.handleMouseDown, this);
20189         }else{ // allow click to work like normal
20190             this.grid.on("rowclick", this.handleDragableRowClick, this);
20191         }
20192
20193         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
20194             "up" : function(e){
20195                 if(!e.shiftKey){
20196                     this.selectPrevious(e.shiftKey);
20197                 }else if(this.last !== false && this.lastActive !== false){
20198                     var last = this.last;
20199                     this.selectRange(this.last,  this.lastActive-1);
20200                     this.grid.getView().focusRow(this.lastActive);
20201                     if(last !== false){
20202                         this.last = last;
20203                     }
20204                 }else{
20205                     this.selectFirstRow();
20206                 }
20207                 this.fireEvent("afterselectionchange", this);
20208             },
20209             "down" : function(e){
20210                 if(!e.shiftKey){
20211                     this.selectNext(e.shiftKey);
20212                 }else if(this.last !== false && this.lastActive !== false){
20213                     var last = this.last;
20214                     this.selectRange(this.last,  this.lastActive+1);
20215                     this.grid.getView().focusRow(this.lastActive);
20216                     if(last !== false){
20217                         this.last = last;
20218                     }
20219                 }else{
20220                     this.selectFirstRow();
20221                 }
20222                 this.fireEvent("afterselectionchange", this);
20223             },
20224             scope: this
20225         });
20226
20227         var view = this.grid.view;
20228         view.on("refresh", this.onRefresh, this);
20229         view.on("rowupdated", this.onRowUpdated, this);
20230         view.on("rowremoved", this.onRemove, this);
20231     },
20232
20233     // private
20234     onRefresh : function(){
20235         var ds = this.grid.dataSource, i, v = this.grid.view;
20236         var s = this.selections;
20237         s.each(function(r){
20238             if((i = ds.indexOfId(r.id)) != -1){
20239                 v.onRowSelect(i);
20240             }else{
20241                 s.remove(r);
20242             }
20243         });
20244     },
20245
20246     // private
20247     onRemove : function(v, index, r){
20248         this.selections.remove(r);
20249     },
20250
20251     // private
20252     onRowUpdated : function(v, index, r){
20253         if(this.isSelected(r)){
20254             v.onRowSelect(index);
20255         }
20256     },
20257
20258     /**
20259      * Select records.
20260      * @param {Array} records The records to select
20261      * @param {Boolean} keepExisting (optional) True to keep existing selections
20262      */
20263     selectRecords : function(records, keepExisting){
20264         if(!keepExisting){
20265             this.clearSelections();
20266         }
20267         var ds = this.grid.dataSource;
20268         for(var i = 0, len = records.length; i < len; i++){
20269             this.selectRow(ds.indexOf(records[i]), true);
20270         }
20271     },
20272
20273     /**
20274      * Gets the number of selected rows.
20275      * @return {Number}
20276      */
20277     getCount : function(){
20278         return this.selections.length;
20279     },
20280
20281     /**
20282      * Selects the first row in the grid.
20283      */
20284     selectFirstRow : function(){
20285         this.selectRow(0);
20286     },
20287
20288     /**
20289      * Select the last row.
20290      * @param {Boolean} keepExisting (optional) True to keep existing selections
20291      */
20292     selectLastRow : function(keepExisting){
20293         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
20294     },
20295
20296     /**
20297      * Selects the row immediately following the last selected row.
20298      * @param {Boolean} keepExisting (optional) True to keep existing selections
20299      */
20300     selectNext : function(keepExisting){
20301         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
20302             this.selectRow(this.last+1, keepExisting);
20303             this.grid.getView().focusRow(this.last);
20304         }
20305     },
20306
20307     /**
20308      * Selects the row that precedes the last selected row.
20309      * @param {Boolean} keepExisting (optional) True to keep existing selections
20310      */
20311     selectPrevious : function(keepExisting){
20312         if(this.last){
20313             this.selectRow(this.last-1, keepExisting);
20314             this.grid.getView().focusRow(this.last);
20315         }
20316     },
20317
20318     /**
20319      * Returns the selected records
20320      * @return {Array} Array of selected records
20321      */
20322     getSelections : function(){
20323         return [].concat(this.selections.items);
20324     },
20325
20326     /**
20327      * Returns the first selected record.
20328      * @return {Record}
20329      */
20330     getSelected : function(){
20331         return this.selections.itemAt(0);
20332     },
20333
20334
20335     /**
20336      * Clears all selections.
20337      */
20338     clearSelections : function(fast){
20339         if(this.locked) return;
20340         if(fast !== true){
20341             var ds = this.grid.dataSource;
20342             var s = this.selections;
20343             s.each(function(r){
20344                 this.deselectRow(ds.indexOfId(r.id));
20345             }, this);
20346             s.clear();
20347         }else{
20348             this.selections.clear();
20349         }
20350         this.last = false;
20351     },
20352
20353
20354     /**
20355      * Selects all rows.
20356      */
20357     selectAll : function(){
20358         if(this.locked) return;
20359         this.selections.clear();
20360         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
20361             this.selectRow(i, true);
20362         }
20363     },
20364
20365     /**
20366      * Returns True if there is a selection.
20367      * @return {Boolean}
20368      */
20369     hasSelection : function(){
20370         return this.selections.length > 0;
20371     },
20372
20373     /**
20374      * Returns True if the specified row is selected.
20375      * @param {Number/Record} record The record or index of the record to check
20376      * @return {Boolean}
20377      */
20378     isSelected : function(index){
20379         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
20380         return (r && this.selections.key(r.id) ? true : false);
20381     },
20382
20383     /**
20384      * Returns True if the specified record id is selected.
20385      * @param {String} id The id of record to check
20386      * @return {Boolean}
20387      */
20388     isIdSelected : function(id){
20389         return (this.selections.key(id) ? true : false);
20390     },
20391
20392     // private
20393     handleMouseDown : function(e, t){
20394         var view = this.grid.getView(), rowIndex;
20395         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
20396             return;
20397         };
20398         if(e.shiftKey && this.last !== false){
20399             var last = this.last;
20400             this.selectRange(last, rowIndex, e.ctrlKey);
20401             this.last = last; // reset the last
20402             view.focusRow(rowIndex);
20403         }else{
20404             var isSelected = this.isSelected(rowIndex);
20405             if(e.button !== 0 && isSelected){
20406                 view.focusRow(rowIndex);
20407             }else if(e.ctrlKey && isSelected){
20408                 this.deselectRow(rowIndex);
20409             }else if(!isSelected){
20410                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
20411                 view.focusRow(rowIndex);
20412             }
20413         }
20414         this.fireEvent("afterselectionchange", this);
20415     },
20416     // private
20417     handleDragableRowClick :  function(grid, rowIndex, e) 
20418     {
20419         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
20420             this.selectRow(rowIndex, false);
20421             grid.view.focusRow(rowIndex);
20422              this.fireEvent("afterselectionchange", this);
20423         }
20424     },
20425     
20426     /**
20427      * Selects multiple rows.
20428      * @param {Array} rows Array of the indexes of the row to select
20429      * @param {Boolean} keepExisting (optional) True to keep existing selections
20430      */
20431     selectRows : function(rows, keepExisting){
20432         if(!keepExisting){
20433             this.clearSelections();
20434         }
20435         for(var i = 0, len = rows.length; i < len; i++){
20436             this.selectRow(rows[i], true);
20437         }
20438     },
20439
20440     /**
20441      * Selects a range of rows. All rows in between startRow and endRow are also selected.
20442      * @param {Number} startRow The index of the first row in the range
20443      * @param {Number} endRow The index of the last row in the range
20444      * @param {Boolean} keepExisting (optional) True to retain existing selections
20445      */
20446     selectRange : function(startRow, endRow, keepExisting){
20447         if(this.locked) return;
20448         if(!keepExisting){
20449             this.clearSelections();
20450         }
20451         if(startRow <= endRow){
20452             for(var i = startRow; i <= endRow; i++){
20453                 this.selectRow(i, true);
20454             }
20455         }else{
20456             for(var i = startRow; i >= endRow; i--){
20457                 this.selectRow(i, true);
20458             }
20459         }
20460     },
20461
20462     /**
20463      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
20464      * @param {Number} startRow The index of the first row in the range
20465      * @param {Number} endRow The index of the last row in the range
20466      */
20467     deselectRange : function(startRow, endRow, preventViewNotify){
20468         if(this.locked) return;
20469         for(var i = startRow; i <= endRow; i++){
20470             this.deselectRow(i, preventViewNotify);
20471         }
20472     },
20473
20474     /**
20475      * Selects a row.
20476      * @param {Number} row The index of the row to select
20477      * @param {Boolean} keepExisting (optional) True to keep existing selections
20478      */
20479     selectRow : function(index, keepExisting, preventViewNotify){
20480         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
20481         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
20482             if(!keepExisting || this.singleSelect){
20483                 this.clearSelections();
20484             }
20485             var r = this.grid.dataSource.getAt(index);
20486             this.selections.add(r);
20487             this.last = this.lastActive = index;
20488             if(!preventViewNotify){
20489                 this.grid.getView().onRowSelect(index);
20490             }
20491             this.fireEvent("rowselect", this, index, r);
20492             this.fireEvent("selectionchange", this);
20493         }
20494     },
20495
20496     /**
20497      * Deselects a row.
20498      * @param {Number} row The index of the row to deselect
20499      */
20500     deselectRow : function(index, preventViewNotify){
20501         if(this.locked) return;
20502         if(this.last == index){
20503             this.last = false;
20504         }
20505         if(this.lastActive == index){
20506             this.lastActive = false;
20507         }
20508         var r = this.grid.dataSource.getAt(index);
20509         this.selections.remove(r);
20510         if(!preventViewNotify){
20511             this.grid.getView().onRowDeselect(index);
20512         }
20513         this.fireEvent("rowdeselect", this, index);
20514         this.fireEvent("selectionchange", this);
20515     },
20516
20517     // private
20518     restoreLast : function(){
20519         if(this._last){
20520             this.last = this._last;
20521         }
20522     },
20523
20524     // private
20525     acceptsNav : function(row, col, cm){
20526         return !cm.isHidden(col) && cm.isCellEditable(col, row);
20527     },
20528
20529     // private
20530     onEditorKey : function(field, e){
20531         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
20532         if(k == e.TAB){
20533             e.stopEvent();
20534             ed.completeEdit();
20535             if(e.shiftKey){
20536                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
20537             }else{
20538                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
20539             }
20540         }else if(k == e.ENTER && !e.ctrlKey){
20541             e.stopEvent();
20542             ed.completeEdit();
20543             if(e.shiftKey){
20544                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
20545             }else{
20546                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
20547             }
20548         }else if(k == e.ESC){
20549             ed.cancelEdit();
20550         }
20551         if(newCell){
20552             g.startEditing(newCell[0], newCell[1]);
20553         }
20554     }
20555 });/*
20556  * Based on:
20557  * Ext JS Library 1.1.1
20558  * Copyright(c) 2006-2007, Ext JS, LLC.
20559  *
20560  * Originally Released Under LGPL - original licence link has changed is not relivant.
20561  *
20562  * Fork - LGPL
20563  * <script type="text/javascript">
20564  */
20565  
20566 /**
20567  * @class Roo.bootstrap.PagingToolbar
20568  * @extends Roo.Row
20569  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
20570  * @constructor
20571  * Create a new PagingToolbar
20572  * @param {Object} config The config object
20573  */
20574 Roo.bootstrap.PagingToolbar = function(config)
20575 {
20576     // old args format still supported... - xtype is prefered..
20577         // created from xtype...
20578     var ds = config.dataSource;
20579     this.toolbarItems = [];
20580     if (config.items) {
20581         this.toolbarItems = config.items;
20582 //        config.items = [];
20583     }
20584     
20585     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
20586     this.ds = ds;
20587     this.cursor = 0;
20588     if (ds) { 
20589         this.bind(ds);
20590     }
20591     
20592     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
20593     
20594 };
20595
20596 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
20597     /**
20598      * @cfg {Roo.data.Store} dataSource
20599      * The underlying data store providing the paged data
20600      */
20601     /**
20602      * @cfg {String/HTMLElement/Element} container
20603      * container The id or element that will contain the toolbar
20604      */
20605     /**
20606      * @cfg {Boolean} displayInfo
20607      * True to display the displayMsg (defaults to false)
20608      */
20609     /**
20610      * @cfg {Number} pageSize
20611      * The number of records to display per page (defaults to 20)
20612      */
20613     pageSize: 20,
20614     /**
20615      * @cfg {String} displayMsg
20616      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
20617      */
20618     displayMsg : 'Displaying {0} - {1} of {2}',
20619     /**
20620      * @cfg {String} emptyMsg
20621      * The message to display when no records are found (defaults to "No data to display")
20622      */
20623     emptyMsg : 'No data to display',
20624     /**
20625      * Customizable piece of the default paging text (defaults to "Page")
20626      * @type String
20627      */
20628     beforePageText : "Page",
20629     /**
20630      * Customizable piece of the default paging text (defaults to "of %0")
20631      * @type String
20632      */
20633     afterPageText : "of {0}",
20634     /**
20635      * Customizable piece of the default paging text (defaults to "First Page")
20636      * @type String
20637      */
20638     firstText : "First Page",
20639     /**
20640      * Customizable piece of the default paging text (defaults to "Previous Page")
20641      * @type String
20642      */
20643     prevText : "Previous Page",
20644     /**
20645      * Customizable piece of the default paging text (defaults to "Next Page")
20646      * @type String
20647      */
20648     nextText : "Next Page",
20649     /**
20650      * Customizable piece of the default paging text (defaults to "Last Page")
20651      * @type String
20652      */
20653     lastText : "Last Page",
20654     /**
20655      * Customizable piece of the default paging text (defaults to "Refresh")
20656      * @type String
20657      */
20658     refreshText : "Refresh",
20659
20660     buttons : false,
20661     // private
20662     onRender : function(ct, position) 
20663     {
20664         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
20665         this.navgroup.parentId = this.id;
20666         this.navgroup.onRender(this.el, null);
20667         // add the buttons to the navgroup
20668         
20669         if(this.displayInfo){
20670             Roo.log(this.el.select('ul.navbar-nav',true).first());
20671             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
20672             this.displayEl = this.el.select('.x-paging-info', true).first();
20673 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
20674 //            this.displayEl = navel.el.select('span',true).first();
20675         }
20676         
20677         var _this = this;
20678         
20679         if(this.buttons){
20680             Roo.each(_this.buttons, function(e){
20681                Roo.factory(e).onRender(_this.el, null);
20682             });
20683         }
20684             
20685         Roo.each(_this.toolbarItems, function(e) {
20686             _this.navgroup.addItem(e);
20687         });
20688         
20689         
20690         this.first = this.navgroup.addItem({
20691             tooltip: this.firstText,
20692             cls: "prev",
20693             icon : 'fa fa-backward',
20694             disabled: true,
20695             preventDefault: true,
20696             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
20697         });
20698         
20699         this.prev =  this.navgroup.addItem({
20700             tooltip: this.prevText,
20701             cls: "prev",
20702             icon : 'fa fa-step-backward',
20703             disabled: true,
20704             preventDefault: true,
20705             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
20706         });
20707     //this.addSeparator();
20708         
20709         
20710         var field = this.navgroup.addItem( {
20711             tagtype : 'span',
20712             cls : 'x-paging-position',
20713             
20714             html : this.beforePageText  +
20715                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
20716                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
20717          } ); //?? escaped?
20718         
20719         this.field = field.el.select('input', true).first();
20720         this.field.on("keydown", this.onPagingKeydown, this);
20721         this.field.on("focus", function(){this.dom.select();});
20722     
20723     
20724         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
20725         //this.field.setHeight(18);
20726         //this.addSeparator();
20727         this.next = this.navgroup.addItem({
20728             tooltip: this.nextText,
20729             cls: "next",
20730             html : ' <i class="fa fa-step-forward">',
20731             disabled: true,
20732             preventDefault: true,
20733             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
20734         });
20735         this.last = this.navgroup.addItem({
20736             tooltip: this.lastText,
20737             icon : 'fa fa-forward',
20738             cls: "next",
20739             disabled: true,
20740             preventDefault: true,
20741             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
20742         });
20743     //this.addSeparator();
20744         this.loading = this.navgroup.addItem({
20745             tooltip: this.refreshText,
20746             icon: 'fa fa-refresh',
20747             preventDefault: true,
20748             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
20749         });
20750
20751     },
20752
20753     // private
20754     updateInfo : function(){
20755         if(this.displayEl){
20756             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
20757             var msg = count == 0 ?
20758                 this.emptyMsg :
20759                 String.format(
20760                     this.displayMsg,
20761                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
20762                 );
20763             this.displayEl.update(msg);
20764         }
20765     },
20766
20767     // private
20768     onLoad : function(ds, r, o){
20769        this.cursor = o.params ? o.params.start : 0;
20770        var d = this.getPageData(),
20771             ap = d.activePage,
20772             ps = d.pages;
20773         
20774        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
20775        this.field.dom.value = ap;
20776        this.first.setDisabled(ap == 1);
20777        this.prev.setDisabled(ap == 1);
20778        this.next.setDisabled(ap == ps);
20779        this.last.setDisabled(ap == ps);
20780        this.loading.enable();
20781        this.updateInfo();
20782     },
20783
20784     // private
20785     getPageData : function(){
20786         var total = this.ds.getTotalCount();
20787         return {
20788             total : total,
20789             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
20790             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
20791         };
20792     },
20793
20794     // private
20795     onLoadError : function(){
20796         this.loading.enable();
20797     },
20798
20799     // private
20800     onPagingKeydown : function(e){
20801         var k = e.getKey();
20802         var d = this.getPageData();
20803         if(k == e.RETURN){
20804             var v = this.field.dom.value, pageNum;
20805             if(!v || isNaN(pageNum = parseInt(v, 10))){
20806                 this.field.dom.value = d.activePage;
20807                 return;
20808             }
20809             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
20810             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20811             e.stopEvent();
20812         }
20813         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))
20814         {
20815           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
20816           this.field.dom.value = pageNum;
20817           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
20818           e.stopEvent();
20819         }
20820         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20821         {
20822           var v = this.field.dom.value, pageNum; 
20823           var increment = (e.shiftKey) ? 10 : 1;
20824           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20825             increment *= -1;
20826           if(!v || isNaN(pageNum = parseInt(v, 10))) {
20827             this.field.dom.value = d.activePage;
20828             return;
20829           }
20830           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
20831           {
20832             this.field.dom.value = parseInt(v, 10) + increment;
20833             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
20834             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20835           }
20836           e.stopEvent();
20837         }
20838     },
20839
20840     // private
20841     beforeLoad : function(){
20842         if(this.loading){
20843             this.loading.disable();
20844         }
20845     },
20846
20847     // private
20848     onClick : function(which){
20849         
20850         var ds = this.ds;
20851         if (!ds) {
20852             return;
20853         }
20854         
20855         switch(which){
20856             case "first":
20857                 ds.load({params:{start: 0, limit: this.pageSize}});
20858             break;
20859             case "prev":
20860                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
20861             break;
20862             case "next":
20863                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
20864             break;
20865             case "last":
20866                 var total = ds.getTotalCount();
20867                 var extra = total % this.pageSize;
20868                 var lastStart = extra ? (total - extra) : total-this.pageSize;
20869                 ds.load({params:{start: lastStart, limit: this.pageSize}});
20870             break;
20871             case "refresh":
20872                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
20873             break;
20874         }
20875     },
20876
20877     /**
20878      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
20879      * @param {Roo.data.Store} store The data store to unbind
20880      */
20881     unbind : function(ds){
20882         ds.un("beforeload", this.beforeLoad, this);
20883         ds.un("load", this.onLoad, this);
20884         ds.un("loadexception", this.onLoadError, this);
20885         ds.un("remove", this.updateInfo, this);
20886         ds.un("add", this.updateInfo, this);
20887         this.ds = undefined;
20888     },
20889
20890     /**
20891      * Binds the paging toolbar to the specified {@link Roo.data.Store}
20892      * @param {Roo.data.Store} store The data store to bind
20893      */
20894     bind : function(ds){
20895         ds.on("beforeload", this.beforeLoad, this);
20896         ds.on("load", this.onLoad, this);
20897         ds.on("loadexception", this.onLoadError, this);
20898         ds.on("remove", this.updateInfo, this);
20899         ds.on("add", this.updateInfo, this);
20900         this.ds = ds;
20901     }
20902 });/*
20903  * - LGPL
20904  *
20905  * element
20906  * 
20907  */
20908
20909 /**
20910  * @class Roo.bootstrap.MessageBar
20911  * @extends Roo.bootstrap.Component
20912  * Bootstrap MessageBar class
20913  * @cfg {String} html contents of the MessageBar
20914  * @cfg {String} weight (info | success | warning | danger) default info
20915  * @cfg {String} beforeClass insert the bar before the given class
20916  * @cfg {Boolean} closable (true | false) default false
20917  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
20918  * 
20919  * @constructor
20920  * Create a new Element
20921  * @param {Object} config The config object
20922  */
20923
20924 Roo.bootstrap.MessageBar = function(config){
20925     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
20926 };
20927
20928 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
20929     
20930     html: '',
20931     weight: 'info',
20932     closable: false,
20933     fixed: false,
20934     beforeClass: 'bootstrap-sticky-wrap',
20935     
20936     getAutoCreate : function(){
20937         
20938         var cfg = {
20939             tag: 'div',
20940             cls: 'alert alert-dismissable alert-' + this.weight,
20941             cn: [
20942                 {
20943                     tag: 'span',
20944                     cls: 'message',
20945                     html: this.html || ''
20946                 }
20947             ]
20948         }
20949         
20950         if(this.fixed){
20951             cfg.cls += ' alert-messages-fixed';
20952         }
20953         
20954         if(this.closable){
20955             cfg.cn.push({
20956                 tag: 'button',
20957                 cls: 'close',
20958                 html: 'x'
20959             });
20960         }
20961         
20962         return cfg;
20963     },
20964     
20965     onRender : function(ct, position)
20966     {
20967         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20968         
20969         if(!this.el){
20970             var cfg = Roo.apply({},  this.getAutoCreate());
20971             cfg.id = Roo.id();
20972             
20973             if (this.cls) {
20974                 cfg.cls += ' ' + this.cls;
20975             }
20976             if (this.style) {
20977                 cfg.style = this.style;
20978             }
20979             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
20980             
20981             this.el.setVisibilityMode(Roo.Element.DISPLAY);
20982         }
20983         
20984         this.el.select('>button.close').on('click', this.hide, this);
20985         
20986     },
20987     
20988     show : function()
20989     {
20990         if (!this.rendered) {
20991             this.render();
20992         }
20993         
20994         this.el.show();
20995         
20996         this.fireEvent('show', this);
20997         
20998     },
20999     
21000     hide : function()
21001     {
21002         if (!this.rendered) {
21003             this.render();
21004         }
21005         
21006         this.el.hide();
21007         
21008         this.fireEvent('hide', this);
21009     },
21010     
21011     update : function()
21012     {
21013 //        var e = this.el.dom.firstChild;
21014 //        
21015 //        if(this.closable){
21016 //            e = e.nextSibling;
21017 //        }
21018 //        
21019 //        e.data = this.html || '';
21020
21021         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
21022     }
21023    
21024 });
21025
21026  
21027
21028      /*
21029  * - LGPL
21030  *
21031  * Graph
21032  * 
21033  */
21034
21035
21036 /**
21037  * @class Roo.bootstrap.Graph
21038  * @extends Roo.bootstrap.Component
21039  * Bootstrap Graph class
21040 > Prameters
21041  -sm {number} sm 4
21042  -md {number} md 5
21043  @cfg {String} graphtype  bar | vbar | pie
21044  @cfg {number} g_x coodinator | centre x (pie)
21045  @cfg {number} g_y coodinator | centre y (pie)
21046  @cfg {number} g_r radius (pie)
21047  @cfg {number} g_height height of the chart (respected by all elements in the set)
21048  @cfg {number} g_width width of the chart (respected by all elements in the set)
21049  @cfg {Object} title The title of the chart
21050     
21051  -{Array}  values
21052  -opts (object) options for the chart 
21053      o {
21054      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
21055      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
21056      o vgutter (number)
21057      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.
21058      o stacked (boolean) whether or not to tread values as in a stacked bar chart
21059      o to
21060      o stretch (boolean)
21061      o }
21062  -opts (object) options for the pie
21063      o{
21064      o cut
21065      o startAngle (number)
21066      o endAngle (number)
21067      } 
21068  *
21069  * @constructor
21070  * Create a new Input
21071  * @param {Object} config The config object
21072  */
21073
21074 Roo.bootstrap.Graph = function(config){
21075     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
21076     
21077     this.addEvents({
21078         // img events
21079         /**
21080          * @event click
21081          * The img click event for the img.
21082          * @param {Roo.EventObject} e
21083          */
21084         "click" : true
21085     });
21086 };
21087
21088 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
21089     
21090     sm: 4,
21091     md: 5,
21092     graphtype: 'bar',
21093     g_height: 250,
21094     g_width: 400,
21095     g_x: 50,
21096     g_y: 50,
21097     g_r: 30,
21098     opts:{
21099         //g_colors: this.colors,
21100         g_type: 'soft',
21101         g_gutter: '20%'
21102
21103     },
21104     title : false,
21105
21106     getAutoCreate : function(){
21107         
21108         var cfg = {
21109             tag: 'div',
21110             html : null
21111         }
21112         
21113         
21114         return  cfg;
21115     },
21116
21117     onRender : function(ct,position){
21118         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
21119         this.raphael = Raphael(this.el.dom);
21120         
21121                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21122                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21123                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21124                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
21125                 /*
21126                 r.text(160, 10, "Single Series Chart").attr(txtattr);
21127                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
21128                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
21129                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
21130                 
21131                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
21132                 r.barchart(330, 10, 300, 220, data1);
21133                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
21134                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
21135                 */
21136                 
21137                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21138                 // r.barchart(30, 30, 560, 250,  xdata, {
21139                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
21140                 //     axis : "0 0 1 1",
21141                 //     axisxlabels :  xdata
21142                 //     //yvalues : cols,
21143                    
21144                 // });
21145 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21146 //        
21147 //        this.load(null,xdata,{
21148 //                axis : "0 0 1 1",
21149 //                axisxlabels :  xdata
21150 //                });
21151
21152     },
21153
21154     load : function(graphtype,xdata,opts){
21155         this.raphael.clear();
21156         if(!graphtype) {
21157             graphtype = this.graphtype;
21158         }
21159         if(!opts){
21160             opts = this.opts;
21161         }
21162         var r = this.raphael,
21163             fin = function () {
21164                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
21165             },
21166             fout = function () {
21167                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
21168             },
21169             pfin = function() {
21170                 this.sector.stop();
21171                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
21172
21173                 if (this.label) {
21174                     this.label[0].stop();
21175                     this.label[0].attr({ r: 7.5 });
21176                     this.label[1].attr({ "font-weight": 800 });
21177                 }
21178             },
21179             pfout = function() {
21180                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
21181
21182                 if (this.label) {
21183                     this.label[0].animate({ r: 5 }, 500, "bounce");
21184                     this.label[1].attr({ "font-weight": 400 });
21185                 }
21186             };
21187
21188         switch(graphtype){
21189             case 'bar':
21190                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21191                 break;
21192             case 'hbar':
21193                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21194                 break;
21195             case 'pie':
21196 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
21197 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
21198 //            
21199                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
21200                 
21201                 break;
21202
21203         }
21204         
21205         if(this.title){
21206             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
21207         }
21208         
21209     },
21210     
21211     setTitle: function(o)
21212     {
21213         this.title = o;
21214     },
21215     
21216     initEvents: function() {
21217         
21218         if(!this.href){
21219             this.el.on('click', this.onClick, this);
21220         }
21221     },
21222     
21223     onClick : function(e)
21224     {
21225         Roo.log('img onclick');
21226         this.fireEvent('click', this, e);
21227     }
21228    
21229 });
21230
21231  
21232 /*
21233  * - LGPL
21234  *
21235  * numberBox
21236  * 
21237  */
21238 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21239
21240 /**
21241  * @class Roo.bootstrap.dash.NumberBox
21242  * @extends Roo.bootstrap.Component
21243  * Bootstrap NumberBox class
21244  * @cfg {String} headline Box headline
21245  * @cfg {String} content Box content
21246  * @cfg {String} icon Box icon
21247  * @cfg {String} footer Footer text
21248  * @cfg {String} fhref Footer href
21249  * 
21250  * @constructor
21251  * Create a new NumberBox
21252  * @param {Object} config The config object
21253  */
21254
21255
21256 Roo.bootstrap.dash.NumberBox = function(config){
21257     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
21258     
21259 };
21260
21261 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
21262     
21263     headline : '',
21264     content : '',
21265     icon : '',
21266     footer : '',
21267     fhref : '',
21268     ficon : '',
21269     
21270     getAutoCreate : function(){
21271         
21272         var cfg = {
21273             tag : 'div',
21274             cls : 'small-box ',
21275             cn : [
21276                 {
21277                     tag : 'div',
21278                     cls : 'inner',
21279                     cn :[
21280                         {
21281                             tag : 'h3',
21282                             cls : 'roo-headline',
21283                             html : this.headline
21284                         },
21285                         {
21286                             tag : 'p',
21287                             cls : 'roo-content',
21288                             html : this.content
21289                         }
21290                     ]
21291                 }
21292             ]
21293         }
21294         
21295         if(this.icon){
21296             cfg.cn.push({
21297                 tag : 'div',
21298                 cls : 'icon',
21299                 cn :[
21300                     {
21301                         tag : 'i',
21302                         cls : 'ion ' + this.icon
21303                     }
21304                 ]
21305             });
21306         }
21307         
21308         if(this.footer){
21309             var footer = {
21310                 tag : 'a',
21311                 cls : 'small-box-footer',
21312                 href : this.fhref || '#',
21313                 html : this.footer
21314             };
21315             
21316             cfg.cn.push(footer);
21317             
21318         }
21319         
21320         return  cfg;
21321     },
21322
21323     onRender : function(ct,position){
21324         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
21325
21326
21327        
21328                 
21329     },
21330
21331     setHeadline: function (value)
21332     {
21333         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
21334     },
21335     
21336     setFooter: function (value, href)
21337     {
21338         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
21339         
21340         if(href){
21341             this.el.select('a.small-box-footer',true).first().attr('href', href);
21342         }
21343         
21344     },
21345
21346     setContent: function (value)
21347     {
21348         this.el.select('.roo-content',true).first().dom.innerHTML = value;
21349     },
21350
21351     initEvents: function() 
21352     {   
21353         
21354     }
21355     
21356 });
21357
21358  
21359 /*
21360  * - LGPL
21361  *
21362  * TabBox
21363  * 
21364  */
21365 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21366
21367 /**
21368  * @class Roo.bootstrap.dash.TabBox
21369  * @extends Roo.bootstrap.Component
21370  * Bootstrap TabBox class
21371  * @cfg {String} title Title of the TabBox
21372  * @cfg {String} icon Icon of the TabBox
21373  * @cfg {Boolean} showtabs (true|false) show the tabs default true
21374  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
21375  * 
21376  * @constructor
21377  * Create a new TabBox
21378  * @param {Object} config The config object
21379  */
21380
21381
21382 Roo.bootstrap.dash.TabBox = function(config){
21383     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
21384     this.addEvents({
21385         // raw events
21386         /**
21387          * @event addpane
21388          * When a pane is added
21389          * @param {Roo.bootstrap.dash.TabPane} pane
21390          */
21391         "addpane" : true,
21392         /**
21393          * @event activatepane
21394          * When a pane is activated
21395          * @param {Roo.bootstrap.dash.TabPane} pane
21396          */
21397         "activatepane" : true
21398         
21399          
21400     });
21401     
21402     this.panes = [];
21403 };
21404
21405 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
21406
21407     title : '',
21408     icon : false,
21409     showtabs : true,
21410     tabScrollable : false,
21411     
21412     getChildContainer : function()
21413     {
21414         return this.el.select('.tab-content', true).first();
21415     },
21416     
21417     getAutoCreate : function(){
21418         
21419         var header = {
21420             tag: 'li',
21421             cls: 'pull-left header',
21422             html: this.title,
21423             cn : []
21424         };
21425         
21426         if(this.icon){
21427             header.cn.push({
21428                 tag: 'i',
21429                 cls: 'fa ' + this.icon
21430             });
21431         }
21432         
21433         var h = {
21434             tag: 'ul',
21435             cls: 'nav nav-tabs pull-right',
21436             cn: [
21437                 header
21438             ]
21439         };
21440         
21441         if(this.tabScrollable){
21442             h = {
21443                 tag: 'div',
21444                 cls: 'tab-header',
21445                 cn: [
21446                     {
21447                         tag: 'ul',
21448                         cls: 'nav nav-tabs pull-right',
21449                         cn: [
21450                             header
21451                         ]
21452                     }
21453                 ]
21454             }
21455         }
21456         
21457         var cfg = {
21458             tag: 'div',
21459             cls: 'nav-tabs-custom',
21460             cn: [
21461                 h,
21462                 {
21463                     tag: 'div',
21464                     cls: 'tab-content no-padding',
21465                     cn: []
21466                 }
21467             ]
21468         }
21469
21470         return  cfg;
21471     },
21472     initEvents : function()
21473     {
21474         //Roo.log('add add pane handler');
21475         this.on('addpane', this.onAddPane, this);
21476     },
21477      /**
21478      * Updates the box title
21479      * @param {String} html to set the title to.
21480      */
21481     setTitle : function(value)
21482     {
21483         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
21484     },
21485     onAddPane : function(pane)
21486     {
21487         this.panes.push(pane);
21488         //Roo.log('addpane');
21489         //Roo.log(pane);
21490         // tabs are rendere left to right..
21491         if(!this.showtabs){
21492             return;
21493         }
21494         
21495         var ctr = this.el.select('.nav-tabs', true).first();
21496          
21497          
21498         var existing = ctr.select('.nav-tab',true);
21499         var qty = existing.getCount();;
21500         
21501         
21502         var tab = ctr.createChild({
21503             tag : 'li',
21504             cls : 'nav-tab' + (qty ? '' : ' active'),
21505             cn : [
21506                 {
21507                     tag : 'a',
21508                     href:'#',
21509                     html : pane.title
21510                 }
21511             ]
21512         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
21513         pane.tab = tab;
21514         
21515         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
21516         if (!qty) {
21517             pane.el.addClass('active');
21518         }
21519         
21520                 
21521     },
21522     onTabClick : function(ev,un,ob,pane)
21523     {
21524         //Roo.log('tab - prev default');
21525         ev.preventDefault();
21526         
21527         
21528         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
21529         pane.tab.addClass('active');
21530         //Roo.log(pane.title);
21531         this.getChildContainer().select('.tab-pane',true).removeClass('active');
21532         // technically we should have a deactivate event.. but maybe add later.
21533         // and it should not de-activate the selected tab...
21534         this.fireEvent('activatepane', pane);
21535         pane.el.addClass('active');
21536         pane.fireEvent('activate');
21537         
21538         
21539     },
21540     
21541     getActivePane : function()
21542     {
21543         var r = false;
21544         Roo.each(this.panes, function(p) {
21545             if(p.el.hasClass('active')){
21546                 r = p;
21547                 return false;
21548             }
21549             
21550             return;
21551         });
21552         
21553         return r;
21554     }
21555     
21556     
21557 });
21558
21559  
21560 /*
21561  * - LGPL
21562  *
21563  * Tab pane
21564  * 
21565  */
21566 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21567 /**
21568  * @class Roo.bootstrap.TabPane
21569  * @extends Roo.bootstrap.Component
21570  * Bootstrap TabPane class
21571  * @cfg {Boolean} active (false | true) Default false
21572  * @cfg {String} title title of panel
21573
21574  * 
21575  * @constructor
21576  * Create a new TabPane
21577  * @param {Object} config The config object
21578  */
21579
21580 Roo.bootstrap.dash.TabPane = function(config){
21581     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
21582     
21583     this.addEvents({
21584         // raw events
21585         /**
21586          * @event activate
21587          * When a pane is activated
21588          * @param {Roo.bootstrap.dash.TabPane} pane
21589          */
21590         "activate" : true
21591          
21592     });
21593 };
21594
21595 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
21596     
21597     active : false,
21598     title : '',
21599     
21600     // the tabBox that this is attached to.
21601     tab : false,
21602      
21603     getAutoCreate : function() 
21604     {
21605         var cfg = {
21606             tag: 'div',
21607             cls: 'tab-pane'
21608         }
21609         
21610         if(this.active){
21611             cfg.cls += ' active';
21612         }
21613         
21614         return cfg;
21615     },
21616     initEvents  : function()
21617     {
21618         //Roo.log('trigger add pane handler');
21619         this.parent().fireEvent('addpane', this)
21620     },
21621     
21622      /**
21623      * Updates the tab title 
21624      * @param {String} html to set the title to.
21625      */
21626     setTitle: function(str)
21627     {
21628         if (!this.tab) {
21629             return;
21630         }
21631         this.title = str;
21632         this.tab.select('a', true).first().dom.innerHTML = str;
21633         
21634     }
21635     
21636     
21637     
21638 });
21639
21640  
21641
21642
21643  /*
21644  * - LGPL
21645  *
21646  * menu
21647  * 
21648  */
21649 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21650
21651 /**
21652  * @class Roo.bootstrap.menu.Menu
21653  * @extends Roo.bootstrap.Component
21654  * Bootstrap Menu class - container for Menu
21655  * @cfg {String} html Text of the menu
21656  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
21657  * @cfg {String} icon Font awesome icon
21658  * @cfg {String} pos Menu align to (top | bottom) default bottom
21659  * 
21660  * 
21661  * @constructor
21662  * Create a new Menu
21663  * @param {Object} config The config object
21664  */
21665
21666
21667 Roo.bootstrap.menu.Menu = function(config){
21668     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
21669     
21670     this.addEvents({
21671         /**
21672          * @event beforeshow
21673          * Fires before this menu is displayed
21674          * @param {Roo.bootstrap.menu.Menu} this
21675          */
21676         beforeshow : true,
21677         /**
21678          * @event beforehide
21679          * Fires before this menu is hidden
21680          * @param {Roo.bootstrap.menu.Menu} this
21681          */
21682         beforehide : true,
21683         /**
21684          * @event show
21685          * Fires after this menu is displayed
21686          * @param {Roo.bootstrap.menu.Menu} this
21687          */
21688         show : true,
21689         /**
21690          * @event hide
21691          * Fires after this menu is hidden
21692          * @param {Roo.bootstrap.menu.Menu} this
21693          */
21694         hide : true,
21695         /**
21696          * @event click
21697          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
21698          * @param {Roo.bootstrap.menu.Menu} this
21699          * @param {Roo.EventObject} e
21700          */
21701         click : true
21702     });
21703     
21704 };
21705
21706 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
21707     
21708     submenu : false,
21709     html : '',
21710     weight : 'default',
21711     icon : false,
21712     pos : 'bottom',
21713     
21714     
21715     getChildContainer : function() {
21716         if(this.isSubMenu){
21717             return this.el;
21718         }
21719         
21720         return this.el.select('ul.dropdown-menu', true).first();  
21721     },
21722     
21723     getAutoCreate : function()
21724     {
21725         var text = [
21726             {
21727                 tag : 'span',
21728                 cls : 'roo-menu-text',
21729                 html : this.html
21730             }
21731         ];
21732         
21733         if(this.icon){
21734             text.unshift({
21735                 tag : 'i',
21736                 cls : 'fa ' + this.icon
21737             })
21738         }
21739         
21740         
21741         var cfg = {
21742             tag : 'div',
21743             cls : 'btn-group',
21744             cn : [
21745                 {
21746                     tag : 'button',
21747                     cls : 'dropdown-button btn btn-' + this.weight,
21748                     cn : text
21749                 },
21750                 {
21751                     tag : 'button',
21752                     cls : 'dropdown-toggle btn btn-' + this.weight,
21753                     cn : [
21754                         {
21755                             tag : 'span',
21756                             cls : 'caret'
21757                         }
21758                     ]
21759                 },
21760                 {
21761                     tag : 'ul',
21762                     cls : 'dropdown-menu'
21763                 }
21764             ]
21765             
21766         };
21767         
21768         if(this.pos == 'top'){
21769             cfg.cls += ' dropup';
21770         }
21771         
21772         if(this.isSubMenu){
21773             cfg = {
21774                 tag : 'ul',
21775                 cls : 'dropdown-menu'
21776             }
21777         }
21778         
21779         return cfg;
21780     },
21781     
21782     onRender : function(ct, position)
21783     {
21784         this.isSubMenu = ct.hasClass('dropdown-submenu');
21785         
21786         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
21787     },
21788     
21789     initEvents : function() 
21790     {
21791         if(this.isSubMenu){
21792             return;
21793         }
21794         
21795         this.hidden = true;
21796         
21797         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
21798         this.triggerEl.on('click', this.onTriggerPress, this);
21799         
21800         this.buttonEl = this.el.select('button.dropdown-button', true).first();
21801         this.buttonEl.on('click', this.onClick, this);
21802         
21803     },
21804     
21805     list : function()
21806     {
21807         if(this.isSubMenu){
21808             return this.el;
21809         }
21810         
21811         return this.el.select('ul.dropdown-menu', true).first();
21812     },
21813     
21814     onClick : function(e)
21815     {
21816         this.fireEvent("click", this, e);
21817     },
21818     
21819     onTriggerPress  : function(e)
21820     {   
21821         if (this.isVisible()) {
21822             this.hide();
21823         } else {
21824             this.show();
21825         }
21826     },
21827     
21828     isVisible : function(){
21829         return !this.hidden;
21830     },
21831     
21832     show : function()
21833     {
21834         this.fireEvent("beforeshow", this);
21835         
21836         this.hidden = false;
21837         this.el.addClass('open');
21838         
21839         Roo.get(document).on("mouseup", this.onMouseUp, this);
21840         
21841         this.fireEvent("show", this);
21842         
21843         
21844     },
21845     
21846     hide : function()
21847     {
21848         this.fireEvent("beforehide", this);
21849         
21850         this.hidden = true;
21851         this.el.removeClass('open');
21852         
21853         Roo.get(document).un("mouseup", this.onMouseUp);
21854         
21855         this.fireEvent("hide", this);
21856     },
21857     
21858     onMouseUp : function()
21859     {
21860         this.hide();
21861     }
21862     
21863 });
21864
21865  
21866  /*
21867  * - LGPL
21868  *
21869  * menu item
21870  * 
21871  */
21872 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21873
21874 /**
21875  * @class Roo.bootstrap.menu.Item
21876  * @extends Roo.bootstrap.Component
21877  * Bootstrap MenuItem class
21878  * @cfg {Boolean} submenu (true | false) default false
21879  * @cfg {String} html text of the item
21880  * @cfg {String} href the link
21881  * @cfg {Boolean} disable (true | false) default false
21882  * @cfg {Boolean} preventDefault (true | false) default true
21883  * @cfg {String} icon Font awesome icon
21884  * @cfg {String} pos Submenu align to (left | right) default right 
21885  * 
21886  * 
21887  * @constructor
21888  * Create a new Item
21889  * @param {Object} config The config object
21890  */
21891
21892
21893 Roo.bootstrap.menu.Item = function(config){
21894     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
21895     this.addEvents({
21896         /**
21897          * @event mouseover
21898          * Fires when the mouse is hovering over this menu
21899          * @param {Roo.bootstrap.menu.Item} this
21900          * @param {Roo.EventObject} e
21901          */
21902         mouseover : true,
21903         /**
21904          * @event mouseout
21905          * Fires when the mouse exits this menu
21906          * @param {Roo.bootstrap.menu.Item} this
21907          * @param {Roo.EventObject} e
21908          */
21909         mouseout : true,
21910         // raw events
21911         /**
21912          * @event click
21913          * The raw click event for the entire grid.
21914          * @param {Roo.EventObject} e
21915          */
21916         click : true
21917     });
21918 };
21919
21920 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
21921     
21922     submenu : false,
21923     href : '',
21924     html : '',
21925     preventDefault: true,
21926     disable : false,
21927     icon : false,
21928     pos : 'right',
21929     
21930     getAutoCreate : function()
21931     {
21932         var text = [
21933             {
21934                 tag : 'span',
21935                 cls : 'roo-menu-item-text',
21936                 html : this.html
21937             }
21938         ];
21939         
21940         if(this.icon){
21941             text.unshift({
21942                 tag : 'i',
21943                 cls : 'fa ' + this.icon
21944             })
21945         }
21946         
21947         var cfg = {
21948             tag : 'li',
21949             cn : [
21950                 {
21951                     tag : 'a',
21952                     href : this.href || '#',
21953                     cn : text
21954                 }
21955             ]
21956         };
21957         
21958         if(this.disable){
21959             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
21960         }
21961         
21962         if(this.submenu){
21963             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
21964             
21965             if(this.pos == 'left'){
21966                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
21967             }
21968         }
21969         
21970         return cfg;
21971     },
21972     
21973     initEvents : function() 
21974     {
21975         this.el.on('mouseover', this.onMouseOver, this);
21976         this.el.on('mouseout', this.onMouseOut, this);
21977         
21978         this.el.select('a', true).first().on('click', this.onClick, this);
21979         
21980     },
21981     
21982     onClick : function(e)
21983     {
21984         if(this.preventDefault){
21985             e.preventDefault();
21986         }
21987         
21988         this.fireEvent("click", this, e);
21989     },
21990     
21991     onMouseOver : function(e)
21992     {
21993         if(this.submenu && this.pos == 'left'){
21994             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
21995         }
21996         
21997         this.fireEvent("mouseover", this, e);
21998     },
21999     
22000     onMouseOut : function(e)
22001     {
22002         this.fireEvent("mouseout", this, e);
22003     }
22004 });
22005
22006  
22007
22008  /*
22009  * - LGPL
22010  *
22011  * menu separator
22012  * 
22013  */
22014 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22015
22016 /**
22017  * @class Roo.bootstrap.menu.Separator
22018  * @extends Roo.bootstrap.Component
22019  * Bootstrap Separator class
22020  * 
22021  * @constructor
22022  * Create a new Separator
22023  * @param {Object} config The config object
22024  */
22025
22026
22027 Roo.bootstrap.menu.Separator = function(config){
22028     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
22029 };
22030
22031 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
22032     
22033     getAutoCreate : function(){
22034         var cfg = {
22035             tag : 'li',
22036             cls: 'divider'
22037         };
22038         
22039         return cfg;
22040     }
22041    
22042 });
22043
22044  
22045
22046  /*
22047  * - LGPL
22048  *
22049  * Tooltip
22050  * 
22051  */
22052
22053 /**
22054  * @class Roo.bootstrap.Tooltip
22055  * Bootstrap Tooltip class
22056  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
22057  * to determine which dom element triggers the tooltip.
22058  * 
22059  * It needs to add support for additional attributes like tooltip-position
22060  * 
22061  * @constructor
22062  * Create a new Toolti
22063  * @param {Object} config The config object
22064  */
22065
22066 Roo.bootstrap.Tooltip = function(config){
22067     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
22068 };
22069
22070 Roo.apply(Roo.bootstrap.Tooltip, {
22071     /**
22072      * @function init initialize tooltip monitoring.
22073      * @static
22074      */
22075     currentEl : false,
22076     currentTip : false,
22077     currentRegion : false,
22078     
22079     //  init : delay?
22080     
22081     init : function()
22082     {
22083         Roo.get(document).on('mouseover', this.enter ,this);
22084         Roo.get(document).on('mouseout', this.leave, this);
22085          
22086         
22087         this.currentTip = new Roo.bootstrap.Tooltip();
22088     },
22089     
22090     enter : function(ev)
22091     {
22092         var dom = ev.getTarget();
22093         
22094         //Roo.log(['enter',dom]);
22095         var el = Roo.fly(dom);
22096         if (this.currentEl) {
22097             //Roo.log(dom);
22098             //Roo.log(this.currentEl);
22099             //Roo.log(this.currentEl.contains(dom));
22100             if (this.currentEl == el) {
22101                 return;
22102             }
22103             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
22104                 return;
22105             }
22106
22107         }
22108         
22109         
22110         
22111         if (this.currentTip.el) {
22112             this.currentTip.el.hide(); // force hiding...
22113         }    
22114         //Roo.log(ev);
22115         var bindEl = el;
22116         
22117         // you can not look for children, as if el is the body.. then everythign is the child..
22118         if (!el.attr('tooltip')) { //
22119             if (!el.select("[tooltip]").elements.length) {
22120                 return;
22121             }
22122             // is the mouse over this child...?
22123             bindEl = el.select("[tooltip]").first();
22124             var xy = ev.getXY();
22125             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
22126                 //Roo.log("not in region.");
22127                 return;
22128             }
22129             //Roo.log("child element over..");
22130             
22131         }
22132         this.currentEl = bindEl;
22133         this.currentTip.bind(bindEl);
22134         this.currentRegion = Roo.lib.Region.getRegion(dom);
22135         this.currentTip.enter();
22136         
22137     },
22138     leave : function(ev)
22139     {
22140         var dom = ev.getTarget();
22141         //Roo.log(['leave',dom]);
22142         if (!this.currentEl) {
22143             return;
22144         }
22145         
22146         
22147         if (dom != this.currentEl.dom) {
22148             return;
22149         }
22150         var xy = ev.getXY();
22151         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
22152             return;
22153         }
22154         // only activate leave if mouse cursor is outside... bounding box..
22155         
22156         
22157         
22158         
22159         if (this.currentTip) {
22160             this.currentTip.leave();
22161         }
22162         //Roo.log('clear currentEl');
22163         this.currentEl = false;
22164         
22165         
22166     },
22167     alignment : {
22168         'left' : ['r-l', [-2,0], 'right'],
22169         'right' : ['l-r', [2,0], 'left'],
22170         'bottom' : ['t-b', [0,2], 'top'],
22171         'top' : [ 'b-t', [0,-2], 'bottom']
22172     }
22173     
22174 });
22175
22176
22177 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
22178     
22179     
22180     bindEl : false,
22181     
22182     delay : null, // can be { show : 300 , hide: 500}
22183     
22184     timeout : null,
22185     
22186     hoverState : null, //???
22187     
22188     placement : 'bottom', 
22189     
22190     getAutoCreate : function(){
22191     
22192         var cfg = {
22193            cls : 'tooltip',
22194            role : 'tooltip',
22195            cn : [
22196                 {
22197                     cls : 'tooltip-arrow'
22198                 },
22199                 {
22200                     cls : 'tooltip-inner'
22201                 }
22202            ]
22203         };
22204         
22205         return cfg;
22206     },
22207     bind : function(el)
22208     {
22209         this.bindEl = el;
22210     },
22211       
22212     
22213     enter : function () {
22214        
22215         if (this.timeout != null) {
22216             clearTimeout(this.timeout);
22217         }
22218         
22219         this.hoverState = 'in';
22220          //Roo.log("enter - show");
22221         if (!this.delay || !this.delay.show) {
22222             this.show();
22223             return;
22224         }
22225         var _t = this;
22226         this.timeout = setTimeout(function () {
22227             if (_t.hoverState == 'in') {
22228                 _t.show();
22229             }
22230         }, this.delay.show);
22231     },
22232     leave : function()
22233     {
22234         clearTimeout(this.timeout);
22235     
22236         this.hoverState = 'out';
22237          if (!this.delay || !this.delay.hide) {
22238             this.hide();
22239             return;
22240         }
22241        
22242         var _t = this;
22243         this.timeout = setTimeout(function () {
22244             //Roo.log("leave - timeout");
22245             
22246             if (_t.hoverState == 'out') {
22247                 _t.hide();
22248                 Roo.bootstrap.Tooltip.currentEl = false;
22249             }
22250         }, delay);
22251     },
22252     
22253     show : function ()
22254     {
22255         if (!this.el) {
22256             this.render(document.body);
22257         }
22258         // set content.
22259         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
22260         
22261         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
22262         
22263         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
22264         
22265         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
22266         
22267         var placement = typeof this.placement == 'function' ?
22268             this.placement.call(this, this.el, on_el) :
22269             this.placement;
22270             
22271         var autoToken = /\s?auto?\s?/i;
22272         var autoPlace = autoToken.test(placement);
22273         if (autoPlace) {
22274             placement = placement.replace(autoToken, '') || 'top';
22275         }
22276         
22277         //this.el.detach()
22278         //this.el.setXY([0,0]);
22279         this.el.show();
22280         //this.el.dom.style.display='block';
22281         this.el.addClass(placement);
22282         
22283         //this.el.appendTo(on_el);
22284         
22285         var p = this.getPosition();
22286         var box = this.el.getBox();
22287         
22288         if (autoPlace) {
22289             // fixme..
22290         }
22291         var align = Roo.bootstrap.Tooltip.alignment[placement];
22292         this.el.alignTo(this.bindEl, align[0],align[1]);
22293         //var arrow = this.el.select('.arrow',true).first();
22294         //arrow.set(align[2], 
22295         
22296         this.el.addClass('in fade');
22297         this.hoverState = null;
22298         
22299         if (this.el.hasClass('fade')) {
22300             // fade it?
22301         }
22302         
22303     },
22304     hide : function()
22305     {
22306          
22307         if (!this.el) {
22308             return;
22309         }
22310         //this.el.setXY([0,0]);
22311         this.el.removeClass('in');
22312         //this.el.hide();
22313         
22314     }
22315     
22316 });
22317  
22318
22319  /*
22320  * - LGPL
22321  *
22322  * Location Picker
22323  * 
22324  */
22325
22326 /**
22327  * @class Roo.bootstrap.LocationPicker
22328  * @extends Roo.bootstrap.Component
22329  * Bootstrap LocationPicker class
22330  * @cfg {Number} latitude Position when init default 0
22331  * @cfg {Number} longitude Position when init default 0
22332  * @cfg {Number} zoom default 15
22333  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
22334  * @cfg {Boolean} mapTypeControl default false
22335  * @cfg {Boolean} disableDoubleClickZoom default false
22336  * @cfg {Boolean} scrollwheel default true
22337  * @cfg {Boolean} streetViewControl default false
22338  * @cfg {Number} radius default 0
22339  * @cfg {String} locationName
22340  * @cfg {Boolean} draggable default true
22341  * @cfg {Boolean} enableAutocomplete default false
22342  * @cfg {Boolean} enableReverseGeocode default true
22343  * @cfg {String} markerTitle
22344  * 
22345  * @constructor
22346  * Create a new LocationPicker
22347  * @param {Object} config The config object
22348  */
22349
22350
22351 Roo.bootstrap.LocationPicker = function(config){
22352     
22353     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
22354     
22355     this.addEvents({
22356         /**
22357          * @event initial
22358          * Fires when the picker initialized.
22359          * @param {Roo.bootstrap.LocationPicker} this
22360          * @param {Google Location} location
22361          */
22362         initial : true,
22363         /**
22364          * @event positionchanged
22365          * Fires when the picker position changed.
22366          * @param {Roo.bootstrap.LocationPicker} this
22367          * @param {Google Location} location
22368          */
22369         positionchanged : true,
22370         /**
22371          * @event resize
22372          * Fires when the map resize.
22373          * @param {Roo.bootstrap.LocationPicker} this
22374          */
22375         resize : true,
22376         /**
22377          * @event show
22378          * Fires when the map show.
22379          * @param {Roo.bootstrap.LocationPicker} this
22380          */
22381         show : true,
22382         /**
22383          * @event hide
22384          * Fires when the map hide.
22385          * @param {Roo.bootstrap.LocationPicker} this
22386          */
22387         hide : true,
22388         /**
22389          * @event mapClick
22390          * Fires when click the map.
22391          * @param {Roo.bootstrap.LocationPicker} this
22392          * @param {Map event} e
22393          */
22394         mapClick : true,
22395         /**
22396          * @event mapRightClick
22397          * Fires when right click the map.
22398          * @param {Roo.bootstrap.LocationPicker} this
22399          * @param {Map event} e
22400          */
22401         mapRightClick : true,
22402         /**
22403          * @event markerClick
22404          * Fires when click the marker.
22405          * @param {Roo.bootstrap.LocationPicker} this
22406          * @param {Map event} e
22407          */
22408         markerClick : true,
22409         /**
22410          * @event markerRightClick
22411          * Fires when right click the marker.
22412          * @param {Roo.bootstrap.LocationPicker} this
22413          * @param {Map event} e
22414          */
22415         markerRightClick : true,
22416         /**
22417          * @event OverlayViewDraw
22418          * Fires when OverlayView Draw
22419          * @param {Roo.bootstrap.LocationPicker} this
22420          */
22421         OverlayViewDraw : true,
22422         /**
22423          * @event OverlayViewOnAdd
22424          * Fires when OverlayView Draw
22425          * @param {Roo.bootstrap.LocationPicker} this
22426          */
22427         OverlayViewOnAdd : true,
22428         /**
22429          * @event OverlayViewOnRemove
22430          * Fires when OverlayView Draw
22431          * @param {Roo.bootstrap.LocationPicker} this
22432          */
22433         OverlayViewOnRemove : true,
22434         /**
22435          * @event OverlayViewShow
22436          * Fires when OverlayView Draw
22437          * @param {Roo.bootstrap.LocationPicker} this
22438          * @param {Pixel} cpx
22439          */
22440         OverlayViewShow : true,
22441         /**
22442          * @event OverlayViewHide
22443          * Fires when OverlayView Draw
22444          * @param {Roo.bootstrap.LocationPicker} this
22445          */
22446         OverlayViewHide : true
22447     });
22448         
22449 };
22450
22451 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
22452     
22453     gMapContext: false,
22454     
22455     latitude: 0,
22456     longitude: 0,
22457     zoom: 15,
22458     mapTypeId: false,
22459     mapTypeControl: false,
22460     disableDoubleClickZoom: false,
22461     scrollwheel: true,
22462     streetViewControl: false,
22463     radius: 0,
22464     locationName: '',
22465     draggable: true,
22466     enableAutocomplete: false,
22467     enableReverseGeocode: true,
22468     markerTitle: '',
22469     
22470     getAutoCreate: function()
22471     {
22472
22473         var cfg = {
22474             tag: 'div',
22475             cls: 'roo-location-picker'
22476         };
22477         
22478         return cfg
22479     },
22480     
22481     initEvents: function(ct, position)
22482     {       
22483         if(!this.el.getWidth() || this.isApplied()){
22484             return;
22485         }
22486         
22487         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22488         
22489         this.initial();
22490     },
22491     
22492     initial: function()
22493     {
22494         if(!this.mapTypeId){
22495             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
22496         }
22497         
22498         this.gMapContext = this.GMapContext();
22499         
22500         this.initOverlayView();
22501         
22502         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
22503         
22504         var _this = this;
22505                 
22506         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
22507             _this.setPosition(_this.gMapContext.marker.position);
22508         });
22509         
22510         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
22511             _this.fireEvent('mapClick', this, event);
22512             
22513         });
22514
22515         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
22516             _this.fireEvent('mapRightClick', this, event);
22517             
22518         });
22519         
22520         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
22521             _this.fireEvent('markerClick', this, event);
22522             
22523         });
22524
22525         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
22526             _this.fireEvent('markerRightClick', this, event);
22527             
22528         });
22529         
22530         this.setPosition(this.gMapContext.location);
22531         
22532         this.fireEvent('initial', this, this.gMapContext.location);
22533     },
22534     
22535     initOverlayView: function()
22536     {
22537         var _this = this;
22538         
22539         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
22540             
22541             draw: function()
22542             {
22543                 _this.fireEvent('OverlayViewDraw', _this);
22544             },
22545             
22546             onAdd: function()
22547             {
22548                 _this.fireEvent('OverlayViewOnAdd', _this);
22549             },
22550             
22551             onRemove: function()
22552             {
22553                 _this.fireEvent('OverlayViewOnRemove', _this);
22554             },
22555             
22556             show: function(cpx)
22557             {
22558                 _this.fireEvent('OverlayViewShow', _this, cpx);
22559             },
22560             
22561             hide: function()
22562             {
22563                 _this.fireEvent('OverlayViewHide', _this);
22564             }
22565             
22566         });
22567     },
22568     
22569     fromLatLngToContainerPixel: function(event)
22570     {
22571         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
22572     },
22573     
22574     isApplied: function() 
22575     {
22576         return this.getGmapContext() == false ? false : true;
22577     },
22578     
22579     getGmapContext: function() 
22580     {
22581         return this.gMapContext
22582     },
22583     
22584     GMapContext: function() 
22585     {
22586         var position = new google.maps.LatLng(this.latitude, this.longitude);
22587         
22588         var _map = new google.maps.Map(this.el.dom, {
22589             center: position,
22590             zoom: this.zoom,
22591             mapTypeId: this.mapTypeId,
22592             mapTypeControl: this.mapTypeControl,
22593             disableDoubleClickZoom: this.disableDoubleClickZoom,
22594             scrollwheel: this.scrollwheel,
22595             streetViewControl: this.streetViewControl,
22596             locationName: this.locationName,
22597             draggable: this.draggable,
22598             enableAutocomplete: this.enableAutocomplete,
22599             enableReverseGeocode: this.enableReverseGeocode
22600         });
22601         
22602         var _marker = new google.maps.Marker({
22603             position: position,
22604             map: _map,
22605             title: this.markerTitle,
22606             draggable: this.draggable
22607         });
22608         
22609         return {
22610             map: _map,
22611             marker: _marker,
22612             circle: null,
22613             location: position,
22614             radius: this.radius,
22615             locationName: this.locationName,
22616             addressComponents: {
22617                 formatted_address: null,
22618                 addressLine1: null,
22619                 addressLine2: null,
22620                 streetName: null,
22621                 streetNumber: null,
22622                 city: null,
22623                 district: null,
22624                 state: null,
22625                 stateOrProvince: null
22626             },
22627             settings: this,
22628             domContainer: this.el.dom,
22629             geodecoder: new google.maps.Geocoder()
22630         };
22631     },
22632     
22633     drawCircle: function(center, radius, options) 
22634     {
22635         if (this.gMapContext.circle != null) {
22636             this.gMapContext.circle.setMap(null);
22637         }
22638         if (radius > 0) {
22639             radius *= 1;
22640             options = Roo.apply({}, options, {
22641                 strokeColor: "#0000FF",
22642                 strokeOpacity: .35,
22643                 strokeWeight: 2,
22644                 fillColor: "#0000FF",
22645                 fillOpacity: .2
22646             });
22647             
22648             options.map = this.gMapContext.map;
22649             options.radius = radius;
22650             options.center = center;
22651             this.gMapContext.circle = new google.maps.Circle(options);
22652             return this.gMapContext.circle;
22653         }
22654         
22655         return null;
22656     },
22657     
22658     setPosition: function(location) 
22659     {
22660         this.gMapContext.location = location;
22661         this.gMapContext.marker.setPosition(location);
22662         this.gMapContext.map.panTo(location);
22663         this.drawCircle(location, this.gMapContext.radius, {});
22664         
22665         var _this = this;
22666         
22667         if (this.gMapContext.settings.enableReverseGeocode) {
22668             this.gMapContext.geodecoder.geocode({
22669                 latLng: this.gMapContext.location
22670             }, function(results, status) {
22671                 
22672                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
22673                     _this.gMapContext.locationName = results[0].formatted_address;
22674                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
22675                     
22676                     _this.fireEvent('positionchanged', this, location);
22677                 }
22678             });
22679             
22680             return;
22681         }
22682         
22683         this.fireEvent('positionchanged', this, location);
22684     },
22685     
22686     resize: function()
22687     {
22688         google.maps.event.trigger(this.gMapContext.map, "resize");
22689         
22690         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
22691         
22692         this.fireEvent('resize', this);
22693     },
22694     
22695     setPositionByLatLng: function(latitude, longitude)
22696     {
22697         this.setPosition(new google.maps.LatLng(latitude, longitude));
22698     },
22699     
22700     getCurrentPosition: function() 
22701     {
22702         return {
22703             latitude: this.gMapContext.location.lat(),
22704             longitude: this.gMapContext.location.lng()
22705         };
22706     },
22707     
22708     getAddressName: function() 
22709     {
22710         return this.gMapContext.locationName;
22711     },
22712     
22713     getAddressComponents: function() 
22714     {
22715         return this.gMapContext.addressComponents;
22716     },
22717     
22718     address_component_from_google_geocode: function(address_components) 
22719     {
22720         var result = {};
22721         
22722         for (var i = 0; i < address_components.length; i++) {
22723             var component = address_components[i];
22724             if (component.types.indexOf("postal_code") >= 0) {
22725                 result.postalCode = component.short_name;
22726             } else if (component.types.indexOf("street_number") >= 0) {
22727                 result.streetNumber = component.short_name;
22728             } else if (component.types.indexOf("route") >= 0) {
22729                 result.streetName = component.short_name;
22730             } else if (component.types.indexOf("neighborhood") >= 0) {
22731                 result.city = component.short_name;
22732             } else if (component.types.indexOf("locality") >= 0) {
22733                 result.city = component.short_name;
22734             } else if (component.types.indexOf("sublocality") >= 0) {
22735                 result.district = component.short_name;
22736             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
22737                 result.stateOrProvince = component.short_name;
22738             } else if (component.types.indexOf("country") >= 0) {
22739                 result.country = component.short_name;
22740             }
22741         }
22742         
22743         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
22744         result.addressLine2 = "";
22745         return result;
22746     },
22747     
22748     setZoomLevel: function(zoom)
22749     {
22750         this.gMapContext.map.setZoom(zoom);
22751     },
22752     
22753     show: function()
22754     {
22755         if(!this.el){
22756             return;
22757         }
22758         
22759         this.el.show();
22760         
22761         this.resize();
22762         
22763         this.fireEvent('show', this);
22764     },
22765     
22766     hide: function()
22767     {
22768         if(!this.el){
22769             return;
22770         }
22771         
22772         this.el.hide();
22773         
22774         this.fireEvent('hide', this);
22775     }
22776     
22777 });
22778
22779 Roo.apply(Roo.bootstrap.LocationPicker, {
22780     
22781     OverlayView : function(map, options)
22782     {
22783         options = options || {};
22784         
22785         this.setMap(map);
22786     }
22787     
22788     
22789 });/*
22790  * - LGPL
22791  *
22792  * Alert
22793  * 
22794  */
22795
22796 /**
22797  * @class Roo.bootstrap.Alert
22798  * @extends Roo.bootstrap.Component
22799  * Bootstrap Alert class
22800  * @cfg {String} title The title of alert
22801  * @cfg {String} html The content of alert
22802  * @cfg {String} weight (  success | info | warning | danger )
22803  * @cfg {String} faicon font-awesomeicon
22804  * 
22805  * @constructor
22806  * Create a new alert
22807  * @param {Object} config The config object
22808  */
22809
22810
22811 Roo.bootstrap.Alert = function(config){
22812     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
22813     
22814 };
22815
22816 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
22817     
22818     title: '',
22819     html: '',
22820     weight: false,
22821     faicon: false,
22822     
22823     getAutoCreate : function()
22824     {
22825         
22826         var cfg = {
22827             tag : 'div',
22828             cls : 'alert',
22829             cn : [
22830                 {
22831                     tag : 'i',
22832                     cls : 'roo-alert-icon'
22833                     
22834                 },
22835                 {
22836                     tag : 'b',
22837                     cls : 'roo-alert-title',
22838                     html : this.title
22839                 },
22840                 {
22841                     tag : 'span',
22842                     cls : 'roo-alert-text',
22843                     html : this.html
22844                 }
22845             ]
22846         };
22847         
22848         if(this.faicon){
22849             cfg.cn[0].cls += ' fa ' + this.faicon;
22850         }
22851         
22852         if(this.weight){
22853             cfg.cls += ' alert-' + this.weight;
22854         }
22855         
22856         return cfg;
22857     },
22858     
22859     initEvents: function() 
22860     {
22861         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22862     },
22863     
22864     setTitle : function(str)
22865     {
22866         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
22867     },
22868     
22869     setText : function(str)
22870     {
22871         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
22872     },
22873     
22874     setWeight : function(weight)
22875     {
22876         if(this.weight){
22877             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
22878         }
22879         
22880         this.weight = weight;
22881         
22882         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
22883     },
22884     
22885     setIcon : function(icon)
22886     {
22887         if(this.faicon){
22888             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
22889         }
22890         
22891         this.faicon = icon
22892         
22893         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
22894     },
22895     
22896     hide: function() 
22897     {
22898         this.el.hide();   
22899     },
22900     
22901     show: function() 
22902     {  
22903         this.el.show();   
22904     }
22905     
22906 });
22907
22908