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             
1846             //xy = this.el.adjustForConstraints(xy);
1847         }
1848         //this.el.setXY(xy);
1849         //this.el.show();
1850         this.hideMenuItems();
1851         this.hidden = false;
1852         this.triggerEl.addClass('open');
1853         this.focus();
1854         this.fireEvent("show", this);
1855     },
1856     
1857     focus : function(){
1858         return;
1859         if(!this.hidden){
1860             this.doFocus.defer(50, this);
1861         }
1862     },
1863
1864     doFocus : function(){
1865         if(!this.hidden){
1866             this.focusEl.focus();
1867         }
1868     },
1869
1870     /**
1871      * Hides this menu and optionally all parent menus
1872      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1873      */
1874     hide : function(deep){
1875         
1876         this.hideMenuItems();
1877         if(this.el && this.isVisible()){
1878             this.fireEvent("beforehide", this);
1879             if(this.activeItem){
1880                 this.activeItem.deactivate();
1881                 this.activeItem = null;
1882             }
1883             this.triggerEl.removeClass('open');;
1884             this.hidden = true;
1885             this.fireEvent("hide", this);
1886         }
1887         if(deep === true && this.parentMenu){
1888             this.parentMenu.hide(true);
1889         }
1890     },
1891     
1892     onTriggerPress  : function(e)
1893     {
1894         
1895         Roo.log('trigger press');
1896         //Roo.log(e.getTarget());
1897        // Roo.log(this.triggerEl.dom);
1898         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1899             return;
1900         }
1901         if (this.isVisible()) {
1902             Roo.log('hide');
1903             this.hide();
1904         } else {
1905             this.show(this.triggerEl, false, false);
1906         }
1907         
1908         
1909     },
1910     
1911          
1912        
1913     
1914     hideMenuItems : function()
1915     {
1916         //$(backdrop).remove()
1917         Roo.select('.open',true).each(function(aa) {
1918             
1919             aa.removeClass('open');
1920           //var parent = getParent($(this))
1921           //var relatedTarget = { relatedTarget: this }
1922           
1923            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1924           //if (e.isDefaultPrevented()) return
1925            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1926         })
1927     },
1928     addxtypeChild : function (tree, cntr) {
1929         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1930           
1931         this.menuitems.add(comp);
1932         return comp;
1933
1934     },
1935     getEl : function()
1936     {
1937         Roo.log(this.el);
1938         return this.el;
1939     }
1940 });
1941
1942  
1943  /*
1944  * - LGPL
1945  *
1946  * menu item
1947  * 
1948  */
1949
1950
1951 /**
1952  * @class Roo.bootstrap.MenuItem
1953  * @extends Roo.bootstrap.Component
1954  * Bootstrap MenuItem class
1955  * @cfg {String} html the menu label
1956  * @cfg {String} href the link
1957  * @cfg {Boolean} preventDefault (true | false) default true
1958  * @cfg {Boolean} isContainer (true | false) default false
1959  * 
1960  * 
1961  * @constructor
1962  * Create a new MenuItem
1963  * @param {Object} config The config object
1964  */
1965
1966
1967 Roo.bootstrap.MenuItem = function(config){
1968     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1969     this.addEvents({
1970         // raw events
1971         /**
1972          * @event click
1973          * The raw click event for the entire grid.
1974          * @param {Roo.bootstrap.MenuItem} this
1975          * @param {Roo.EventObject} e
1976          */
1977         "click" : true
1978     });
1979 };
1980
1981 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1982     
1983     href : false,
1984     html : false,
1985     preventDefault: true,
1986     isContainer : false,
1987     
1988     getAutoCreate : function(){
1989         
1990         if(this.isContainer){
1991             return {
1992                 tag: 'li',
1993                 cls: 'dropdown-menu-item'
1994             };
1995         }
1996         
1997         var cfg= {
1998             tag: 'li',
1999             cls: 'dropdown-menu-item',
2000             cn: [
2001                     {
2002                         tag : 'a',
2003                         href : '#',
2004                         html : 'Link'
2005                     }
2006                 ]
2007         };
2008         if (this.parent().type == 'treeview') {
2009             cfg.cls = 'treeview-menu';
2010         }
2011         
2012         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2013         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2014         return cfg;
2015     },
2016     
2017     initEvents: function() {
2018         
2019         //this.el.select('a').on('click', this.onClick, this);
2020         
2021     },
2022     onClick : function(e)
2023     {
2024         Roo.log('item on click ');
2025         //if(this.preventDefault){
2026         //    e.preventDefault();
2027         //}
2028         //this.parent().hideMenuItems();
2029         
2030         this.fireEvent('click', this, e);
2031     },
2032     getEl : function()
2033     {
2034         return this.el;
2035     }
2036 });
2037
2038  
2039
2040  /*
2041  * - LGPL
2042  *
2043  * menu separator
2044  * 
2045  */
2046
2047
2048 /**
2049  * @class Roo.bootstrap.MenuSeparator
2050  * @extends Roo.bootstrap.Component
2051  * Bootstrap MenuSeparator class
2052  * 
2053  * @constructor
2054  * Create a new MenuItem
2055  * @param {Object} config The config object
2056  */
2057
2058
2059 Roo.bootstrap.MenuSeparator = function(config){
2060     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2061 };
2062
2063 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2064     
2065     getAutoCreate : function(){
2066         var cfg = {
2067             cls: 'divider',
2068             tag : 'li'
2069         };
2070         
2071         return cfg;
2072     }
2073    
2074 });
2075
2076  
2077
2078  
2079 /*
2080 * Licence: LGPL
2081 */
2082
2083 /**
2084  * @class Roo.bootstrap.Modal
2085  * @extends Roo.bootstrap.Component
2086  * Bootstrap Modal class
2087  * @cfg {String} title Title of dialog
2088  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2089  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2090  * @cfg {Boolean} specificTitle default false
2091  * @cfg {Array} buttons Array of buttons or standard button set..
2092  * @cfg {String} buttonPosition (left|right|center) default right
2093  * @cfg {Boolean} animate default true
2094  * @cfg {Boolean} allow_close default true
2095  * 
2096  * @constructor
2097  * Create a new Modal Dialog
2098  * @param {Object} config The config object
2099  */
2100
2101 Roo.bootstrap.Modal = function(config){
2102     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2103     this.addEvents({
2104         // raw events
2105         /**
2106          * @event btnclick
2107          * The raw btnclick event for the button
2108          * @param {Roo.EventObject} e
2109          */
2110         "btnclick" : true
2111     });
2112     this.buttons = this.buttons || [];
2113      
2114     if (this.tmpl) {
2115         this.tmpl = Roo.factory(this.tmpl);
2116     }
2117     
2118 };
2119
2120 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2121     
2122     title : 'test dialog',
2123    
2124     buttons : false,
2125     
2126     // set on load...
2127      
2128     html: false,
2129     
2130     tmp: false,
2131     
2132     specificTitle: false,
2133     
2134     buttonPosition: 'right',
2135     
2136     allow_close : true,
2137     
2138     animate : true,
2139     
2140     
2141      // private
2142     bodyEl:  false,
2143     footerEl:  false,
2144     titleEl:  false,
2145     closeEl:  false,
2146     
2147     
2148     onRender : function(ct, position)
2149     {
2150         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2151      
2152         if(!this.el){
2153             var cfg = Roo.apply({},  this.getAutoCreate());
2154             cfg.id = Roo.id();
2155             //if(!cfg.name){
2156             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2157             //}
2158             //if (!cfg.name.length) {
2159             //    delete cfg.name;
2160            // }
2161             if (this.cls) {
2162                 cfg.cls += ' ' + this.cls;
2163             }
2164             if (this.style) {
2165                 cfg.style = this.style;
2166             }
2167             this.el = Roo.get(document.body).createChild(cfg, position);
2168         }
2169         //var type = this.el.dom.type;
2170         
2171         
2172         
2173         
2174         if(this.tabIndex !== undefined){
2175             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2176         }
2177         
2178         
2179         this.bodyEl = this.el.select('.modal-body',true).first();
2180         this.closeEl = this.el.select('.modal-header .close', true).first();
2181         this.footerEl = this.el.select('.modal-footer',true).first();
2182         this.titleEl = this.el.select('.modal-title',true).first();
2183         
2184         
2185          
2186         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2187         this.maskEl.enableDisplayMode("block");
2188         this.maskEl.hide();
2189         //this.el.addClass("x-dlg-modal");
2190     
2191         if (this.buttons.length) {
2192             Roo.each(this.buttons, function(bb) {
2193                 b = Roo.apply({}, bb);
2194                 b.xns = b.xns || Roo.bootstrap;
2195                 b.xtype = b.xtype || 'Button';
2196                 if (typeof(b.listeners) == 'undefined') {
2197                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2198                 }
2199                 
2200                 var btn = Roo.factory(b);
2201                 
2202                 btn.onRender(this.el.select('.modal-footer div').first());
2203                 
2204             },this);
2205         }
2206         // render the children.
2207         var nitems = [];
2208         
2209         if(typeof(this.items) != 'undefined'){
2210             var items = this.items;
2211             delete this.items;
2212
2213             for(var i =0;i < items.length;i++) {
2214                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2215             }
2216         }
2217         
2218         this.items = nitems;
2219         
2220         // where are these used - they used to be body/close/footer
2221         
2222        
2223         this.initEvents();
2224         //this.el.addClass([this.fieldClass, this.cls]);
2225         
2226     },
2227     getAutoCreate : function(){
2228         
2229         
2230         var bdy = {
2231                 cls : 'modal-body',
2232                 html : this.html || ''
2233         };
2234         
2235         var title = {
2236             tag: 'h4',
2237             cls : 'modal-title',
2238             html : this.title
2239         };
2240         
2241         if(this.specificTitle){
2242             title = this.title;
2243             
2244         };
2245         
2246         var header = [];
2247         if (this.allow_close) {
2248             header.push({
2249                 tag: 'button',
2250                 cls : 'close',
2251                 html : '&times'
2252             });
2253         }
2254         header.push(title);
2255         
2256         var modal = {
2257             cls: "modal",
2258             style : 'display: none',
2259             cn : [
2260                 {
2261                     cls: "modal-dialog",
2262                     cn : [
2263                         {
2264                             cls : "modal-content",
2265                             cn : [
2266                                 {
2267                                     cls : 'modal-header',
2268                                     cn : header
2269                                 },
2270                                 bdy,
2271                                 {
2272                                     cls : 'modal-footer',
2273                                     cn : [
2274                                         {
2275                                             tag: 'div',
2276                                             cls: 'btn-' + this.buttonPosition
2277                                         }
2278                                     ]
2279                                     
2280                                 }
2281                                 
2282                                 
2283                             ]
2284                             
2285                         }
2286                     ]
2287                         
2288                 }
2289             ]
2290         };
2291         
2292         if(this.animate){
2293             modal.cls += ' fade';
2294         }
2295         
2296         return modal;
2297           
2298     },
2299     getChildContainer : function() {
2300          
2301          return this.bodyEl;
2302         
2303     },
2304     getButtonContainer : function() {
2305          return this.el.select('.modal-footer div',true).first();
2306         
2307     },
2308     initEvents : function()
2309     {
2310         if (this.allow_close) {
2311             this.closeEl.on('click', this.hide, this);
2312         }
2313
2314     },
2315     show : function() {
2316         
2317         if (!this.rendered) {
2318             this.render();
2319         }
2320         
2321         this.el.setStyle('display', 'block');
2322         
2323         if(this.animate){
2324             var _this = this;
2325             (function(){ _this.el.addClass('in'); }).defer(50);
2326         }else{
2327             this.el.addClass('in');
2328         }
2329         
2330         // not sure how we can show data in here.. 
2331         //if (this.tmpl) {
2332         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2333         //}
2334         
2335         Roo.get(document.body).addClass("x-body-masked");
2336         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2337         this.maskEl.show();
2338         this.el.setStyle('zIndex', '10001');
2339        
2340         this.fireEvent('show', this);
2341         
2342         
2343     },
2344     hide : function()
2345     {
2346         this.maskEl.hide();
2347         Roo.get(document.body).removeClass("x-body-masked");
2348         this.el.removeClass('in');
2349         
2350         if(this.animate){
2351             var _this = this;
2352             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2353         }else{
2354             this.el.setStyle('display', 'none');
2355         }
2356         
2357         this.fireEvent('hide', this);
2358     },
2359     
2360     addButton : function(str, cb)
2361     {
2362          
2363         
2364         var b = Roo.apply({}, { html : str } );
2365         b.xns = b.xns || Roo.bootstrap;
2366         b.xtype = b.xtype || 'Button';
2367         if (typeof(b.listeners) == 'undefined') {
2368             b.listeners = { click : cb.createDelegate(this)  };
2369         }
2370         
2371         var btn = Roo.factory(b);
2372            
2373         btn.onRender(this.el.select('.modal-footer div').first());
2374         
2375         return btn;   
2376        
2377     },
2378     
2379     setDefaultButton : function(btn)
2380     {
2381         //this.el.select('.modal-footer').()
2382     },
2383     resizeTo: function(w,h)
2384     {
2385         // skip..
2386     },
2387     setContentSize  : function(w, h)
2388     {
2389         
2390     },
2391     onButtonClick: function(btn,e)
2392     {
2393         //Roo.log([a,b,c]);
2394         this.fireEvent('btnclick', btn.name, e);
2395     },
2396      /**
2397      * Set the title of the Dialog
2398      * @param {String} str new Title
2399      */
2400     setTitle: function(str) {
2401         this.titleEl.dom.innerHTML = str;    
2402     },
2403     /**
2404      * Set the body of the Dialog
2405      * @param {String} str new Title
2406      */
2407     setBody: function(str) {
2408         this.bodyEl.dom.innerHTML = str;    
2409     },
2410     /**
2411      * Set the body of the Dialog using the template
2412      * @param {Obj} data - apply this data to the template and replace the body contents.
2413      */
2414     applyBody: function(obj)
2415     {
2416         if (!this.tmpl) {
2417             Roo.log("Error - using apply Body without a template");
2418             //code
2419         }
2420         this.tmpl.overwrite(this.bodyEl, obj);
2421     }
2422     
2423 });
2424
2425
2426 Roo.apply(Roo.bootstrap.Modal,  {
2427     /**
2428          * Button config that displays a single OK button
2429          * @type Object
2430          */
2431         OK :  [{
2432             name : 'ok',
2433             weight : 'primary',
2434             html : 'OK'
2435         }], 
2436         /**
2437          * Button config that displays Yes and No buttons
2438          * @type Object
2439          */
2440         YESNO : [
2441             {
2442                 name  : 'no',
2443                 html : 'No'
2444             },
2445             {
2446                 name  :'yes',
2447                 weight : 'primary',
2448                 html : 'Yes'
2449             }
2450         ],
2451         
2452         /**
2453          * Button config that displays OK and Cancel buttons
2454          * @type Object
2455          */
2456         OKCANCEL : [
2457             {
2458                name : 'cancel',
2459                 html : 'Cancel'
2460             },
2461             {
2462                 name : 'ok',
2463                 weight : 'primary',
2464                 html : 'OK'
2465             }
2466         ],
2467         /**
2468          * Button config that displays Yes, No and Cancel buttons
2469          * @type Object
2470          */
2471         YESNOCANCEL : [
2472             {
2473                 name : 'yes',
2474                 weight : 'primary',
2475                 html : 'Yes'
2476             },
2477             {
2478                 name : 'no',
2479                 html : 'No'
2480             },
2481             {
2482                 name : 'cancel',
2483                 html : 'Cancel'
2484             }
2485         ]
2486 });
2487  
2488  /*
2489  * - LGPL
2490  *
2491  * messagebox - can be used as a replace
2492  * 
2493  */
2494 /**
2495  * @class Roo.MessageBox
2496  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2497  * Example usage:
2498  *<pre><code>
2499 // Basic alert:
2500 Roo.Msg.alert('Status', 'Changes saved successfully.');
2501
2502 // Prompt for user data:
2503 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2504     if (btn == 'ok'){
2505         // process text value...
2506     }
2507 });
2508
2509 // Show a dialog using config options:
2510 Roo.Msg.show({
2511    title:'Save Changes?',
2512    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2513    buttons: Roo.Msg.YESNOCANCEL,
2514    fn: processResult,
2515    animEl: 'elId'
2516 });
2517 </code></pre>
2518  * @singleton
2519  */
2520 Roo.bootstrap.MessageBox = function(){
2521     var dlg, opt, mask, waitTimer;
2522     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2523     var buttons, activeTextEl, bwidth;
2524
2525     
2526     // private
2527     var handleButton = function(button){
2528         dlg.hide();
2529         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2530     };
2531
2532     // private
2533     var handleHide = function(){
2534         if(opt && opt.cls){
2535             dlg.el.removeClass(opt.cls);
2536         }
2537         //if(waitTimer){
2538         //    Roo.TaskMgr.stop(waitTimer);
2539         //    waitTimer = null;
2540         //}
2541     };
2542
2543     // private
2544     var updateButtons = function(b){
2545         var width = 0;
2546         if(!b){
2547             buttons["ok"].hide();
2548             buttons["cancel"].hide();
2549             buttons["yes"].hide();
2550             buttons["no"].hide();
2551             //dlg.footer.dom.style.display = 'none';
2552             return width;
2553         }
2554         dlg.footerEl.dom.style.display = '';
2555         for(var k in buttons){
2556             if(typeof buttons[k] != "function"){
2557                 if(b[k]){
2558                     buttons[k].show();
2559                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2560                     width += buttons[k].el.getWidth()+15;
2561                 }else{
2562                     buttons[k].hide();
2563                 }
2564             }
2565         }
2566         return width;
2567     };
2568
2569     // private
2570     var handleEsc = function(d, k, e){
2571         if(opt && opt.closable !== false){
2572             dlg.hide();
2573         }
2574         if(e){
2575             e.stopEvent();
2576         }
2577     };
2578
2579     return {
2580         /**
2581          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2582          * @return {Roo.BasicDialog} The BasicDialog element
2583          */
2584         getDialog : function(){
2585            if(!dlg){
2586                 dlg = new Roo.bootstrap.Modal( {
2587                     //draggable: true,
2588                     //resizable:false,
2589                     //constraintoviewport:false,
2590                     //fixedcenter:true,
2591                     //collapsible : false,
2592                     //shim:true,
2593                     //modal: true,
2594                   //  width:400,
2595                   //  height:100,
2596                     //buttonAlign:"center",
2597                     closeClick : function(){
2598                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2599                             handleButton("no");
2600                         }else{
2601                             handleButton("cancel");
2602                         }
2603                     }
2604                 });
2605                 dlg.render();
2606                 dlg.on("hide", handleHide);
2607                 mask = dlg.mask;
2608                 //dlg.addKeyListener(27, handleEsc);
2609                 buttons = {};
2610                 this.buttons = buttons;
2611                 var bt = this.buttonText;
2612                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2613                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2614                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2615                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2616                 Roo.log(buttons)
2617                 bodyEl = dlg.bodyEl.createChild({
2618
2619                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2620                         '<textarea class="roo-mb-textarea"></textarea>' +
2621                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2622                 });
2623                 msgEl = bodyEl.dom.firstChild;
2624                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2625                 textboxEl.enableDisplayMode();
2626                 textboxEl.addKeyListener([10,13], function(){
2627                     if(dlg.isVisible() && opt && opt.buttons){
2628                         if(opt.buttons.ok){
2629                             handleButton("ok");
2630                         }else if(opt.buttons.yes){
2631                             handleButton("yes");
2632                         }
2633                     }
2634                 });
2635                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2636                 textareaEl.enableDisplayMode();
2637                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2638                 progressEl.enableDisplayMode();
2639                 var pf = progressEl.dom.firstChild;
2640                 if (pf) {
2641                     pp = Roo.get(pf.firstChild);
2642                     pp.setHeight(pf.offsetHeight);
2643                 }
2644                 
2645             }
2646             return dlg;
2647         },
2648
2649         /**
2650          * Updates the message box body text
2651          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2652          * the XHTML-compliant non-breaking space character '&amp;#160;')
2653          * @return {Roo.MessageBox} This message box
2654          */
2655         updateText : function(text){
2656             if(!dlg.isVisible() && !opt.width){
2657                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2658             }
2659             msgEl.innerHTML = text || '&#160;';
2660       
2661             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2662             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2663             var w = Math.max(
2664                     Math.min(opt.width || cw , this.maxWidth), 
2665                     Math.max(opt.minWidth || this.minWidth, bwidth)
2666             );
2667             if(opt.prompt){
2668                 activeTextEl.setWidth(w);
2669             }
2670             if(dlg.isVisible()){
2671                 dlg.fixedcenter = false;
2672             }
2673             // to big, make it scroll. = But as usual stupid IE does not support
2674             // !important..
2675             
2676             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2677                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2678                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2679             } else {
2680                 bodyEl.dom.style.height = '';
2681                 bodyEl.dom.style.overflowY = '';
2682             }
2683             if (cw > w) {
2684                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2685             } else {
2686                 bodyEl.dom.style.overflowX = '';
2687             }
2688             
2689             dlg.setContentSize(w, bodyEl.getHeight());
2690             if(dlg.isVisible()){
2691                 dlg.fixedcenter = true;
2692             }
2693             return this;
2694         },
2695
2696         /**
2697          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2698          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2699          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2700          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2701          * @return {Roo.MessageBox} This message box
2702          */
2703         updateProgress : function(value, text){
2704             if(text){
2705                 this.updateText(text);
2706             }
2707             if (pp) { // weird bug on my firefox - for some reason this is not defined
2708                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2709             }
2710             return this;
2711         },        
2712
2713         /**
2714          * Returns true if the message box is currently displayed
2715          * @return {Boolean} True if the message box is visible, else false
2716          */
2717         isVisible : function(){
2718             return dlg && dlg.isVisible();  
2719         },
2720
2721         /**
2722          * Hides the message box if it is displayed
2723          */
2724         hide : function(){
2725             if(this.isVisible()){
2726                 dlg.hide();
2727             }  
2728         },
2729
2730         /**
2731          * Displays a new message box, or reinitializes an existing message box, based on the config options
2732          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2733          * The following config object properties are supported:
2734          * <pre>
2735 Property    Type             Description
2736 ----------  ---------------  ------------------------------------------------------------------------------------
2737 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2738                                    closes (defaults to undefined)
2739 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2740                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2741 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2742                                    progress and wait dialogs will ignore this property and always hide the
2743                                    close button as they can only be closed programmatically.
2744 cls               String           A custom CSS class to apply to the message box element
2745 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2746                                    displayed (defaults to 75)
2747 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2748                                    function will be btn (the name of the button that was clicked, if applicable,
2749                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2750                                    Progress and wait dialogs will ignore this option since they do not respond to
2751                                    user actions and can only be closed programmatically, so any required function
2752                                    should be called by the same code after it closes the dialog.
2753 icon              String           A CSS class that provides a background image to be used as an icon for
2754                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2755 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2756 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2757 modal             Boolean          False to allow user interaction with the page while the message box is
2758                                    displayed (defaults to true)
2759 msg               String           A string that will replace the existing message box body text (defaults
2760                                    to the XHTML-compliant non-breaking space character '&#160;')
2761 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2762 progress          Boolean          True to display a progress bar (defaults to false)
2763 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2764 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2765 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2766 title             String           The title text
2767 value             String           The string value to set into the active textbox element if displayed
2768 wait              Boolean          True to display a progress bar (defaults to false)
2769 width             Number           The width of the dialog in pixels
2770 </pre>
2771          *
2772          * Example usage:
2773          * <pre><code>
2774 Roo.Msg.show({
2775    title: 'Address',
2776    msg: 'Please enter your address:',
2777    width: 300,
2778    buttons: Roo.MessageBox.OKCANCEL,
2779    multiline: true,
2780    fn: saveAddress,
2781    animEl: 'addAddressBtn'
2782 });
2783 </code></pre>
2784          * @param {Object} config Configuration options
2785          * @return {Roo.MessageBox} This message box
2786          */
2787         show : function(options)
2788         {
2789             
2790             // this causes nightmares if you show one dialog after another
2791             // especially on callbacks..
2792              
2793             if(this.isVisible()){
2794                 
2795                 this.hide();
2796                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2797                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2798                 Roo.log("New Dialog Message:" +  options.msg )
2799                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2800                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2801                 
2802             }
2803             var d = this.getDialog();
2804             opt = options;
2805             d.setTitle(opt.title || "&#160;");
2806             d.closeEl.setDisplayed(opt.closable !== false);
2807             activeTextEl = textboxEl;
2808             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2809             if(opt.prompt){
2810                 if(opt.multiline){
2811                     textboxEl.hide();
2812                     textareaEl.show();
2813                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2814                         opt.multiline : this.defaultTextHeight);
2815                     activeTextEl = textareaEl;
2816                 }else{
2817                     textboxEl.show();
2818                     textareaEl.hide();
2819                 }
2820             }else{
2821                 textboxEl.hide();
2822                 textareaEl.hide();
2823             }
2824             progressEl.setDisplayed(opt.progress === true);
2825             this.updateProgress(0);
2826             activeTextEl.dom.value = opt.value || "";
2827             if(opt.prompt){
2828                 dlg.setDefaultButton(activeTextEl);
2829             }else{
2830                 var bs = opt.buttons;
2831                 var db = null;
2832                 if(bs && bs.ok){
2833                     db = buttons["ok"];
2834                 }else if(bs && bs.yes){
2835                     db = buttons["yes"];
2836                 }
2837                 dlg.setDefaultButton(db);
2838             }
2839             bwidth = updateButtons(opt.buttons);
2840             this.updateText(opt.msg);
2841             if(opt.cls){
2842                 d.el.addClass(opt.cls);
2843             }
2844             d.proxyDrag = opt.proxyDrag === true;
2845             d.modal = opt.modal !== false;
2846             d.mask = opt.modal !== false ? mask : false;
2847             if(!d.isVisible()){
2848                 // force it to the end of the z-index stack so it gets a cursor in FF
2849                 document.body.appendChild(dlg.el.dom);
2850                 d.animateTarget = null;
2851                 d.show(options.animEl);
2852             }
2853             return this;
2854         },
2855
2856         /**
2857          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2858          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2859          * and closing the message box when the process is complete.
2860          * @param {String} title The title bar text
2861          * @param {String} msg The message box body text
2862          * @return {Roo.MessageBox} This message box
2863          */
2864         progress : function(title, msg){
2865             this.show({
2866                 title : title,
2867                 msg : msg,
2868                 buttons: false,
2869                 progress:true,
2870                 closable:false,
2871                 minWidth: this.minProgressWidth,
2872                 modal : true
2873             });
2874             return this;
2875         },
2876
2877         /**
2878          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2879          * If a callback function is passed it will be called after the user clicks the button, and the
2880          * id of the button that was clicked will be passed as the only parameter to the callback
2881          * (could also be the top-right close button).
2882          * @param {String} title The title bar text
2883          * @param {String} msg The message box body text
2884          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2885          * @param {Object} scope (optional) The scope of the callback function
2886          * @return {Roo.MessageBox} This message box
2887          */
2888         alert : function(title, msg, fn, scope){
2889             this.show({
2890                 title : title,
2891                 msg : msg,
2892                 buttons: this.OK,
2893                 fn: fn,
2894                 scope : scope,
2895                 modal : true
2896             });
2897             return this;
2898         },
2899
2900         /**
2901          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2902          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2903          * You are responsible for closing the message box when the process is complete.
2904          * @param {String} msg The message box body text
2905          * @param {String} title (optional) The title bar text
2906          * @return {Roo.MessageBox} This message box
2907          */
2908         wait : function(msg, title){
2909             this.show({
2910                 title : title,
2911                 msg : msg,
2912                 buttons: false,
2913                 closable:false,
2914                 progress:true,
2915                 modal:true,
2916                 width:300,
2917                 wait:true
2918             });
2919             waitTimer = Roo.TaskMgr.start({
2920                 run: function(i){
2921                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2922                 },
2923                 interval: 1000
2924             });
2925             return this;
2926         },
2927
2928         /**
2929          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2930          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2931          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2932          * @param {String} title The title bar text
2933          * @param {String} msg The message box body text
2934          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2935          * @param {Object} scope (optional) The scope of the callback function
2936          * @return {Roo.MessageBox} This message box
2937          */
2938         confirm : function(title, msg, fn, scope){
2939             this.show({
2940                 title : title,
2941                 msg : msg,
2942                 buttons: this.YESNO,
2943                 fn: fn,
2944                 scope : scope,
2945                 modal : true
2946             });
2947             return this;
2948         },
2949
2950         /**
2951          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2952          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2953          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2954          * (could also be the top-right close button) and the text that was entered will be passed as the two
2955          * parameters to the callback.
2956          * @param {String} title The title bar text
2957          * @param {String} msg The message box body text
2958          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2959          * @param {Object} scope (optional) The scope of the callback function
2960          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2961          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2962          * @return {Roo.MessageBox} This message box
2963          */
2964         prompt : function(title, msg, fn, scope, multiline){
2965             this.show({
2966                 title : title,
2967                 msg : msg,
2968                 buttons: this.OKCANCEL,
2969                 fn: fn,
2970                 minWidth:250,
2971                 scope : scope,
2972                 prompt:true,
2973                 multiline: multiline,
2974                 modal : true
2975             });
2976             return this;
2977         },
2978
2979         /**
2980          * Button config that displays a single OK button
2981          * @type Object
2982          */
2983         OK : {ok:true},
2984         /**
2985          * Button config that displays Yes and No buttons
2986          * @type Object
2987          */
2988         YESNO : {yes:true, no:true},
2989         /**
2990          * Button config that displays OK and Cancel buttons
2991          * @type Object
2992          */
2993         OKCANCEL : {ok:true, cancel:true},
2994         /**
2995          * Button config that displays Yes, No and Cancel buttons
2996          * @type Object
2997          */
2998         YESNOCANCEL : {yes:true, no:true, cancel:true},
2999
3000         /**
3001          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3002          * @type Number
3003          */
3004         defaultTextHeight : 75,
3005         /**
3006          * The maximum width in pixels of the message box (defaults to 600)
3007          * @type Number
3008          */
3009         maxWidth : 600,
3010         /**
3011          * The minimum width in pixels of the message box (defaults to 100)
3012          * @type Number
3013          */
3014         minWidth : 100,
3015         /**
3016          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3017          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3018          * @type Number
3019          */
3020         minProgressWidth : 250,
3021         /**
3022          * An object containing the default button text strings that can be overriden for localized language support.
3023          * Supported properties are: ok, cancel, yes and no.
3024          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3025          * @type Object
3026          */
3027         buttonText : {
3028             ok : "OK",
3029             cancel : "Cancel",
3030             yes : "Yes",
3031             no : "No"
3032         }
3033     };
3034 }();
3035
3036 /**
3037  * Shorthand for {@link Roo.MessageBox}
3038  */
3039 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3040 Roo.Msg = Roo.Msg || Roo.MessageBox;
3041 /*
3042  * - LGPL
3043  *
3044  * navbar
3045  * 
3046  */
3047
3048 /**
3049  * @class Roo.bootstrap.Navbar
3050  * @extends Roo.bootstrap.Component
3051  * Bootstrap Navbar class
3052
3053  * @constructor
3054  * Create a new Navbar
3055  * @param {Object} config The config object
3056  */
3057
3058
3059 Roo.bootstrap.Navbar = function(config){
3060     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3061     
3062 };
3063
3064 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3065     
3066     
3067    
3068     // private
3069     navItems : false,
3070     loadMask : false,
3071     
3072     
3073     getAutoCreate : function(){
3074         
3075         
3076         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3077         
3078     },
3079     
3080     initEvents :function ()
3081     {
3082         //Roo.log(this.el.select('.navbar-toggle',true));
3083         this.el.select('.navbar-toggle',true).on('click', function() {
3084            // Roo.log('click');
3085             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3086         }, this);
3087         
3088         var mark = {
3089             tag: "div",
3090             cls:"x-dlg-mask"
3091         }
3092         
3093         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3094         
3095         var size = this.el.getSize();
3096         this.maskEl.setSize(size.width, size.height);
3097         this.maskEl.enableDisplayMode("block");
3098         this.maskEl.hide();
3099         
3100         if(this.loadMask){
3101             this.maskEl.show();
3102         }
3103     },
3104     
3105     
3106     getChildContainer : function()
3107     {
3108         if (this.el.select('.collapse').getCount()) {
3109             return this.el.select('.collapse',true).first();
3110         }
3111         
3112         return this.el;
3113     },
3114     
3115     mask : function()
3116     {
3117         this.maskEl.show();
3118     },
3119     
3120     unmask : function()
3121     {
3122         this.maskEl.hide();
3123     } 
3124     
3125     
3126     
3127     
3128 });
3129
3130
3131
3132  
3133
3134  /*
3135  * - LGPL
3136  *
3137  * navbar
3138  * 
3139  */
3140
3141 /**
3142  * @class Roo.bootstrap.NavSimplebar
3143  * @extends Roo.bootstrap.Navbar
3144  * Bootstrap Sidebar class
3145  *
3146  * @cfg {Boolean} inverse is inverted color
3147  * 
3148  * @cfg {String} type (nav | pills | tabs)
3149  * @cfg {Boolean} arrangement stacked | justified
3150  * @cfg {String} align (left | right) alignment
3151  * 
3152  * @cfg {Boolean} main (true|false) main nav bar? default false
3153  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3154  * 
3155  * @cfg {String} tag (header|footer|nav|div) default is nav 
3156
3157  * 
3158  * 
3159  * 
3160  * @constructor
3161  * Create a new Sidebar
3162  * @param {Object} config The config object
3163  */
3164
3165
3166 Roo.bootstrap.NavSimplebar = function(config){
3167     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3168 };
3169
3170 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3171     
3172     inverse: false,
3173     
3174     type: false,
3175     arrangement: '',
3176     align : false,
3177     
3178     
3179     
3180     main : false,
3181     
3182     
3183     tag : false,
3184     
3185     
3186     getAutoCreate : function(){
3187         
3188         
3189         var cfg = {
3190             tag : this.tag || 'div',
3191             cls : 'navbar'
3192         };
3193           
3194         
3195         cfg.cn = [
3196             {
3197                 cls: 'nav',
3198                 tag : 'ul'
3199             }
3200         ];
3201         
3202          
3203         this.type = this.type || 'nav';
3204         if (['tabs','pills'].indexOf(this.type)!==-1) {
3205             cfg.cn[0].cls += ' nav-' + this.type
3206         
3207         
3208         } else {
3209             if (this.type!=='nav') {
3210                 Roo.log('nav type must be nav/tabs/pills')
3211             }
3212             cfg.cn[0].cls += ' navbar-nav'
3213         }
3214         
3215         
3216         
3217         
3218         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3219             cfg.cn[0].cls += ' nav-' + this.arrangement;
3220         }
3221         
3222         
3223         if (this.align === 'right') {
3224             cfg.cn[0].cls += ' navbar-right';
3225         }
3226         
3227         if (this.inverse) {
3228             cfg.cls += ' navbar-inverse';
3229             
3230         }
3231         
3232         
3233         return cfg;
3234     
3235         
3236     }
3237     
3238     
3239     
3240 });
3241
3242
3243
3244  
3245
3246  
3247        /*
3248  * - LGPL
3249  *
3250  * navbar
3251  * 
3252  */
3253
3254 /**
3255  * @class Roo.bootstrap.NavHeaderbar
3256  * @extends Roo.bootstrap.NavSimplebar
3257  * Bootstrap Sidebar class
3258  *
3259  * @cfg {String} brand what is brand
3260  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3261  * @cfg {String} brand_href href of the brand
3262  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3263  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3264  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3265  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3266  * 
3267  * @constructor
3268  * Create a new Sidebar
3269  * @param {Object} config The config object
3270  */
3271
3272
3273 Roo.bootstrap.NavHeaderbar = function(config){
3274     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3275       
3276 };
3277
3278 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3279     
3280     position: '',
3281     brand: '',
3282     brand_href: false,
3283     srButton : true,
3284     autohide : false,
3285     desktopCenter : false,
3286    
3287     
3288     getAutoCreate : function(){
3289         
3290         var   cfg = {
3291             tag: this.nav || 'nav',
3292             cls: 'navbar',
3293             role: 'navigation',
3294             cn: []
3295         };
3296         
3297         var cn = cfg.cn;
3298         if (this.desktopCenter) {
3299             cn.push({cls : 'container', cn : []});
3300             cn = cn[0].cn;
3301         }
3302         
3303         if(this.srButton){
3304             cn.push({
3305                 tag: 'div',
3306                 cls: 'navbar-header',
3307                 cn: [
3308                     {
3309                         tag: 'button',
3310                         type: 'button',
3311                         cls: 'navbar-toggle',
3312                         'data-toggle': 'collapse',
3313                         cn: [
3314                             {
3315                                 tag: 'span',
3316                                 cls: 'sr-only',
3317                                 html: 'Toggle navigation'
3318                             },
3319                             {
3320                                 tag: 'span',
3321                                 cls: 'icon-bar'
3322                             },
3323                             {
3324                                 tag: 'span',
3325                                 cls: 'icon-bar'
3326                             },
3327                             {
3328                                 tag: 'span',
3329                                 cls: 'icon-bar'
3330                             }
3331                         ]
3332                     }
3333                 ]
3334             });
3335         }
3336         
3337         cn.push({
3338             tag: 'div',
3339             cls: 'collapse navbar-collapse',
3340             cn : []
3341         });
3342         
3343         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3344         
3345         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3346             cfg.cls += ' navbar-' + this.position;
3347             
3348             // tag can override this..
3349             
3350             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3351         }
3352         
3353         if (this.brand !== '') {
3354             cn[0].cn.push({
3355                 tag: 'a',
3356                 href: this.brand_href ? this.brand_href : '#',
3357                 cls: 'navbar-brand',
3358                 cn: [
3359                 this.brand
3360                 ]
3361             });
3362         }
3363         
3364         if(this.main){
3365             cfg.cls += ' main-nav';
3366         }
3367         
3368         
3369         return cfg;
3370
3371         
3372     },
3373     getHeaderChildContainer : function()
3374     {
3375         if (this.el.select('.navbar-header').getCount()) {
3376             return this.el.select('.navbar-header',true).first();
3377         }
3378         
3379         return this.getChildContainer();
3380     },
3381     
3382     
3383     initEvents : function()
3384     {
3385         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3386         
3387         if (this.autohide) {
3388             
3389             var prevScroll = 0;
3390             var ft = this.el;
3391             
3392             Roo.get(document).on('scroll',function(e) {
3393                 var ns = Roo.get(document).getScroll().top;
3394                 var os = prevScroll;
3395                 prevScroll = ns;
3396                 
3397                 if(ns > os){
3398                     ft.removeClass('slideDown');
3399                     ft.addClass('slideUp');
3400                     return;
3401                 }
3402                 ft.removeClass('slideUp');
3403                 ft.addClass('slideDown');
3404                  
3405               
3406           },this);
3407         }
3408     }    
3409           
3410       
3411     
3412     
3413 });
3414
3415
3416
3417  
3418
3419  /*
3420  * - LGPL
3421  *
3422  * navbar
3423  * 
3424  */
3425
3426 /**
3427  * @class Roo.bootstrap.NavSidebar
3428  * @extends Roo.bootstrap.Navbar
3429  * Bootstrap Sidebar class
3430  * 
3431  * @constructor
3432  * Create a new Sidebar
3433  * @param {Object} config The config object
3434  */
3435
3436
3437 Roo.bootstrap.NavSidebar = function(config){
3438     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3439 };
3440
3441 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3442     
3443     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3444     
3445     getAutoCreate : function(){
3446         
3447         
3448         return  {
3449             tag: 'div',
3450             cls: 'sidebar sidebar-nav'
3451         };
3452     
3453         
3454     }
3455     
3456     
3457     
3458 });
3459
3460
3461
3462  
3463
3464  /*
3465  * - LGPL
3466  *
3467  * nav group
3468  * 
3469  */
3470
3471 /**
3472  * @class Roo.bootstrap.NavGroup
3473  * @extends Roo.bootstrap.Component
3474  * Bootstrap NavGroup class
3475  * @cfg {String} align left | right
3476  * @cfg {Boolean} inverse false | true
3477  * @cfg {String} type (nav|pills|tab) default nav
3478  * @cfg {String} navId - reference Id for navbar.
3479
3480  * 
3481  * @constructor
3482  * Create a new nav group
3483  * @param {Object} config The config object
3484  */
3485
3486 Roo.bootstrap.NavGroup = function(config){
3487     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3488     this.navItems = [];
3489    
3490     Roo.bootstrap.NavGroup.register(this);
3491      this.addEvents({
3492         /**
3493              * @event changed
3494              * Fires when the active item changes
3495              * @param {Roo.bootstrap.NavGroup} this
3496              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3497              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3498          */
3499         'changed': true
3500      });
3501     
3502 };
3503
3504 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3505     
3506     align: '',
3507     inverse: false,
3508     form: false,
3509     type: 'nav',
3510     navId : '',
3511     // private
3512     
3513     navItems : false, 
3514     
3515     getAutoCreate : function()
3516     {
3517         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3518         
3519         cfg = {
3520             tag : 'ul',
3521             cls: 'nav' 
3522         }
3523         
3524         if (['tabs','pills'].indexOf(this.type)!==-1) {
3525             cfg.cls += ' nav-' + this.type
3526         } else {
3527             if (this.type!=='nav') {
3528                 Roo.log('nav type must be nav/tabs/pills')
3529             }
3530             cfg.cls += ' navbar-nav'
3531         }
3532         
3533         if (this.parent().sidebar) {
3534             cfg = {
3535                 tag: 'ul',
3536                 cls: 'dashboard-menu sidebar-menu'
3537             }
3538             
3539             return cfg;
3540         }
3541         
3542         if (this.form === true) {
3543             cfg = {
3544                 tag: 'form',
3545                 cls: 'navbar-form'
3546             }
3547             
3548             if (this.align === 'right') {
3549                 cfg.cls += ' navbar-right';
3550             } else {
3551                 cfg.cls += ' navbar-left';
3552             }
3553         }
3554         
3555         if (this.align === 'right') {
3556             cfg.cls += ' navbar-right';
3557         }
3558         
3559         if (this.inverse) {
3560             cfg.cls += ' navbar-inverse';
3561             
3562         }
3563         
3564         
3565         return cfg;
3566     },
3567     /**
3568     * sets the active Navigation item
3569     * @param {Roo.bootstrap.NavItem} the new current navitem
3570     */
3571     setActiveItem : function(item)
3572     {
3573         var prev = false;
3574         Roo.each(this.navItems, function(v){
3575             if (v == item) {
3576                 return ;
3577             }
3578             if (v.isActive()) {
3579                 v.setActive(false, true);
3580                 prev = v;
3581                 
3582             }
3583             
3584         });
3585
3586         item.setActive(true, true);
3587         this.fireEvent('changed', this, item, prev);
3588         
3589         
3590     },
3591     /**
3592     * gets the active Navigation item
3593     * @return {Roo.bootstrap.NavItem} the current navitem
3594     */
3595     getActive : function()
3596     {
3597         
3598         var prev = false;
3599         Roo.each(this.navItems, function(v){
3600             
3601             if (v.isActive()) {
3602                 prev = v;
3603                 
3604             }
3605             
3606         });
3607         return prev;
3608     },
3609     
3610     indexOfNav : function()
3611     {
3612         
3613         var prev = false;
3614         Roo.each(this.navItems, function(v,i){
3615             
3616             if (v.isActive()) {
3617                 prev = i;
3618                 
3619             }
3620             
3621         });
3622         return prev;
3623     },
3624     /**
3625     * adds a Navigation item
3626     * @param {Roo.bootstrap.NavItem} the navitem to add
3627     */
3628     addItem : function(cfg)
3629     {
3630         var cn = new Roo.bootstrap.NavItem(cfg);
3631         this.register(cn);
3632         cn.parentId = this.id;
3633         cn.onRender(this.el, null);
3634         return cn;
3635     },
3636     /**
3637     * register a Navigation item
3638     * @param {Roo.bootstrap.NavItem} the navitem to add
3639     */
3640     register : function(item)
3641     {
3642         this.navItems.push( item);
3643         item.navId = this.navId;
3644     
3645     },
3646     
3647     /**
3648     * clear all the Navigation item
3649     */
3650    
3651     clearAll : function()
3652     {
3653         this.navItems = [];
3654         this.el.dom.innerHTML = '';
3655     },
3656     
3657     getNavItem: function(tabId)
3658     {
3659         var ret = false;
3660         Roo.each(this.navItems, function(e) {
3661             if (e.tabId == tabId) {
3662                ret =  e;
3663                return false;
3664             }
3665             return true;
3666             
3667         });
3668         return ret;
3669     },
3670     
3671     setActiveNext : function()
3672     {
3673         var i = this.indexOfNav(this.getActive());
3674         if (i > this.navItems.length) {
3675             return;
3676         }
3677         this.setActiveItem(this.navItems[i+1]);
3678     },
3679     setActivePrev : function()
3680     {
3681         var i = this.indexOfNav(this.getActive());
3682         if (i  < 1) {
3683             return;
3684         }
3685         this.setActiveItem(this.navItems[i-1]);
3686     },
3687     clearWasActive : function(except) {
3688         Roo.each(this.navItems, function(e) {
3689             if (e.tabId != except.tabId && e.was_active) {
3690                e.was_active = false;
3691                return false;
3692             }
3693             return true;
3694             
3695         });
3696     },
3697     getWasActive : function ()
3698     {
3699         var r = false;
3700         Roo.each(this.navItems, function(e) {
3701             if (e.was_active) {
3702                r = e;
3703                return false;
3704             }
3705             return true;
3706             
3707         });
3708         return r;
3709     }
3710     
3711     
3712 });
3713
3714  
3715 Roo.apply(Roo.bootstrap.NavGroup, {
3716     
3717     groups: {},
3718      /**
3719     * register a Navigation Group
3720     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3721     */
3722     register : function(navgrp)
3723     {
3724         this.groups[navgrp.navId] = navgrp;
3725         
3726     },
3727     /**
3728     * fetch a Navigation Group based on the navigation ID
3729     * @param {string} the navgroup to add
3730     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3731     */
3732     get: function(navId) {
3733         if (typeof(this.groups[navId]) == 'undefined') {
3734             return false;
3735             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3736         }
3737         return this.groups[navId] ;
3738     }
3739     
3740     
3741     
3742 });
3743
3744  /*
3745  * - LGPL
3746  *
3747  * row
3748  * 
3749  */
3750
3751 /**
3752  * @class Roo.bootstrap.NavItem
3753  * @extends Roo.bootstrap.Component
3754  * Bootstrap Navbar.NavItem class
3755  * @cfg {String} href  link to
3756  * @cfg {String} html content of button
3757  * @cfg {String} badge text inside badge
3758  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3759  * @cfg {String} glyphicon name of glyphicon
3760  * @cfg {String} icon name of font awesome icon
3761  * @cfg {Boolean} active Is item active
3762  * @cfg {Boolean} disabled Is item disabled
3763  
3764  * @cfg {Boolean} preventDefault (true | false) default false
3765  * @cfg {String} tabId the tab that this item activates.
3766  * @cfg {String} tagtype (a|span) render as a href or span?
3767  * @cfg {Boolean} animateRef (true|false) link to element default false
3768   
3769  * @constructor
3770  * Create a new Navbar Item
3771  * @param {Object} config The config object
3772  */
3773 Roo.bootstrap.NavItem = function(config){
3774     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3775     this.addEvents({
3776         // raw events
3777         /**
3778          * @event click
3779          * The raw click event for the entire grid.
3780          * @param {Roo.EventObject} e
3781          */
3782         "click" : true,
3783          /**
3784             * @event changed
3785             * Fires when the active item active state changes
3786             * @param {Roo.bootstrap.NavItem} this
3787             * @param {boolean} state the new state
3788              
3789          */
3790         'changed': true,
3791         /**
3792             * @event scrollto
3793             * Fires when scroll to element
3794             * @param {Roo.bootstrap.NavItem} this
3795             * @param {Object} options
3796             * @param {Roo.EventObject} e
3797              
3798          */
3799         'scrollto': true
3800     });
3801    
3802 };
3803
3804 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3805     
3806     href: false,
3807     html: '',
3808     badge: '',
3809     icon: false,
3810     glyphicon: false,
3811     active: false,
3812     preventDefault : false,
3813     tabId : false,
3814     tagtype : 'a',
3815     disabled : false,
3816     animateRef : false,
3817     was_active : false,
3818     
3819     getAutoCreate : function(){
3820          
3821         var cfg = {
3822             tag: 'li',
3823             cls: 'nav-item'
3824             
3825         }
3826         if (this.active) {
3827             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3828         }
3829         if (this.disabled) {
3830             cfg.cls += ' disabled';
3831         }
3832         
3833         if (this.href || this.html || this.glyphicon || this.icon) {
3834             cfg.cn = [
3835                 {
3836                     tag: this.tagtype,
3837                     href : this.href || "#",
3838                     html: this.html || ''
3839                 }
3840             ];
3841             
3842             if (this.icon) {
3843                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3844             }
3845
3846             if(this.glyphicon) {
3847                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3848             }
3849             
3850             if (this.menu) {
3851                 
3852                 cfg.cn[0].html += " <span class='caret'></span>";
3853              
3854             }
3855             
3856             if (this.badge !== '') {
3857                  
3858                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3859             }
3860         }
3861         
3862         
3863         
3864         return cfg;
3865     },
3866     initEvents: function() 
3867     {
3868         if (typeof (this.menu) != 'undefined') {
3869             this.menu.parentType = this.xtype;
3870             this.menu.triggerEl = this.el;
3871             this.menu = this.addxtype(Roo.apply({}, this.menu));
3872         }
3873         
3874         this.el.select('a',true).on('click', this.onClick, this);
3875         
3876         if(this.tagtype == 'span'){
3877             this.el.select('span',true).on('click', this.onClick, this);
3878         }
3879        
3880         // at this point parent should be available..
3881         this.parent().register(this);
3882     },
3883     
3884     onClick : function(e)
3885     {
3886         if(
3887                 this.preventDefault || 
3888                 this.href == '#' ||
3889                 (this.animateRef && this.href.charAt(0) == '#')
3890         ){
3891             e.preventDefault();
3892         }
3893         
3894         if (this.disabled) {
3895             return;
3896         }
3897         
3898         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3899         if (tg && tg.transition) {
3900             Roo.log("waiting for the transitionend");
3901             return;
3902         }
3903         
3904         Roo.log("fire event clicked");
3905         if(this.fireEvent('click', this, e) === false){
3906             return;
3907         };
3908         
3909         if(this.tagtype == 'span'){
3910             return;
3911         }
3912         
3913         if(this.animateRef && this.href.charAt(0) == '#'){
3914             this.scrollToElement(e);
3915             return;
3916         }
3917         
3918         var p = this.parent();
3919         if (['tabs','pills'].indexOf(p.type)!==-1) {
3920             if (typeof(p.setActiveItem) !== 'undefined') {
3921                 p.setActiveItem(this);
3922             }
3923         }
3924         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3925         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3926             // remove the collapsed menu expand...
3927             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3928         }
3929         
3930     },
3931     
3932     isActive: function () {
3933         return this.active
3934     },
3935     setActive : function(state, fire, is_was_active)
3936     {
3937         if (this.active && !state & this.navId) {
3938             this.was_active = true;
3939             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3940             if (nv) {
3941                 nv.clearWasActive(this);
3942             }
3943             
3944         }
3945         this.active = state;
3946         
3947         if (!state ) {
3948             this.el.removeClass('active');
3949         } else if (!this.el.hasClass('active')) {
3950             this.el.addClass('active');
3951         }
3952         if (fire) {
3953             this.fireEvent('changed', this, state);
3954         }
3955         
3956         // show a panel if it's registered and related..
3957         
3958         if (!this.navId || !this.tabId || !state || is_was_active) {
3959             return;
3960         }
3961         
3962         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3963         if (!tg) {
3964             return;
3965         }
3966         var pan = tg.getPanelByName(this.tabId);
3967         if (!pan) {
3968             return;
3969         }
3970         // if we can not flip to new panel - go back to old nav highlight..
3971         if (false == tg.showPanel(pan)) {
3972             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3973             if (nv) {
3974                 var onav = nv.getWasActive();
3975                 if (onav) {
3976                     onav.setActive(true, false, true);
3977                 }
3978             }
3979             
3980         }
3981         
3982         
3983         
3984     },
3985      // this should not be here...
3986     setDisabled : function(state)
3987     {
3988         this.disabled = state;
3989         if (!state ) {
3990             this.el.removeClass('disabled');
3991         } else if (!this.el.hasClass('disabled')) {
3992             this.el.addClass('disabled');
3993         }
3994         
3995     },
3996     
3997     /**
3998      * Fetch the element to display the tooltip on.
3999      * @return {Roo.Element} defaults to this.el
4000      */
4001     tooltipEl : function()
4002     {
4003         return this.el.select('' + this.tagtype + '', true).first();
4004     },
4005     
4006     scrollToElement : function(e)
4007     {
4008         var c = document.body;
4009         
4010         var target = Roo.get(c).select('a[name=' + this.href.replace('#', '') +']', true).first();
4011         
4012         if(!target){
4013             return;
4014         }
4015
4016         var o = target.calcOffsetsTo(c);
4017         
4018         var options = {
4019             target : target,
4020             value : o[1]
4021         }
4022         
4023         this.fireEvent('scrollto', this, options, e);
4024         
4025         Roo.get(c).scrollTo('top', options.value, true);
4026         
4027         return;
4028     }
4029 });
4030  
4031
4032  /*
4033  * - LGPL
4034  *
4035  * sidebar item
4036  *
4037  *  li
4038  *    <span> icon </span>
4039  *    <span> text </span>
4040  *    <span>badge </span>
4041  */
4042
4043 /**
4044  * @class Roo.bootstrap.NavSidebarItem
4045  * @extends Roo.bootstrap.NavItem
4046  * Bootstrap Navbar.NavSidebarItem class
4047  * @constructor
4048  * Create a new Navbar Button
4049  * @param {Object} config The config object
4050  */
4051 Roo.bootstrap.NavSidebarItem = function(config){
4052     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4053     this.addEvents({
4054         // raw events
4055         /**
4056          * @event click
4057          * The raw click event for the entire grid.
4058          * @param {Roo.EventObject} e
4059          */
4060         "click" : true,
4061          /**
4062             * @event changed
4063             * Fires when the active item active state changes
4064             * @param {Roo.bootstrap.NavSidebarItem} this
4065             * @param {boolean} state the new state
4066              
4067          */
4068         'changed': true
4069     });
4070    
4071 };
4072
4073 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4074     
4075     
4076     getAutoCreate : function(){
4077         
4078         
4079         var a = {
4080                 tag: 'a',
4081                 href : this.href || '#',
4082                 cls: '',
4083                 html : '',
4084                 cn : []
4085         };
4086         var cfg = {
4087             tag: 'li',
4088             cls: '',
4089             cn: [ a ]
4090         }
4091         var span = {
4092             tag: 'span',
4093             html : this.html || ''
4094         }
4095         
4096         
4097         if (this.active) {
4098             cfg.cls += ' active';
4099         }
4100         
4101         // left icon..
4102         if (this.glyphicon || this.icon) {
4103             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4104             a.cn.push({ tag : 'i', cls : c }) ;
4105         }
4106         // html..
4107         a.cn.push(span);
4108         // then badge..
4109         if (this.badge !== '') {
4110             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4111         }
4112         // fi
4113         if (this.menu) {
4114             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4115             a.cls += 'dropdown-toggle treeview' ;
4116             
4117         }
4118         
4119         
4120         
4121         return cfg;
4122          
4123            
4124     }
4125    
4126      
4127  
4128 });
4129  
4130
4131  /*
4132  * - LGPL
4133  *
4134  * row
4135  * 
4136  */
4137
4138 /**
4139  * @class Roo.bootstrap.Row
4140  * @extends Roo.bootstrap.Component
4141  * Bootstrap Row class (contains columns...)
4142  * 
4143  * @constructor
4144  * Create a new Row
4145  * @param {Object} config The config object
4146  */
4147
4148 Roo.bootstrap.Row = function(config){
4149     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4150 };
4151
4152 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4153     
4154     getAutoCreate : function(){
4155        return {
4156             cls: 'row clearfix'
4157        };
4158     }
4159     
4160     
4161 });
4162
4163  
4164
4165  /*
4166  * - LGPL
4167  *
4168  * element
4169  * 
4170  */
4171
4172 /**
4173  * @class Roo.bootstrap.Element
4174  * @extends Roo.bootstrap.Component
4175  * Bootstrap Element class
4176  * @cfg {String} html contents of the element
4177  * @cfg {String} tag tag of the element
4178  * @cfg {String} cls class of the element
4179  * 
4180  * @constructor
4181  * Create a new Element
4182  * @param {Object} config The config object
4183  */
4184
4185 Roo.bootstrap.Element = function(config){
4186     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4187 };
4188
4189 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4190     
4191     tag: 'div',
4192     cls: '',
4193     html: '',
4194      
4195     
4196     getAutoCreate : function(){
4197         
4198         var cfg = {
4199             tag: this.tag,
4200             cls: this.cls,
4201             html: this.html
4202         }
4203         
4204         
4205         
4206         return cfg;
4207     },
4208     
4209     getValue : function()
4210     {
4211         return this.el.dom.innerHTML;
4212     },
4213     
4214     setValue : function(value)
4215     {
4216         this.el.dom.innerHTML = value;
4217     }
4218    
4219 });
4220
4221  
4222
4223  /*
4224  * - LGPL
4225  *
4226  * pagination
4227  * 
4228  */
4229
4230 /**
4231  * @class Roo.bootstrap.Pagination
4232  * @extends Roo.bootstrap.Component
4233  * Bootstrap Pagination class
4234  * @cfg {String} size xs | sm | md | lg
4235  * @cfg {Boolean} inverse false | true
4236  * 
4237  * @constructor
4238  * Create a new Pagination
4239  * @param {Object} config The config object
4240  */
4241
4242 Roo.bootstrap.Pagination = function(config){
4243     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4244 };
4245
4246 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4247     
4248     cls: false,
4249     size: false,
4250     inverse: false,
4251     
4252     getAutoCreate : function(){
4253         var cfg = {
4254             tag: 'ul',
4255                 cls: 'pagination'
4256         };
4257         if (this.inverse) {
4258             cfg.cls += ' inverse';
4259         }
4260         if (this.html) {
4261             cfg.html=this.html;
4262         }
4263         if (this.cls) {
4264             cfg.cls += " " + this.cls;
4265         }
4266         return cfg;
4267     }
4268    
4269 });
4270
4271  
4272
4273  /*
4274  * - LGPL
4275  *
4276  * Pagination item
4277  * 
4278  */
4279
4280
4281 /**
4282  * @class Roo.bootstrap.PaginationItem
4283  * @extends Roo.bootstrap.Component
4284  * Bootstrap PaginationItem class
4285  * @cfg {String} html text
4286  * @cfg {String} href the link
4287  * @cfg {Boolean} preventDefault (true | false) default true
4288  * @cfg {Boolean} active (true | false) default false
4289  * @cfg {Boolean} disabled default false
4290  * 
4291  * 
4292  * @constructor
4293  * Create a new PaginationItem
4294  * @param {Object} config The config object
4295  */
4296
4297
4298 Roo.bootstrap.PaginationItem = function(config){
4299     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4300     this.addEvents({
4301         // raw events
4302         /**
4303          * @event click
4304          * The raw click event for the entire grid.
4305          * @param {Roo.EventObject} e
4306          */
4307         "click" : true
4308     });
4309 };
4310
4311 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4312     
4313     href : false,
4314     html : false,
4315     preventDefault: true,
4316     active : false,
4317     cls : false,
4318     disabled: false,
4319     
4320     getAutoCreate : function(){
4321         var cfg= {
4322             tag: 'li',
4323             cn: [
4324                 {
4325                     tag : 'a',
4326                     href : this.href ? this.href : '#',
4327                     html : this.html ? this.html : ''
4328                 }
4329             ]
4330         };
4331         
4332         if(this.cls){
4333             cfg.cls = this.cls;
4334         }
4335         
4336         if(this.disabled){
4337             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4338         }
4339         
4340         if(this.active){
4341             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4342         }
4343         
4344         return cfg;
4345     },
4346     
4347     initEvents: function() {
4348         
4349         this.el.on('click', this.onClick, this);
4350         
4351     },
4352     onClick : function(e)
4353     {
4354         Roo.log('PaginationItem on click ');
4355         if(this.preventDefault){
4356             e.preventDefault();
4357         }
4358         
4359         if(this.disabled){
4360             return;
4361         }
4362         
4363         this.fireEvent('click', this, e);
4364     }
4365    
4366 });
4367
4368  
4369
4370  /*
4371  * - LGPL
4372  *
4373  * slider
4374  * 
4375  */
4376
4377
4378 /**
4379  * @class Roo.bootstrap.Slider
4380  * @extends Roo.bootstrap.Component
4381  * Bootstrap Slider class
4382  *    
4383  * @constructor
4384  * Create a new Slider
4385  * @param {Object} config The config object
4386  */
4387
4388 Roo.bootstrap.Slider = function(config){
4389     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4390 };
4391
4392 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4393     
4394     getAutoCreate : function(){
4395         
4396         var cfg = {
4397             tag: 'div',
4398             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4399             cn: [
4400                 {
4401                     tag: 'a',
4402                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4403                 }
4404             ]
4405         }
4406         
4407         return cfg;
4408     }
4409    
4410 });
4411
4412  /*
4413  * Based on:
4414  * Ext JS Library 1.1.1
4415  * Copyright(c) 2006-2007, Ext JS, LLC.
4416  *
4417  * Originally Released Under LGPL - original licence link has changed is not relivant.
4418  *
4419  * Fork - LGPL
4420  * <script type="text/javascript">
4421  */
4422  
4423
4424 /**
4425  * @class Roo.grid.ColumnModel
4426  * @extends Roo.util.Observable
4427  * This is the default implementation of a ColumnModel used by the Grid. It defines
4428  * the columns in the grid.
4429  * <br>Usage:<br>
4430  <pre><code>
4431  var colModel = new Roo.grid.ColumnModel([
4432         {header: "Ticker", width: 60, sortable: true, locked: true},
4433         {header: "Company Name", width: 150, sortable: true},
4434         {header: "Market Cap.", width: 100, sortable: true},
4435         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4436         {header: "Employees", width: 100, sortable: true, resizable: false}
4437  ]);
4438  </code></pre>
4439  * <p>
4440  
4441  * The config options listed for this class are options which may appear in each
4442  * individual column definition.
4443  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4444  * @constructor
4445  * @param {Object} config An Array of column config objects. See this class's
4446  * config objects for details.
4447 */
4448 Roo.grid.ColumnModel = function(config){
4449         /**
4450      * The config passed into the constructor
4451      */
4452     this.config = config;
4453     this.lookup = {};
4454
4455     // if no id, create one
4456     // if the column does not have a dataIndex mapping,
4457     // map it to the order it is in the config
4458     for(var i = 0, len = config.length; i < len; i++){
4459         var c = config[i];
4460         if(typeof c.dataIndex == "undefined"){
4461             c.dataIndex = i;
4462         }
4463         if(typeof c.renderer == "string"){
4464             c.renderer = Roo.util.Format[c.renderer];
4465         }
4466         if(typeof c.id == "undefined"){
4467             c.id = Roo.id();
4468         }
4469         if(c.editor && c.editor.xtype){
4470             c.editor  = Roo.factory(c.editor, Roo.grid);
4471         }
4472         if(c.editor && c.editor.isFormField){
4473             c.editor = new Roo.grid.GridEditor(c.editor);
4474         }
4475         this.lookup[c.id] = c;
4476     }
4477
4478     /**
4479      * The width of columns which have no width specified (defaults to 100)
4480      * @type Number
4481      */
4482     this.defaultWidth = 100;
4483
4484     /**
4485      * Default sortable of columns which have no sortable specified (defaults to false)
4486      * @type Boolean
4487      */
4488     this.defaultSortable = false;
4489
4490     this.addEvents({
4491         /**
4492              * @event widthchange
4493              * Fires when the width of a column changes.
4494              * @param {ColumnModel} this
4495              * @param {Number} columnIndex The column index
4496              * @param {Number} newWidth The new width
4497              */
4498             "widthchange": true,
4499         /**
4500              * @event headerchange
4501              * Fires when the text of a header changes.
4502              * @param {ColumnModel} this
4503              * @param {Number} columnIndex The column index
4504              * @param {Number} newText The new header text
4505              */
4506             "headerchange": true,
4507         /**
4508              * @event hiddenchange
4509              * Fires when a column is hidden or "unhidden".
4510              * @param {ColumnModel} this
4511              * @param {Number} columnIndex The column index
4512              * @param {Boolean} hidden true if hidden, false otherwise
4513              */
4514             "hiddenchange": true,
4515             /**
4516          * @event columnmoved
4517          * Fires when a column is moved.
4518          * @param {ColumnModel} this
4519          * @param {Number} oldIndex
4520          * @param {Number} newIndex
4521          */
4522         "columnmoved" : true,
4523         /**
4524          * @event columlockchange
4525          * Fires when a column's locked state is changed
4526          * @param {ColumnModel} this
4527          * @param {Number} colIndex
4528          * @param {Boolean} locked true if locked
4529          */
4530         "columnlockchange" : true
4531     });
4532     Roo.grid.ColumnModel.superclass.constructor.call(this);
4533 };
4534 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4535     /**
4536      * @cfg {String} header The header text to display in the Grid view.
4537      */
4538     /**
4539      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4540      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4541      * specified, the column's index is used as an index into the Record's data Array.
4542      */
4543     /**
4544      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4545      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4546      */
4547     /**
4548      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4549      * Defaults to the value of the {@link #defaultSortable} property.
4550      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4551      */
4552     /**
4553      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4554      */
4555     /**
4556      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4557      */
4558     /**
4559      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4560      */
4561     /**
4562      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4563      */
4564     /**
4565      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4566      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4567      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4568      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4569      */
4570        /**
4571      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4572      */
4573     /**
4574      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4575      */
4576     /**
4577      * @cfg {String} cursor (Optional)
4578      */
4579     /**
4580      * @cfg {String} tooltip (Optional)
4581      */
4582     /**
4583      * Returns the id of the column at the specified index.
4584      * @param {Number} index The column index
4585      * @return {String} the id
4586      */
4587     getColumnId : function(index){
4588         return this.config[index].id;
4589     },
4590
4591     /**
4592      * Returns the column for a specified id.
4593      * @param {String} id The column id
4594      * @return {Object} the column
4595      */
4596     getColumnById : function(id){
4597         return this.lookup[id];
4598     },
4599
4600     
4601     /**
4602      * Returns the column for a specified dataIndex.
4603      * @param {String} dataIndex The column dataIndex
4604      * @return {Object|Boolean} the column or false if not found
4605      */
4606     getColumnByDataIndex: function(dataIndex){
4607         var index = this.findColumnIndex(dataIndex);
4608         return index > -1 ? this.config[index] : false;
4609     },
4610     
4611     /**
4612      * Returns the index for a specified column id.
4613      * @param {String} id The column id
4614      * @return {Number} the index, or -1 if not found
4615      */
4616     getIndexById : function(id){
4617         for(var i = 0, len = this.config.length; i < len; i++){
4618             if(this.config[i].id == id){
4619                 return i;
4620             }
4621         }
4622         return -1;
4623     },
4624     
4625     /**
4626      * Returns the index for a specified column dataIndex.
4627      * @param {String} dataIndex The column dataIndex
4628      * @return {Number} the index, or -1 if not found
4629      */
4630     
4631     findColumnIndex : function(dataIndex){
4632         for(var i = 0, len = this.config.length; i < len; i++){
4633             if(this.config[i].dataIndex == dataIndex){
4634                 return i;
4635             }
4636         }
4637         return -1;
4638     },
4639     
4640     
4641     moveColumn : function(oldIndex, newIndex){
4642         var c = this.config[oldIndex];
4643         this.config.splice(oldIndex, 1);
4644         this.config.splice(newIndex, 0, c);
4645         this.dataMap = null;
4646         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4647     },
4648
4649     isLocked : function(colIndex){
4650         return this.config[colIndex].locked === true;
4651     },
4652
4653     setLocked : function(colIndex, value, suppressEvent){
4654         if(this.isLocked(colIndex) == value){
4655             return;
4656         }
4657         this.config[colIndex].locked = value;
4658         if(!suppressEvent){
4659             this.fireEvent("columnlockchange", this, colIndex, value);
4660         }
4661     },
4662
4663     getTotalLockedWidth : function(){
4664         var totalWidth = 0;
4665         for(var i = 0; i < this.config.length; i++){
4666             if(this.isLocked(i) && !this.isHidden(i)){
4667                 this.totalWidth += this.getColumnWidth(i);
4668             }
4669         }
4670         return totalWidth;
4671     },
4672
4673     getLockedCount : function(){
4674         for(var i = 0, len = this.config.length; i < len; i++){
4675             if(!this.isLocked(i)){
4676                 return i;
4677             }
4678         }
4679     },
4680
4681     /**
4682      * Returns the number of columns.
4683      * @return {Number}
4684      */
4685     getColumnCount : function(visibleOnly){
4686         if(visibleOnly === true){
4687             var c = 0;
4688             for(var i = 0, len = this.config.length; i < len; i++){
4689                 if(!this.isHidden(i)){
4690                     c++;
4691                 }
4692             }
4693             return c;
4694         }
4695         return this.config.length;
4696     },
4697
4698     /**
4699      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4700      * @param {Function} fn
4701      * @param {Object} scope (optional)
4702      * @return {Array} result
4703      */
4704     getColumnsBy : function(fn, scope){
4705         var r = [];
4706         for(var i = 0, len = this.config.length; i < len; i++){
4707             var c = this.config[i];
4708             if(fn.call(scope||this, c, i) === true){
4709                 r[r.length] = c;
4710             }
4711         }
4712         return r;
4713     },
4714
4715     /**
4716      * Returns true if the specified column is sortable.
4717      * @param {Number} col The column index
4718      * @return {Boolean}
4719      */
4720     isSortable : function(col){
4721         if(typeof this.config[col].sortable == "undefined"){
4722             return this.defaultSortable;
4723         }
4724         return this.config[col].sortable;
4725     },
4726
4727     /**
4728      * Returns the rendering (formatting) function defined for the column.
4729      * @param {Number} col The column index.
4730      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4731      */
4732     getRenderer : function(col){
4733         if(!this.config[col].renderer){
4734             return Roo.grid.ColumnModel.defaultRenderer;
4735         }
4736         return this.config[col].renderer;
4737     },
4738
4739     /**
4740      * Sets the rendering (formatting) function for a column.
4741      * @param {Number} col The column index
4742      * @param {Function} fn The function to use to process the cell's raw data
4743      * to return HTML markup for the grid view. The render function is called with
4744      * the following parameters:<ul>
4745      * <li>Data value.</li>
4746      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4747      * <li>css A CSS style string to apply to the table cell.</li>
4748      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4749      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4750      * <li>Row index</li>
4751      * <li>Column index</li>
4752      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4753      */
4754     setRenderer : function(col, fn){
4755         this.config[col].renderer = fn;
4756     },
4757
4758     /**
4759      * Returns the width for the specified column.
4760      * @param {Number} col The column index
4761      * @return {Number}
4762      */
4763     getColumnWidth : function(col){
4764         return this.config[col].width * 1 || this.defaultWidth;
4765     },
4766
4767     /**
4768      * Sets the width for a column.
4769      * @param {Number} col The column index
4770      * @param {Number} width The new width
4771      */
4772     setColumnWidth : function(col, width, suppressEvent){
4773         this.config[col].width = width;
4774         this.totalWidth = null;
4775         if(!suppressEvent){
4776              this.fireEvent("widthchange", this, col, width);
4777         }
4778     },
4779
4780     /**
4781      * Returns the total width of all columns.
4782      * @param {Boolean} includeHidden True to include hidden column widths
4783      * @return {Number}
4784      */
4785     getTotalWidth : function(includeHidden){
4786         if(!this.totalWidth){
4787             this.totalWidth = 0;
4788             for(var i = 0, len = this.config.length; i < len; i++){
4789                 if(includeHidden || !this.isHidden(i)){
4790                     this.totalWidth += this.getColumnWidth(i);
4791                 }
4792             }
4793         }
4794         return this.totalWidth;
4795     },
4796
4797     /**
4798      * Returns the header for the specified column.
4799      * @param {Number} col The column index
4800      * @return {String}
4801      */
4802     getColumnHeader : function(col){
4803         return this.config[col].header;
4804     },
4805
4806     /**
4807      * Sets the header for a column.
4808      * @param {Number} col The column index
4809      * @param {String} header The new header
4810      */
4811     setColumnHeader : function(col, header){
4812         this.config[col].header = header;
4813         this.fireEvent("headerchange", this, col, header);
4814     },
4815
4816     /**
4817      * Returns the tooltip for the specified column.
4818      * @param {Number} col The column index
4819      * @return {String}
4820      */
4821     getColumnTooltip : function(col){
4822             return this.config[col].tooltip;
4823     },
4824     /**
4825      * Sets the tooltip for a column.
4826      * @param {Number} col The column index
4827      * @param {String} tooltip The new tooltip
4828      */
4829     setColumnTooltip : function(col, tooltip){
4830             this.config[col].tooltip = tooltip;
4831     },
4832
4833     /**
4834      * Returns the dataIndex for the specified column.
4835      * @param {Number} col The column index
4836      * @return {Number}
4837      */
4838     getDataIndex : function(col){
4839         return this.config[col].dataIndex;
4840     },
4841
4842     /**
4843      * Sets the dataIndex for a column.
4844      * @param {Number} col The column index
4845      * @param {Number} dataIndex The new dataIndex
4846      */
4847     setDataIndex : function(col, dataIndex){
4848         this.config[col].dataIndex = dataIndex;
4849     },
4850
4851     
4852     
4853     /**
4854      * Returns true if the cell is editable.
4855      * @param {Number} colIndex The column index
4856      * @param {Number} rowIndex The row index
4857      * @return {Boolean}
4858      */
4859     isCellEditable : function(colIndex, rowIndex){
4860         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4861     },
4862
4863     /**
4864      * Returns the editor defined for the cell/column.
4865      * return false or null to disable editing.
4866      * @param {Number} colIndex The column index
4867      * @param {Number} rowIndex The row index
4868      * @return {Object}
4869      */
4870     getCellEditor : function(colIndex, rowIndex){
4871         return this.config[colIndex].editor;
4872     },
4873
4874     /**
4875      * Sets if a column is editable.
4876      * @param {Number} col The column index
4877      * @param {Boolean} editable True if the column is editable
4878      */
4879     setEditable : function(col, editable){
4880         this.config[col].editable = editable;
4881     },
4882
4883
4884     /**
4885      * Returns true if the column is hidden.
4886      * @param {Number} colIndex The column index
4887      * @return {Boolean}
4888      */
4889     isHidden : function(colIndex){
4890         return this.config[colIndex].hidden;
4891     },
4892
4893
4894     /**
4895      * Returns true if the column width cannot be changed
4896      */
4897     isFixed : function(colIndex){
4898         return this.config[colIndex].fixed;
4899     },
4900
4901     /**
4902      * Returns true if the column can be resized
4903      * @return {Boolean}
4904      */
4905     isResizable : function(colIndex){
4906         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4907     },
4908     /**
4909      * Sets if a column is hidden.
4910      * @param {Number} colIndex The column index
4911      * @param {Boolean} hidden True if the column is hidden
4912      */
4913     setHidden : function(colIndex, hidden){
4914         this.config[colIndex].hidden = hidden;
4915         this.totalWidth = null;
4916         this.fireEvent("hiddenchange", this, colIndex, hidden);
4917     },
4918
4919     /**
4920      * Sets the editor for a column.
4921      * @param {Number} col The column index
4922      * @param {Object} editor The editor object
4923      */
4924     setEditor : function(col, editor){
4925         this.config[col].editor = editor;
4926     }
4927 });
4928
4929 Roo.grid.ColumnModel.defaultRenderer = function(value){
4930         if(typeof value == "string" && value.length < 1){
4931             return "&#160;";
4932         }
4933         return value;
4934 };
4935
4936 // Alias for backwards compatibility
4937 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4938 /*
4939  * Based on:
4940  * Ext JS Library 1.1.1
4941  * Copyright(c) 2006-2007, Ext JS, LLC.
4942  *
4943  * Originally Released Under LGPL - original licence link has changed is not relivant.
4944  *
4945  * Fork - LGPL
4946  * <script type="text/javascript">
4947  */
4948  
4949 /**
4950  * @class Roo.LoadMask
4951  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4952  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4953  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4954  * element's UpdateManager load indicator and will be destroyed after the initial load.
4955  * @constructor
4956  * Create a new LoadMask
4957  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4958  * @param {Object} config The config object
4959  */
4960 Roo.LoadMask = function(el, config){
4961     this.el = Roo.get(el);
4962     Roo.apply(this, config);
4963     if(this.store){
4964         this.store.on('beforeload', this.onBeforeLoad, this);
4965         this.store.on('load', this.onLoad, this);
4966         this.store.on('loadexception', this.onLoadException, this);
4967         this.removeMask = false;
4968     }else{
4969         var um = this.el.getUpdateManager();
4970         um.showLoadIndicator = false; // disable the default indicator
4971         um.on('beforeupdate', this.onBeforeLoad, this);
4972         um.on('update', this.onLoad, this);
4973         um.on('failure', this.onLoad, this);
4974         this.removeMask = true;
4975     }
4976 };
4977
4978 Roo.LoadMask.prototype = {
4979     /**
4980      * @cfg {Boolean} removeMask
4981      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4982      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4983      */
4984     /**
4985      * @cfg {String} msg
4986      * The text to display in a centered loading message box (defaults to 'Loading...')
4987      */
4988     msg : 'Loading...',
4989     /**
4990      * @cfg {String} msgCls
4991      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4992      */
4993     msgCls : 'x-mask-loading',
4994
4995     /**
4996      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4997      * @type Boolean
4998      */
4999     disabled: false,
5000
5001     /**
5002      * Disables the mask to prevent it from being displayed
5003      */
5004     disable : function(){
5005        this.disabled = true;
5006     },
5007
5008     /**
5009      * Enables the mask so that it can be displayed
5010      */
5011     enable : function(){
5012         this.disabled = false;
5013     },
5014     
5015     onLoadException : function()
5016     {
5017         Roo.log(arguments);
5018         
5019         if (typeof(arguments[3]) != 'undefined') {
5020             Roo.MessageBox.alert("Error loading",arguments[3]);
5021         } 
5022         /*
5023         try {
5024             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5025                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5026             }   
5027         } catch(e) {
5028             
5029         }
5030         */
5031     
5032         
5033         
5034         this.el.unmask(this.removeMask);
5035     },
5036     // private
5037     onLoad : function()
5038     {
5039         this.el.unmask(this.removeMask);
5040     },
5041
5042     // private
5043     onBeforeLoad : function(){
5044         if(!this.disabled){
5045             this.el.mask(this.msg, this.msgCls);
5046         }
5047     },
5048
5049     // private
5050     destroy : function(){
5051         if(this.store){
5052             this.store.un('beforeload', this.onBeforeLoad, this);
5053             this.store.un('load', this.onLoad, this);
5054             this.store.un('loadexception', this.onLoadException, this);
5055         }else{
5056             var um = this.el.getUpdateManager();
5057             um.un('beforeupdate', this.onBeforeLoad, this);
5058             um.un('update', this.onLoad, this);
5059             um.un('failure', this.onLoad, this);
5060         }
5061     }
5062 };/*
5063  * - LGPL
5064  *
5065  * table
5066  * 
5067  */
5068
5069 /**
5070  * @class Roo.bootstrap.Table
5071  * @extends Roo.bootstrap.Component
5072  * Bootstrap Table class
5073  * @cfg {String} cls table class
5074  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5075  * @cfg {String} bgcolor Specifies the background color for a table
5076  * @cfg {Number} border Specifies whether the table cells should have borders or not
5077  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5078  * @cfg {Number} cellspacing Specifies the space between cells
5079  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5080  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5081  * @cfg {String} sortable Specifies that the table should be sortable
5082  * @cfg {String} summary Specifies a summary of the content of a table
5083  * @cfg {Number} width Specifies the width of a table
5084  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5085  * 
5086  * @cfg {boolean} striped Should the rows be alternative striped
5087  * @cfg {boolean} bordered Add borders to the table
5088  * @cfg {boolean} hover Add hover highlighting
5089  * @cfg {boolean} condensed Format condensed
5090  * @cfg {boolean} responsive Format condensed
5091  * @cfg {Boolean} loadMask (true|false) default false
5092  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5093  * @cfg {Boolean} thead (true|false) generate thead, default true
5094  * @cfg {Boolean} RowSelection (true|false) default false
5095  * @cfg {Boolean} CellSelection (true|false) default false
5096  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5097  
5098  * 
5099  * @constructor
5100  * Create a new Table
5101  * @param {Object} config The config object
5102  */
5103
5104 Roo.bootstrap.Table = function(config){
5105     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5106     
5107     if (this.sm) {
5108         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5109         this.sm = this.selModel;
5110         this.sm.xmodule = this.xmodule || false;
5111     }
5112     if (this.cm && typeof(this.cm.config) == 'undefined') {
5113         this.colModel = new Roo.grid.ColumnModel(this.cm);
5114         this.cm = this.colModel;
5115         this.cm.xmodule = this.xmodule || false;
5116     }
5117     if (this.store) {
5118         this.store= Roo.factory(this.store, Roo.data);
5119         this.ds = this.store;
5120         this.ds.xmodule = this.xmodule || false;
5121          
5122     }
5123     if (this.footer && this.store) {
5124         this.footer.dataSource = this.ds;
5125         this.footer = Roo.factory(this.footer);
5126     }
5127     
5128     /** @private */
5129     this.addEvents({
5130         /**
5131          * @event cellclick
5132          * Fires when a cell is clicked
5133          * @param {Roo.bootstrap.Table} this
5134          * @param {Roo.Element} el
5135          * @param {Number} rowIndex
5136          * @param {Number} columnIndex
5137          * @param {Roo.EventObject} e
5138          */
5139         "cellclick" : true,
5140         /**
5141          * @event celldblclick
5142          * Fires when a cell is double clicked
5143          * @param {Roo.bootstrap.Table} this
5144          * @param {Roo.Element} el
5145          * @param {Number} rowIndex
5146          * @param {Number} columnIndex
5147          * @param {Roo.EventObject} e
5148          */
5149         "celldblclick" : true,
5150         /**
5151          * @event rowclick
5152          * Fires when a row is clicked
5153          * @param {Roo.bootstrap.Table} this
5154          * @param {Roo.Element} el
5155          * @param {Number} rowIndex
5156          * @param {Roo.EventObject} e
5157          */
5158         "rowclick" : true,
5159         /**
5160          * @event rowdblclick
5161          * Fires when a row is double clicked
5162          * @param {Roo.bootstrap.Table} this
5163          * @param {Roo.Element} el
5164          * @param {Number} rowIndex
5165          * @param {Roo.EventObject} e
5166          */
5167         "rowdblclick" : true,
5168         /**
5169          * @event mouseover
5170          * Fires when a mouseover occur
5171          * @param {Roo.bootstrap.Table} this
5172          * @param {Roo.Element} el
5173          * @param {Number} rowIndex
5174          * @param {Number} columnIndex
5175          * @param {Roo.EventObject} e
5176          */
5177         "mouseover" : true,
5178         /**
5179          * @event mouseout
5180          * Fires when a mouseout occur
5181          * @param {Roo.bootstrap.Table} this
5182          * @param {Roo.Element} el
5183          * @param {Number} rowIndex
5184          * @param {Number} columnIndex
5185          * @param {Roo.EventObject} e
5186          */
5187         "mouseout" : true,
5188         /**
5189          * @event rowclass
5190          * Fires when a row is rendered, so you can change add a style to it.
5191          * @param {Roo.bootstrap.Table} this
5192          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5193          */
5194         'rowclass' : true,
5195           /**
5196          * @event rowsrendered
5197          * Fires when all the  rows have been rendered
5198          * @param {Roo.bootstrap.Table} this
5199          */
5200         'rowsrendered' : true
5201         
5202     });
5203 };
5204
5205 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5206     
5207     cls: false,
5208     align: false,
5209     bgcolor: false,
5210     border: false,
5211     cellpadding: false,
5212     cellspacing: false,
5213     frame: false,
5214     rules: false,
5215     sortable: false,
5216     summary: false,
5217     width: false,
5218     striped : false,
5219     bordered: false,
5220     hover:  false,
5221     condensed : false,
5222     responsive : false,
5223     sm : false,
5224     cm : false,
5225     store : false,
5226     loadMask : false,
5227     tfoot : true,
5228     thead : true,
5229     RowSelection : false,
5230     CellSelection : false,
5231     layout : false,
5232     
5233     // Roo.Element - the tbody
5234     mainBody: false, 
5235     
5236     getAutoCreate : function(){
5237         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5238         
5239         cfg = {
5240             tag: 'table',
5241             cls : 'table',
5242             cn : []
5243         }
5244             
5245         if (this.striped) {
5246             cfg.cls += ' table-striped';
5247         }
5248         
5249         if (this.hover) {
5250             cfg.cls += ' table-hover';
5251         }
5252         if (this.bordered) {
5253             cfg.cls += ' table-bordered';
5254         }
5255         if (this.condensed) {
5256             cfg.cls += ' table-condensed';
5257         }
5258         if (this.responsive) {
5259             cfg.cls += ' table-responsive';
5260         }
5261         
5262         if (this.cls) {
5263             cfg.cls+=  ' ' +this.cls;
5264         }
5265         
5266         // this lot should be simplifed...
5267         
5268         if (this.align) {
5269             cfg.align=this.align;
5270         }
5271         if (this.bgcolor) {
5272             cfg.bgcolor=this.bgcolor;
5273         }
5274         if (this.border) {
5275             cfg.border=this.border;
5276         }
5277         if (this.cellpadding) {
5278             cfg.cellpadding=this.cellpadding;
5279         }
5280         if (this.cellspacing) {
5281             cfg.cellspacing=this.cellspacing;
5282         }
5283         if (this.frame) {
5284             cfg.frame=this.frame;
5285         }
5286         if (this.rules) {
5287             cfg.rules=this.rules;
5288         }
5289         if (this.sortable) {
5290             cfg.sortable=this.sortable;
5291         }
5292         if (this.summary) {
5293             cfg.summary=this.summary;
5294         }
5295         if (this.width) {
5296             cfg.width=this.width;
5297         }
5298         if (this.layout) {
5299             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5300         }
5301         
5302         if(this.store || this.cm){
5303             if(this.thead){
5304                 cfg.cn.push(this.renderHeader());
5305             }
5306             
5307             cfg.cn.push(this.renderBody());
5308             
5309             if(this.tfoot){
5310                 cfg.cn.push(this.renderFooter());
5311             }
5312             
5313             cfg.cls+=  ' TableGrid';
5314         }
5315         
5316         return { cn : [ cfg ] };
5317     },
5318     
5319     initEvents : function()
5320     {   
5321         if(!this.store || !this.cm){
5322             return;
5323         }
5324         
5325         //Roo.log('initEvents with ds!!!!');
5326         
5327         this.mainBody = this.el.select('tbody', true).first();
5328         
5329         
5330         var _this = this;
5331         
5332         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5333             e.on('click', _this.sort, _this);
5334         });
5335         
5336         this.el.on("click", this.onClick, this);
5337         this.el.on("dblclick", this.onDblClick, this);
5338         
5339         // why is this done????? = it breaks dialogs??
5340         //this.parent().el.setStyle('position', 'relative');
5341         
5342         
5343         if (this.footer) {
5344             this.footer.parentId = this.id;
5345             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5346         }
5347         
5348         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5349         
5350         this.store.on('load', this.onLoad, this);
5351         this.store.on('beforeload', this.onBeforeLoad, this);
5352         this.store.on('update', this.onUpdate, this);
5353         this.store.on('add', this.onAdd, this);
5354         
5355     },
5356     
5357     onMouseover : function(e, el)
5358     {
5359         var cell = Roo.get(el);
5360         
5361         if(!cell){
5362             return;
5363         }
5364         
5365         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5366             cell = cell.findParent('td', false, true);
5367         }
5368         
5369         var row = cell.findParent('tr', false, true);
5370         var cellIndex = cell.dom.cellIndex;
5371         var rowIndex = row.dom.rowIndex - 1; // start from 0
5372         
5373         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5374         
5375     },
5376     
5377     onMouseout : function(e, el)
5378     {
5379         var cell = Roo.get(el);
5380         
5381         if(!cell){
5382             return;
5383         }
5384         
5385         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5386             cell = cell.findParent('td', false, true);
5387         }
5388         
5389         var row = cell.findParent('tr', false, true);
5390         var cellIndex = cell.dom.cellIndex;
5391         var rowIndex = row.dom.rowIndex - 1; // start from 0
5392         
5393         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5394         
5395     },
5396     
5397     onClick : function(e, el)
5398     {
5399         var cell = Roo.get(el);
5400         
5401         if(!cell || (!this.CellSelection && !this.RowSelection)){
5402             return;
5403         }
5404         
5405         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5406             cell = cell.findParent('td', false, true);
5407         }
5408         
5409         if(!cell || typeof(cell) == 'undefined'){
5410             return;
5411         }
5412         
5413         var row = cell.findParent('tr', false, true);
5414         
5415         if(!row || typeof(row) == 'undefined'){
5416             return;
5417         }
5418         
5419         var cellIndex = cell.dom.cellIndex;
5420         var rowIndex = this.getRowIndex(row);
5421         
5422         if(this.CellSelection){
5423             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5424         }
5425         
5426         if(this.RowSelection){
5427             this.fireEvent('rowclick', this, row, rowIndex, e);
5428         }
5429         
5430         
5431     },
5432     
5433     onDblClick : function(e,el)
5434     {
5435         var cell = Roo.get(el);
5436         
5437         if(!cell || (!this.CellSelection && !this.RowSelection)){
5438             return;
5439         }
5440         
5441         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5442             cell = cell.findParent('td', false, true);
5443         }
5444         
5445         if(!cell || typeof(cell) == 'undefined'){
5446             return;
5447         }
5448         
5449         var row = cell.findParent('tr', false, true);
5450         
5451         if(!row || typeof(row) == 'undefined'){
5452             return;
5453         }
5454         
5455         var cellIndex = cell.dom.cellIndex;
5456         var rowIndex = this.getRowIndex(row);
5457         
5458         if(this.CellSelection){
5459             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5460         }
5461         
5462         if(this.RowSelection){
5463             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5464         }
5465     },
5466     
5467     sort : function(e,el)
5468     {
5469         var col = Roo.get(el);
5470         
5471         if(!col.hasClass('sortable')){
5472             return;
5473         }
5474         
5475         var sort = col.attr('sort');
5476         var dir = 'ASC';
5477         
5478         if(col.hasClass('glyphicon-arrow-up')){
5479             dir = 'DESC';
5480         }
5481         
5482         this.store.sortInfo = {field : sort, direction : dir};
5483         
5484         if (this.footer) {
5485             Roo.log("calling footer first");
5486             this.footer.onClick('first');
5487         } else {
5488         
5489             this.store.load({ params : { start : 0 } });
5490         }
5491     },
5492     
5493     renderHeader : function()
5494     {
5495         var header = {
5496             tag: 'thead',
5497             cn : []
5498         };
5499         
5500         var cm = this.cm;
5501         
5502         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5503             
5504             var config = cm.config[i];
5505                     
5506             var c = {
5507                 tag: 'th',
5508                 style : '',
5509                 html: cm.getColumnHeader(i)
5510             };
5511             
5512             if(typeof(config.tooltip) != 'undefined'){
5513                 c.tooltip = config.tooltip;
5514             }
5515             
5516             if(typeof(config.colspan) != 'undefined'){
5517                 c.colspan = config.colspan;
5518             }
5519             
5520             if(typeof(config.hidden) != 'undefined' && config.hidden){
5521                 c.style += ' display:none;';
5522             }
5523             
5524             if(typeof(config.dataIndex) != 'undefined'){
5525                 c.sort = config.dataIndex;
5526             }
5527             
5528             if(typeof(config.sortable) != 'undefined' && config.sortable){
5529                 c.cls = 'sortable';
5530             }
5531             
5532             if(typeof(config.align) != 'undefined' && config.align.length){
5533                 c.style += ' text-align:' + config.align + ';';
5534             }
5535             
5536             if(typeof(config.width) != 'undefined'){
5537                 c.style += ' width:' + config.width + 'px;';
5538             }
5539             
5540             header.cn.push(c)
5541         }
5542         
5543         return header;
5544     },
5545     
5546     renderBody : function()
5547     {
5548         var body = {
5549             tag: 'tbody',
5550             cn : [
5551                 {
5552                     tag: 'tr',
5553                     cn : [
5554                         {
5555                             tag : 'td',
5556                             colspan :  this.cm.getColumnCount()
5557                         }
5558                     ]
5559                 }
5560             ]
5561         };
5562         
5563         return body;
5564     },
5565     
5566     renderFooter : function()
5567     {
5568         var footer = {
5569             tag: 'tfoot',
5570             cn : [
5571                 {
5572                     tag: 'tr',
5573                     cn : [
5574                         {
5575                             tag : 'td',
5576                             colspan :  this.cm.getColumnCount()
5577                         }
5578                     ]
5579                 }
5580             ]
5581         };
5582         
5583         return footer;
5584     },
5585     
5586     
5587     
5588     onLoad : function()
5589     {
5590         Roo.log('ds onload');
5591         this.clear();
5592         
5593         var _this = this;
5594         var cm = this.cm;
5595         var ds = this.store;
5596         
5597         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5598             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5599             
5600             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5601                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5602             }
5603             
5604             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5605                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5606             }
5607         });
5608         
5609         var tbody =  this.mainBody;
5610               
5611         if(ds.getCount() > 0){
5612             ds.data.each(function(d,rowIndex){
5613                 var row =  this.renderRow(cm, ds, rowIndex);
5614                 
5615                 tbody.createChild(row);
5616                 
5617                 var _this = this;
5618                 
5619                 if(row.cellObjects.length){
5620                     Roo.each(row.cellObjects, function(r){
5621                         _this.renderCellObject(r);
5622                     })
5623                 }
5624                 
5625             }, this);
5626         }
5627         
5628         Roo.each(this.el.select('tbody td', true).elements, function(e){
5629             e.on('mouseover', _this.onMouseover, _this);
5630         });
5631         
5632         Roo.each(this.el.select('tbody td', true).elements, function(e){
5633             e.on('mouseout', _this.onMouseout, _this);
5634         });
5635         this.fireEvent('rowsrendered', this);
5636         //if(this.loadMask){
5637         //    this.maskEl.hide();
5638         //}
5639     },
5640     
5641     
5642     onUpdate : function(ds,record)
5643     {
5644         this.refreshRow(record);
5645     },
5646     
5647     onRemove : function(ds, record, index, isUpdate){
5648         if(isUpdate !== true){
5649             this.fireEvent("beforerowremoved", this, index, record);
5650         }
5651         var bt = this.mainBody.dom;
5652         
5653         var rows = this.el.select('tbody > tr', true).elements;
5654         
5655         if(typeof(rows[index]) != 'undefined'){
5656             bt.removeChild(rows[index].dom);
5657         }
5658         
5659 //        if(bt.rows[index]){
5660 //            bt.removeChild(bt.rows[index]);
5661 //        }
5662         
5663         if(isUpdate !== true){
5664             //this.stripeRows(index);
5665             //this.syncRowHeights(index, index);
5666             //this.layout();
5667             this.fireEvent("rowremoved", this, index, record);
5668         }
5669     },
5670     
5671     onAdd : function(ds, records, rowIndex)
5672     {
5673         //Roo.log('on Add called');
5674         // - note this does not handle multiple adding very well..
5675         var bt = this.mainBody.dom;
5676         for (var i =0 ; i < records.length;i++) {
5677             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5678             //Roo.log(records[i]);
5679             //Roo.log(this.store.getAt(rowIndex+i));
5680             this.insertRow(this.store, rowIndex + i, false);
5681             return;
5682         }
5683         
5684     },
5685     
5686     
5687     refreshRow : function(record){
5688         var ds = this.store, index;
5689         if(typeof record == 'number'){
5690             index = record;
5691             record = ds.getAt(index);
5692         }else{
5693             index = ds.indexOf(record);
5694         }
5695         this.insertRow(ds, index, true);
5696         this.onRemove(ds, record, index+1, true);
5697         //this.syncRowHeights(index, index);
5698         //this.layout();
5699         this.fireEvent("rowupdated", this, index, record);
5700     },
5701     
5702     insertRow : function(dm, rowIndex, isUpdate){
5703         
5704         if(!isUpdate){
5705             this.fireEvent("beforerowsinserted", this, rowIndex);
5706         }
5707             //var s = this.getScrollState();
5708         var row = this.renderRow(this.cm, this.store, rowIndex);
5709         // insert before rowIndex..
5710         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5711         
5712         var _this = this;
5713                 
5714         if(row.cellObjects.length){
5715             Roo.each(row.cellObjects, function(r){
5716                 _this.renderCellObject(r);
5717             })
5718         }
5719             
5720         if(!isUpdate){
5721             this.fireEvent("rowsinserted", this, rowIndex);
5722             //this.syncRowHeights(firstRow, lastRow);
5723             //this.stripeRows(firstRow);
5724             //this.layout();
5725         }
5726         
5727     },
5728     
5729     
5730     getRowDom : function(rowIndex)
5731     {
5732         var rows = this.el.select('tbody > tr', true).elements;
5733         
5734         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
5735         
5736     },
5737     // returns the object tree for a tr..
5738   
5739     
5740     renderRow : function(cm, ds, rowIndex) 
5741     {
5742         
5743         var d = ds.getAt(rowIndex);
5744         
5745         var row = {
5746             tag : 'tr',
5747             cn : []
5748         };
5749             
5750         var cellObjects = [];
5751         
5752         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5753             var config = cm.config[i];
5754             
5755             var renderer = cm.getRenderer(i);
5756             var value = '';
5757             var id = false;
5758             
5759             if(typeof(renderer) !== 'undefined'){
5760                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5761             }
5762             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5763             // and are rendered into the cells after the row is rendered - using the id for the element.
5764             
5765             if(typeof(value) === 'object'){
5766                 id = Roo.id();
5767                 cellObjects.push({
5768                     container : id,
5769                     cfg : value 
5770                 })
5771             }
5772             
5773             var rowcfg = {
5774                 record: d,
5775                 rowIndex : rowIndex,
5776                 colIndex : i,
5777                 rowClass : ''
5778             }
5779
5780             this.fireEvent('rowclass', this, rowcfg);
5781             
5782             var td = {
5783                 tag: 'td',
5784                 cls : rowcfg.rowClass,
5785                 style: '',
5786                 html: (typeof(value) === 'object') ? '' : value
5787             };
5788             
5789             if (id) {
5790                 td.id = id;
5791             }
5792             
5793             if(typeof(config.colspan) != 'undefined'){
5794                 td.colspan = config.colspan;
5795             }
5796             
5797             if(typeof(config.hidden) != 'undefined' && config.hidden){
5798                 td.style += ' display:none;';
5799             }
5800             
5801             if(typeof(config.align) != 'undefined' && config.align.length){
5802                 td.style += ' text-align:' + config.align + ';';
5803             }
5804             
5805             if(typeof(config.width) != 'undefined'){
5806                 td.style += ' width:' +  config.width + 'px;';
5807             }
5808             
5809             if(typeof(config.cursor) != 'undefined'){
5810                 td.style += ' cursor:' +  config.cursor + ';';
5811             }
5812              
5813             row.cn.push(td);
5814            
5815         }
5816         
5817         row.cellObjects = cellObjects;
5818         
5819         return row;
5820           
5821     },
5822     
5823     
5824     
5825     onBeforeLoad : function()
5826     {
5827         //Roo.log('ds onBeforeLoad');
5828         
5829         //this.clear();
5830         
5831         //if(this.loadMask){
5832         //    this.maskEl.show();
5833         //}
5834     },
5835      /**
5836      * Remove all rows
5837      */
5838     clear : function()
5839     {
5840         this.el.select('tbody', true).first().dom.innerHTML = '';
5841     },
5842     /**
5843      * Show or hide a row.
5844      * @param {Number} rowIndex to show or hide
5845      * @param {Boolean} state hide
5846      */
5847     setRowVisibility : function(rowIndex, state)
5848     {
5849         var bt = this.mainBody.dom;
5850         
5851         var rows = this.el.select('tbody > tr', true).elements;
5852         
5853         if(typeof(rows[rowIndex]) == 'undefined'){
5854             return;
5855         }
5856         rows[rowIndex].dom.style.display = state ? '' : 'none';
5857     },
5858     
5859     
5860     getSelectionModel : function(){
5861         if(!this.selModel){
5862             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5863         }
5864         return this.selModel;
5865     },
5866     /*
5867      * Render the Roo.bootstrap object from renderder
5868      */
5869     renderCellObject : function(r)
5870     {
5871         var _this = this;
5872         
5873         var t = r.cfg.render(r.container);
5874         
5875         if(r.cfg.cn){
5876             Roo.each(r.cfg.cn, function(c){
5877                 var child = {
5878                     container: t.getChildContainer(),
5879                     cfg: c
5880                 }
5881                 _this.renderCellObject(child);
5882             })
5883         }
5884     },
5885     
5886     getRowIndex : function(row)
5887     {
5888         var rowIndex = -1;
5889         
5890         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
5891             if(el != row){
5892                 return;
5893             }
5894             
5895             rowIndex = index;
5896         });
5897         
5898         return rowIndex;
5899     }
5900    
5901 });
5902
5903  
5904
5905  /*
5906  * - LGPL
5907  *
5908  * table cell
5909  * 
5910  */
5911
5912 /**
5913  * @class Roo.bootstrap.TableCell
5914  * @extends Roo.bootstrap.Component
5915  * Bootstrap TableCell class
5916  * @cfg {String} html cell contain text
5917  * @cfg {String} cls cell class
5918  * @cfg {String} tag cell tag (td|th) default td
5919  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5920  * @cfg {String} align Aligns the content in a cell
5921  * @cfg {String} axis Categorizes cells
5922  * @cfg {String} bgcolor Specifies the background color of a cell
5923  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5924  * @cfg {Number} colspan Specifies the number of columns a cell should span
5925  * @cfg {String} headers Specifies one or more header cells a cell is related to
5926  * @cfg {Number} height Sets the height of a cell
5927  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5928  * @cfg {Number} rowspan Sets the number of rows a cell should span
5929  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5930  * @cfg {String} valign Vertical aligns the content in a cell
5931  * @cfg {Number} width Specifies the width of a cell
5932  * 
5933  * @constructor
5934  * Create a new TableCell
5935  * @param {Object} config The config object
5936  */
5937
5938 Roo.bootstrap.TableCell = function(config){
5939     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5940 };
5941
5942 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5943     
5944     html: false,
5945     cls: false,
5946     tag: false,
5947     abbr: false,
5948     align: false,
5949     axis: false,
5950     bgcolor: false,
5951     charoff: false,
5952     colspan: false,
5953     headers: false,
5954     height: false,
5955     nowrap: false,
5956     rowspan: false,
5957     scope: false,
5958     valign: false,
5959     width: false,
5960     
5961     
5962     getAutoCreate : function(){
5963         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5964         
5965         cfg = {
5966             tag: 'td'
5967         }
5968         
5969         if(this.tag){
5970             cfg.tag = this.tag;
5971         }
5972         
5973         if (this.html) {
5974             cfg.html=this.html
5975         }
5976         if (this.cls) {
5977             cfg.cls=this.cls
5978         }
5979         if (this.abbr) {
5980             cfg.abbr=this.abbr
5981         }
5982         if (this.align) {
5983             cfg.align=this.align
5984         }
5985         if (this.axis) {
5986             cfg.axis=this.axis
5987         }
5988         if (this.bgcolor) {
5989             cfg.bgcolor=this.bgcolor
5990         }
5991         if (this.charoff) {
5992             cfg.charoff=this.charoff
5993         }
5994         if (this.colspan) {
5995             cfg.colspan=this.colspan
5996         }
5997         if (this.headers) {
5998             cfg.headers=this.headers
5999         }
6000         if (this.height) {
6001             cfg.height=this.height
6002         }
6003         if (this.nowrap) {
6004             cfg.nowrap=this.nowrap
6005         }
6006         if (this.rowspan) {
6007             cfg.rowspan=this.rowspan
6008         }
6009         if (this.scope) {
6010             cfg.scope=this.scope
6011         }
6012         if (this.valign) {
6013             cfg.valign=this.valign
6014         }
6015         if (this.width) {
6016             cfg.width=this.width
6017         }
6018         
6019         
6020         return cfg;
6021     }
6022    
6023 });
6024
6025  
6026
6027  /*
6028  * - LGPL
6029  *
6030  * table row
6031  * 
6032  */
6033
6034 /**
6035  * @class Roo.bootstrap.TableRow
6036  * @extends Roo.bootstrap.Component
6037  * Bootstrap TableRow class
6038  * @cfg {String} cls row class
6039  * @cfg {String} align Aligns the content in a table row
6040  * @cfg {String} bgcolor Specifies a background color for a table row
6041  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6042  * @cfg {String} valign Vertical aligns the content in a table row
6043  * 
6044  * @constructor
6045  * Create a new TableRow
6046  * @param {Object} config The config object
6047  */
6048
6049 Roo.bootstrap.TableRow = function(config){
6050     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6051 };
6052
6053 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6054     
6055     cls: false,
6056     align: false,
6057     bgcolor: false,
6058     charoff: false,
6059     valign: false,
6060     
6061     getAutoCreate : function(){
6062         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6063         
6064         cfg = {
6065             tag: 'tr'
6066         }
6067             
6068         if(this.cls){
6069             cfg.cls = this.cls;
6070         }
6071         if(this.align){
6072             cfg.align = this.align;
6073         }
6074         if(this.bgcolor){
6075             cfg.bgcolor = this.bgcolor;
6076         }
6077         if(this.charoff){
6078             cfg.charoff = this.charoff;
6079         }
6080         if(this.valign){
6081             cfg.valign = this.valign;
6082         }
6083         
6084         return cfg;
6085     }
6086    
6087 });
6088
6089  
6090
6091  /*
6092  * - LGPL
6093  *
6094  * table body
6095  * 
6096  */
6097
6098 /**
6099  * @class Roo.bootstrap.TableBody
6100  * @extends Roo.bootstrap.Component
6101  * Bootstrap TableBody class
6102  * @cfg {String} cls element class
6103  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6104  * @cfg {String} align Aligns the content inside the element
6105  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6106  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6107  * 
6108  * @constructor
6109  * Create a new TableBody
6110  * @param {Object} config The config object
6111  */
6112
6113 Roo.bootstrap.TableBody = function(config){
6114     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6115 };
6116
6117 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6118     
6119     cls: false,
6120     tag: false,
6121     align: false,
6122     charoff: false,
6123     valign: false,
6124     
6125     getAutoCreate : function(){
6126         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6127         
6128         cfg = {
6129             tag: 'tbody'
6130         }
6131             
6132         if (this.cls) {
6133             cfg.cls=this.cls
6134         }
6135         if(this.tag){
6136             cfg.tag = this.tag;
6137         }
6138         
6139         if(this.align){
6140             cfg.align = this.align;
6141         }
6142         if(this.charoff){
6143             cfg.charoff = this.charoff;
6144         }
6145         if(this.valign){
6146             cfg.valign = this.valign;
6147         }
6148         
6149         return cfg;
6150     }
6151     
6152     
6153 //    initEvents : function()
6154 //    {
6155 //        
6156 //        if(!this.store){
6157 //            return;
6158 //        }
6159 //        
6160 //        this.store = Roo.factory(this.store, Roo.data);
6161 //        this.store.on('load', this.onLoad, this);
6162 //        
6163 //        this.store.load();
6164 //        
6165 //    },
6166 //    
6167 //    onLoad: function () 
6168 //    {   
6169 //        this.fireEvent('load', this);
6170 //    }
6171 //    
6172 //   
6173 });
6174
6175  
6176
6177  /*
6178  * Based on:
6179  * Ext JS Library 1.1.1
6180  * Copyright(c) 2006-2007, Ext JS, LLC.
6181  *
6182  * Originally Released Under LGPL - original licence link has changed is not relivant.
6183  *
6184  * Fork - LGPL
6185  * <script type="text/javascript">
6186  */
6187
6188 // as we use this in bootstrap.
6189 Roo.namespace('Roo.form');
6190  /**
6191  * @class Roo.form.Action
6192  * Internal Class used to handle form actions
6193  * @constructor
6194  * @param {Roo.form.BasicForm} el The form element or its id
6195  * @param {Object} config Configuration options
6196  */
6197
6198  
6199  
6200 // define the action interface
6201 Roo.form.Action = function(form, options){
6202     this.form = form;
6203     this.options = options || {};
6204 };
6205 /**
6206  * Client Validation Failed
6207  * @const 
6208  */
6209 Roo.form.Action.CLIENT_INVALID = 'client';
6210 /**
6211  * Server Validation Failed
6212  * @const 
6213  */
6214 Roo.form.Action.SERVER_INVALID = 'server';
6215  /**
6216  * Connect to Server Failed
6217  * @const 
6218  */
6219 Roo.form.Action.CONNECT_FAILURE = 'connect';
6220 /**
6221  * Reading Data from Server Failed
6222  * @const 
6223  */
6224 Roo.form.Action.LOAD_FAILURE = 'load';
6225
6226 Roo.form.Action.prototype = {
6227     type : 'default',
6228     failureType : undefined,
6229     response : undefined,
6230     result : undefined,
6231
6232     // interface method
6233     run : function(options){
6234
6235     },
6236
6237     // interface method
6238     success : function(response){
6239
6240     },
6241
6242     // interface method
6243     handleResponse : function(response){
6244
6245     },
6246
6247     // default connection failure
6248     failure : function(response){
6249         
6250         this.response = response;
6251         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6252         this.form.afterAction(this, false);
6253     },
6254
6255     processResponse : function(response){
6256         this.response = response;
6257         if(!response.responseText){
6258             return true;
6259         }
6260         this.result = this.handleResponse(response);
6261         return this.result;
6262     },
6263
6264     // utility functions used internally
6265     getUrl : function(appendParams){
6266         var url = this.options.url || this.form.url || this.form.el.dom.action;
6267         if(appendParams){
6268             var p = this.getParams();
6269             if(p){
6270                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6271             }
6272         }
6273         return url;
6274     },
6275
6276     getMethod : function(){
6277         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6278     },
6279
6280     getParams : function(){
6281         var bp = this.form.baseParams;
6282         var p = this.options.params;
6283         if(p){
6284             if(typeof p == "object"){
6285                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6286             }else if(typeof p == 'string' && bp){
6287                 p += '&' + Roo.urlEncode(bp);
6288             }
6289         }else if(bp){
6290             p = Roo.urlEncode(bp);
6291         }
6292         return p;
6293     },
6294
6295     createCallback : function(){
6296         return {
6297             success: this.success,
6298             failure: this.failure,
6299             scope: this,
6300             timeout: (this.form.timeout*1000),
6301             upload: this.form.fileUpload ? this.success : undefined
6302         };
6303     }
6304 };
6305
6306 Roo.form.Action.Submit = function(form, options){
6307     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6308 };
6309
6310 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6311     type : 'submit',
6312
6313     haveProgress : false,
6314     uploadComplete : false,
6315     
6316     // uploadProgress indicator.
6317     uploadProgress : function()
6318     {
6319         if (!this.form.progressUrl) {
6320             return;
6321         }
6322         
6323         if (!this.haveProgress) {
6324             Roo.MessageBox.progress("Uploading", "Uploading");
6325         }
6326         if (this.uploadComplete) {
6327            Roo.MessageBox.hide();
6328            return;
6329         }
6330         
6331         this.haveProgress = true;
6332    
6333         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6334         
6335         var c = new Roo.data.Connection();
6336         c.request({
6337             url : this.form.progressUrl,
6338             params: {
6339                 id : uid
6340             },
6341             method: 'GET',
6342             success : function(req){
6343                //console.log(data);
6344                 var rdata = false;
6345                 var edata;
6346                 try  {
6347                    rdata = Roo.decode(req.responseText)
6348                 } catch (e) {
6349                     Roo.log("Invalid data from server..");
6350                     Roo.log(edata);
6351                     return;
6352                 }
6353                 if (!rdata || !rdata.success) {
6354                     Roo.log(rdata);
6355                     Roo.MessageBox.alert(Roo.encode(rdata));
6356                     return;
6357                 }
6358                 var data = rdata.data;
6359                 
6360                 if (this.uploadComplete) {
6361                    Roo.MessageBox.hide();
6362                    return;
6363                 }
6364                    
6365                 if (data){
6366                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6367                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6368                     );
6369                 }
6370                 this.uploadProgress.defer(2000,this);
6371             },
6372        
6373             failure: function(data) {
6374                 Roo.log('progress url failed ');
6375                 Roo.log(data);
6376             },
6377             scope : this
6378         });
6379            
6380     },
6381     
6382     
6383     run : function()
6384     {
6385         // run get Values on the form, so it syncs any secondary forms.
6386         this.form.getValues();
6387         
6388         var o = this.options;
6389         var method = this.getMethod();
6390         var isPost = method == 'POST';
6391         if(o.clientValidation === false || this.form.isValid()){
6392             
6393             if (this.form.progressUrl) {
6394                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6395                     (new Date() * 1) + '' + Math.random());
6396                     
6397             } 
6398             
6399             
6400             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6401                 form:this.form.el.dom,
6402                 url:this.getUrl(!isPost),
6403                 method: method,
6404                 params:isPost ? this.getParams() : null,
6405                 isUpload: this.form.fileUpload
6406             }));
6407             
6408             this.uploadProgress();
6409
6410         }else if (o.clientValidation !== false){ // client validation failed
6411             this.failureType = Roo.form.Action.CLIENT_INVALID;
6412             this.form.afterAction(this, false);
6413         }
6414     },
6415
6416     success : function(response)
6417     {
6418         this.uploadComplete= true;
6419         if (this.haveProgress) {
6420             Roo.MessageBox.hide();
6421         }
6422         
6423         
6424         var result = this.processResponse(response);
6425         if(result === true || result.success){
6426             this.form.afterAction(this, true);
6427             return;
6428         }
6429         if(result.errors){
6430             this.form.markInvalid(result.errors);
6431             this.failureType = Roo.form.Action.SERVER_INVALID;
6432         }
6433         this.form.afterAction(this, false);
6434     },
6435     failure : function(response)
6436     {
6437         this.uploadComplete= true;
6438         if (this.haveProgress) {
6439             Roo.MessageBox.hide();
6440         }
6441         
6442         this.response = response;
6443         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6444         this.form.afterAction(this, false);
6445     },
6446     
6447     handleResponse : function(response){
6448         if(this.form.errorReader){
6449             var rs = this.form.errorReader.read(response);
6450             var errors = [];
6451             if(rs.records){
6452                 for(var i = 0, len = rs.records.length; i < len; i++) {
6453                     var r = rs.records[i];
6454                     errors[i] = r.data;
6455                 }
6456             }
6457             if(errors.length < 1){
6458                 errors = null;
6459             }
6460             return {
6461                 success : rs.success,
6462                 errors : errors
6463             };
6464         }
6465         var ret = false;
6466         try {
6467             ret = Roo.decode(response.responseText);
6468         } catch (e) {
6469             ret = {
6470                 success: false,
6471                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6472                 errors : []
6473             };
6474         }
6475         return ret;
6476         
6477     }
6478 });
6479
6480
6481 Roo.form.Action.Load = function(form, options){
6482     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6483     this.reader = this.form.reader;
6484 };
6485
6486 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6487     type : 'load',
6488
6489     run : function(){
6490         
6491         Roo.Ajax.request(Roo.apply(
6492                 this.createCallback(), {
6493                     method:this.getMethod(),
6494                     url:this.getUrl(false),
6495                     params:this.getParams()
6496         }));
6497     },
6498
6499     success : function(response){
6500         
6501         var result = this.processResponse(response);
6502         if(result === true || !result.success || !result.data){
6503             this.failureType = Roo.form.Action.LOAD_FAILURE;
6504             this.form.afterAction(this, false);
6505             return;
6506         }
6507         this.form.clearInvalid();
6508         this.form.setValues(result.data);
6509         this.form.afterAction(this, true);
6510     },
6511
6512     handleResponse : function(response){
6513         if(this.form.reader){
6514             var rs = this.form.reader.read(response);
6515             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6516             return {
6517                 success : rs.success,
6518                 data : data
6519             };
6520         }
6521         return Roo.decode(response.responseText);
6522     }
6523 });
6524
6525 Roo.form.Action.ACTION_TYPES = {
6526     'load' : Roo.form.Action.Load,
6527     'submit' : Roo.form.Action.Submit
6528 };/*
6529  * - LGPL
6530  *
6531  * form
6532  * 
6533  */
6534
6535 /**
6536  * @class Roo.bootstrap.Form
6537  * @extends Roo.bootstrap.Component
6538  * Bootstrap Form class
6539  * @cfg {String} method  GET | POST (default POST)
6540  * @cfg {String} labelAlign top | left (default top)
6541  * @cfg {String} align left  | right - for navbars
6542  * @cfg {Boolean} loadMask load mask when submit (default true)
6543
6544  * 
6545  * @constructor
6546  * Create a new Form
6547  * @param {Object} config The config object
6548  */
6549
6550
6551 Roo.bootstrap.Form = function(config){
6552     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6553     this.addEvents({
6554         /**
6555          * @event clientvalidation
6556          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6557          * @param {Form} this
6558          * @param {Boolean} valid true if the form has passed client-side validation
6559          */
6560         clientvalidation: true,
6561         /**
6562          * @event beforeaction
6563          * Fires before any action is performed. Return false to cancel the action.
6564          * @param {Form} this
6565          * @param {Action} action The action to be performed
6566          */
6567         beforeaction: true,
6568         /**
6569          * @event actionfailed
6570          * Fires when an action fails.
6571          * @param {Form} this
6572          * @param {Action} action The action that failed
6573          */
6574         actionfailed : true,
6575         /**
6576          * @event actioncomplete
6577          * Fires when an action is completed.
6578          * @param {Form} this
6579          * @param {Action} action The action that completed
6580          */
6581         actioncomplete : true
6582     });
6583     
6584 };
6585
6586 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6587       
6588      /**
6589      * @cfg {String} method
6590      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6591      */
6592     method : 'POST',
6593     /**
6594      * @cfg {String} url
6595      * The URL to use for form actions if one isn't supplied in the action options.
6596      */
6597     /**
6598      * @cfg {Boolean} fileUpload
6599      * Set to true if this form is a file upload.
6600      */
6601      
6602     /**
6603      * @cfg {Object} baseParams
6604      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6605      */
6606       
6607     /**
6608      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6609      */
6610     timeout: 30,
6611     /**
6612      * @cfg {Sting} align (left|right) for navbar forms
6613      */
6614     align : 'left',
6615
6616     // private
6617     activeAction : null,
6618  
6619     /**
6620      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6621      * element by passing it or its id or mask the form itself by passing in true.
6622      * @type Mixed
6623      */
6624     waitMsgTarget : false,
6625     
6626     loadMask : true,
6627     
6628     getAutoCreate : function(){
6629         
6630         var cfg = {
6631             tag: 'form',
6632             method : this.method || 'POST',
6633             id : this.id || Roo.id(),
6634             cls : ''
6635         }
6636         if (this.parent().xtype.match(/^Nav/)) {
6637             cfg.cls = 'navbar-form navbar-' + this.align;
6638             
6639         }
6640         
6641         if (this.labelAlign == 'left' ) {
6642             cfg.cls += ' form-horizontal';
6643         }
6644         
6645         
6646         return cfg;
6647     },
6648     initEvents : function()
6649     {
6650         this.el.on('submit', this.onSubmit, this);
6651         // this was added as random key presses on the form where triggering form submit.
6652         this.el.on('keypress', function(e) {
6653             if (e.getCharCode() != 13) {
6654                 return true;
6655             }
6656             // we might need to allow it for textareas.. and some other items.
6657             // check e.getTarget().
6658             
6659             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6660                 return true;
6661             }
6662         
6663             Roo.log("keypress blocked");
6664             
6665             e.preventDefault();
6666             return false;
6667         });
6668         
6669     },
6670     // private
6671     onSubmit : function(e){
6672         e.stopEvent();
6673     },
6674     
6675      /**
6676      * Returns true if client-side validation on the form is successful.
6677      * @return Boolean
6678      */
6679     isValid : function(){
6680         var items = this.getItems();
6681         var valid = true;
6682         items.each(function(f){
6683            if(!f.validate()){
6684                valid = false;
6685                
6686            }
6687         });
6688         return valid;
6689     },
6690     /**
6691      * Returns true if any fields in this form have changed since their original load.
6692      * @return Boolean
6693      */
6694     isDirty : function(){
6695         var dirty = false;
6696         var items = this.getItems();
6697         items.each(function(f){
6698            if(f.isDirty()){
6699                dirty = true;
6700                return false;
6701            }
6702            return true;
6703         });
6704         return dirty;
6705     },
6706      /**
6707      * Performs a predefined action (submit or load) or custom actions you define on this form.
6708      * @param {String} actionName The name of the action type
6709      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6710      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6711      * accept other config options):
6712      * <pre>
6713 Property          Type             Description
6714 ----------------  ---------------  ----------------------------------------------------------------------------------
6715 url               String           The url for the action (defaults to the form's url)
6716 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6717 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6718 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6719                                    validate the form on the client (defaults to false)
6720      * </pre>
6721      * @return {BasicForm} this
6722      */
6723     doAction : function(action, options){
6724         if(typeof action == 'string'){
6725             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6726         }
6727         if(this.fireEvent('beforeaction', this, action) !== false){
6728             this.beforeAction(action);
6729             action.run.defer(100, action);
6730         }
6731         return this;
6732     },
6733     
6734     // private
6735     beforeAction : function(action){
6736         var o = action.options;
6737         
6738         if(this.loadMask){
6739             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6740         }
6741         // not really supported yet.. ??
6742         
6743         //if(this.waitMsgTarget === true){
6744         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6745         //}else if(this.waitMsgTarget){
6746         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6747         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6748         //}else {
6749         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6750        // }
6751          
6752     },
6753
6754     // private
6755     afterAction : function(action, success){
6756         this.activeAction = null;
6757         var o = action.options;
6758         
6759         //if(this.waitMsgTarget === true){
6760             this.el.unmask();
6761         //}else if(this.waitMsgTarget){
6762         //    this.waitMsgTarget.unmask();
6763         //}else{
6764         //    Roo.MessageBox.updateProgress(1);
6765         //    Roo.MessageBox.hide();
6766        // }
6767         // 
6768         if(success){
6769             if(o.reset){
6770                 this.reset();
6771             }
6772             Roo.callback(o.success, o.scope, [this, action]);
6773             this.fireEvent('actioncomplete', this, action);
6774             
6775         }else{
6776             
6777             // failure condition..
6778             // we have a scenario where updates need confirming.
6779             // eg. if a locking scenario exists..
6780             // we look for { errors : { needs_confirm : true }} in the response.
6781             if (
6782                 (typeof(action.result) != 'undefined')  &&
6783                 (typeof(action.result.errors) != 'undefined')  &&
6784                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6785            ){
6786                 var _t = this;
6787                 Roo.log("not supported yet");
6788                  /*
6789                 
6790                 Roo.MessageBox.confirm(
6791                     "Change requires confirmation",
6792                     action.result.errorMsg,
6793                     function(r) {
6794                         if (r != 'yes') {
6795                             return;
6796                         }
6797                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6798                     }
6799                     
6800                 );
6801                 */
6802                 
6803                 
6804                 return;
6805             }
6806             
6807             Roo.callback(o.failure, o.scope, [this, action]);
6808             // show an error message if no failed handler is set..
6809             if (!this.hasListener('actionfailed')) {
6810                 Roo.log("need to add dialog support");
6811                 /*
6812                 Roo.MessageBox.alert("Error",
6813                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6814                         action.result.errorMsg :
6815                         "Saving Failed, please check your entries or try again"
6816                 );
6817                 */
6818             }
6819             
6820             this.fireEvent('actionfailed', this, action);
6821         }
6822         
6823     },
6824     /**
6825      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6826      * @param {String} id The value to search for
6827      * @return Field
6828      */
6829     findField : function(id){
6830         var items = this.getItems();
6831         var field = items.get(id);
6832         if(!field){
6833              items.each(function(f){
6834                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6835                     field = f;
6836                     return false;
6837                 }
6838                 return true;
6839             });
6840         }
6841         return field || null;
6842     },
6843      /**
6844      * Mark fields in this form invalid in bulk.
6845      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6846      * @return {BasicForm} this
6847      */
6848     markInvalid : function(errors){
6849         if(errors instanceof Array){
6850             for(var i = 0, len = errors.length; i < len; i++){
6851                 var fieldError = errors[i];
6852                 var f = this.findField(fieldError.id);
6853                 if(f){
6854                     f.markInvalid(fieldError.msg);
6855                 }
6856             }
6857         }else{
6858             var field, id;
6859             for(id in errors){
6860                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6861                     field.markInvalid(errors[id]);
6862                 }
6863             }
6864         }
6865         //Roo.each(this.childForms || [], function (f) {
6866         //    f.markInvalid(errors);
6867         //});
6868         
6869         return this;
6870     },
6871
6872     /**
6873      * Set values for fields in this form in bulk.
6874      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6875      * @return {BasicForm} this
6876      */
6877     setValues : function(values){
6878         if(values instanceof Array){ // array of objects
6879             for(var i = 0, len = values.length; i < len; i++){
6880                 var v = values[i];
6881                 var f = this.findField(v.id);
6882                 if(f){
6883                     f.setValue(v.value);
6884                     if(this.trackResetOnLoad){
6885                         f.originalValue = f.getValue();
6886                     }
6887                 }
6888             }
6889         }else{ // object hash
6890             var field, id;
6891             for(id in values){
6892                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6893                     
6894                     if (field.setFromData && 
6895                         field.valueField && 
6896                         field.displayField &&
6897                         // combos' with local stores can 
6898                         // be queried via setValue()
6899                         // to set their value..
6900                         (field.store && !field.store.isLocal)
6901                         ) {
6902                         // it's a combo
6903                         var sd = { };
6904                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6905                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6906                         field.setFromData(sd);
6907                         
6908                     } else {
6909                         field.setValue(values[id]);
6910                     }
6911                     
6912                     
6913                     if(this.trackResetOnLoad){
6914                         field.originalValue = field.getValue();
6915                     }
6916                 }
6917             }
6918         }
6919          
6920         //Roo.each(this.childForms || [], function (f) {
6921         //    f.setValues(values);
6922         //});
6923                 
6924         return this;
6925     },
6926
6927     /**
6928      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6929      * they are returned as an array.
6930      * @param {Boolean} asString
6931      * @return {Object}
6932      */
6933     getValues : function(asString){
6934         //if (this.childForms) {
6935             // copy values from the child forms
6936         //    Roo.each(this.childForms, function (f) {
6937         //        this.setValues(f.getValues());
6938         //    }, this);
6939         //}
6940         
6941         
6942         
6943         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6944         if(asString === true){
6945             return fs;
6946         }
6947         return Roo.urlDecode(fs);
6948     },
6949     
6950     /**
6951      * Returns the fields in this form as an object with key/value pairs. 
6952      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6953      * @return {Object}
6954      */
6955     getFieldValues : function(with_hidden)
6956     {
6957         var items = this.getItems();
6958         var ret = {};
6959         items.each(function(f){
6960             if (!f.getName()) {
6961                 return;
6962             }
6963             var v = f.getValue();
6964             if (f.inputType =='radio') {
6965                 if (typeof(ret[f.getName()]) == 'undefined') {
6966                     ret[f.getName()] = ''; // empty..
6967                 }
6968                 
6969                 if (!f.el.dom.checked) {
6970                     return;
6971                     
6972                 }
6973                 v = f.el.dom.value;
6974                 
6975             }
6976             
6977             // not sure if this supported any more..
6978             if ((typeof(v) == 'object') && f.getRawValue) {
6979                 v = f.getRawValue() ; // dates..
6980             }
6981             // combo boxes where name != hiddenName...
6982             if (f.name != f.getName()) {
6983                 ret[f.name] = f.getRawValue();
6984             }
6985             ret[f.getName()] = v;
6986         });
6987         
6988         return ret;
6989     },
6990
6991     /**
6992      * Clears all invalid messages in this form.
6993      * @return {BasicForm} this
6994      */
6995     clearInvalid : function(){
6996         var items = this.getItems();
6997         
6998         items.each(function(f){
6999            f.clearInvalid();
7000         });
7001         
7002         
7003         
7004         return this;
7005     },
7006
7007     /**
7008      * Resets this form.
7009      * @return {BasicForm} this
7010      */
7011     reset : function(){
7012         var items = this.getItems();
7013         items.each(function(f){
7014             f.reset();
7015         });
7016         
7017         Roo.each(this.childForms || [], function (f) {
7018             f.reset();
7019         });
7020        
7021         
7022         return this;
7023     },
7024     getItems : function()
7025     {
7026         var r=new Roo.util.MixedCollection(false, function(o){
7027             return o.id || (o.id = Roo.id());
7028         });
7029         var iter = function(el) {
7030             if (el.inputEl) {
7031                 r.add(el);
7032             }
7033             if (!el.items) {
7034                 return;
7035             }
7036             Roo.each(el.items,function(e) {
7037                 iter(e);
7038             });
7039             
7040             
7041         };
7042         
7043         iter(this);
7044         return r;
7045         
7046         
7047         
7048         
7049     }
7050     
7051 });
7052
7053  
7054 /*
7055  * Based on:
7056  * Ext JS Library 1.1.1
7057  * Copyright(c) 2006-2007, Ext JS, LLC.
7058  *
7059  * Originally Released Under LGPL - original licence link has changed is not relivant.
7060  *
7061  * Fork - LGPL
7062  * <script type="text/javascript">
7063  */
7064 /**
7065  * @class Roo.form.VTypes
7066  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7067  * @singleton
7068  */
7069 Roo.form.VTypes = function(){
7070     // closure these in so they are only created once.
7071     var alpha = /^[a-zA-Z_]+$/;
7072     var alphanum = /^[a-zA-Z0-9_]+$/;
7073     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7074     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7075
7076     // All these messages and functions are configurable
7077     return {
7078         /**
7079          * The function used to validate email addresses
7080          * @param {String} value The email address
7081          */
7082         'email' : function(v){
7083             return email.test(v);
7084         },
7085         /**
7086          * The error text to display when the email validation function returns false
7087          * @type String
7088          */
7089         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7090         /**
7091          * The keystroke filter mask to be applied on email input
7092          * @type RegExp
7093          */
7094         'emailMask' : /[a-z0-9_\.\-@]/i,
7095
7096         /**
7097          * The function used to validate URLs
7098          * @param {String} value The URL
7099          */
7100         'url' : function(v){
7101             return url.test(v);
7102         },
7103         /**
7104          * The error text to display when the url validation function returns false
7105          * @type String
7106          */
7107         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7108         
7109         /**
7110          * The function used to validate alpha values
7111          * @param {String} value The value
7112          */
7113         'alpha' : function(v){
7114             return alpha.test(v);
7115         },
7116         /**
7117          * The error text to display when the alpha validation function returns false
7118          * @type String
7119          */
7120         'alphaText' : 'This field should only contain letters and _',
7121         /**
7122          * The keystroke filter mask to be applied on alpha input
7123          * @type RegExp
7124          */
7125         'alphaMask' : /[a-z_]/i,
7126
7127         /**
7128          * The function used to validate alphanumeric values
7129          * @param {String} value The value
7130          */
7131         'alphanum' : function(v){
7132             return alphanum.test(v);
7133         },
7134         /**
7135          * The error text to display when the alphanumeric validation function returns false
7136          * @type String
7137          */
7138         'alphanumText' : 'This field should only contain letters, numbers and _',
7139         /**
7140          * The keystroke filter mask to be applied on alphanumeric input
7141          * @type RegExp
7142          */
7143         'alphanumMask' : /[a-z0-9_]/i
7144     };
7145 }();/*
7146  * - LGPL
7147  *
7148  * Input
7149  * 
7150  */
7151
7152 /**
7153  * @class Roo.bootstrap.Input
7154  * @extends Roo.bootstrap.Component
7155  * Bootstrap Input class
7156  * @cfg {Boolean} disabled is it disabled
7157  * @cfg {String} fieldLabel - the label associated
7158  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7159  * @cfg {String} name name of the input
7160  * @cfg {string} fieldLabel - the label associated
7161  * @cfg {string}  inputType - input / file submit ...
7162  * @cfg {string} placeholder - placeholder to put in text.
7163  * @cfg {string}  before - input group add on before
7164  * @cfg {string} after - input group add on after
7165  * @cfg {string} size - (lg|sm) or leave empty..
7166  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7167  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7168  * @cfg {Number} md colspan out of 12 for computer-sized screens
7169  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7170  * @cfg {string} value default value of the input
7171  * @cfg {Number} labelWidth set the width of label (0-12)
7172  * @cfg {String} labelAlign (top|left)
7173  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7174  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7175
7176  * @cfg {String} align (left|center|right) Default left
7177  * 
7178  * 
7179  * 
7180  * @constructor
7181  * Create a new Input
7182  * @param {Object} config The config object
7183  */
7184
7185 Roo.bootstrap.Input = function(config){
7186     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7187    
7188         this.addEvents({
7189             /**
7190              * @event focus
7191              * Fires when this field receives input focus.
7192              * @param {Roo.form.Field} this
7193              */
7194             focus : true,
7195             /**
7196              * @event blur
7197              * Fires when this field loses input focus.
7198              * @param {Roo.form.Field} this
7199              */
7200             blur : true,
7201             /**
7202              * @event specialkey
7203              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7204              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7205              * @param {Roo.form.Field} this
7206              * @param {Roo.EventObject} e The event object
7207              */
7208             specialkey : true,
7209             /**
7210              * @event change
7211              * Fires just before the field blurs if the field value has changed.
7212              * @param {Roo.form.Field} this
7213              * @param {Mixed} newValue The new value
7214              * @param {Mixed} oldValue The original value
7215              */
7216             change : true,
7217             /**
7218              * @event invalid
7219              * Fires after the field has been marked as invalid.
7220              * @param {Roo.form.Field} this
7221              * @param {String} msg The validation message
7222              */
7223             invalid : true,
7224             /**
7225              * @event valid
7226              * Fires after the field has been validated with no errors.
7227              * @param {Roo.form.Field} this
7228              */
7229             valid : true,
7230              /**
7231              * @event keyup
7232              * Fires after the key up
7233              * @param {Roo.form.Field} this
7234              * @param {Roo.EventObject}  e The event Object
7235              */
7236             keyup : true
7237         });
7238 };
7239
7240 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7241      /**
7242      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7243       automatic validation (defaults to "keyup").
7244      */
7245     validationEvent : "keyup",
7246      /**
7247      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7248      */
7249     validateOnBlur : true,
7250     /**
7251      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7252      */
7253     validationDelay : 250,
7254      /**
7255      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7256      */
7257     focusClass : "x-form-focus",  // not needed???
7258     
7259        
7260     /**
7261      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7262      */
7263     invalidClass : "has-warning",
7264     
7265     /**
7266      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7267      */
7268     validClass : "has-success",
7269     
7270     /**
7271      * @cfg {Boolean} hasFeedback (true|false) default true
7272      */
7273     hasFeedback : true,
7274     
7275     /**
7276      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7277      */
7278     invalidFeedbackClass : "glyphicon-warning-sign",
7279     
7280     /**
7281      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7282      */
7283     validFeedbackClass : "glyphicon-ok",
7284     
7285     /**
7286      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7287      */
7288     selectOnFocus : false,
7289     
7290      /**
7291      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7292      */
7293     maskRe : null,
7294        /**
7295      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7296      */
7297     vtype : null,
7298     
7299       /**
7300      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7301      */
7302     disableKeyFilter : false,
7303     
7304        /**
7305      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7306      */
7307     disabled : false,
7308      /**
7309      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7310      */
7311     allowBlank : true,
7312     /**
7313      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7314      */
7315     blankText : "This field is required",
7316     
7317      /**
7318      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7319      */
7320     minLength : 0,
7321     /**
7322      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7323      */
7324     maxLength : Number.MAX_VALUE,
7325     /**
7326      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7327      */
7328     minLengthText : "The minimum length for this field is {0}",
7329     /**
7330      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7331      */
7332     maxLengthText : "The maximum length for this field is {0}",
7333   
7334     
7335     /**
7336      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7337      * If available, this function will be called only after the basic validators all return true, and will be passed the
7338      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7339      */
7340     validator : null,
7341     /**
7342      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7343      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7344      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7345      */
7346     regex : null,
7347     /**
7348      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7349      */
7350     regexText : "",
7351     
7352     autocomplete: false,
7353     
7354     
7355     fieldLabel : '',
7356     inputType : 'text',
7357     
7358     name : false,
7359     placeholder: false,
7360     before : false,
7361     after : false,
7362     size : false,
7363     hasFocus : false,
7364     preventMark: false,
7365     isFormField : true,
7366     value : '',
7367     labelWidth : 2,
7368     labelAlign : false,
7369     readOnly : false,
7370     align : false,
7371     formatedValue : false,
7372     
7373     parentLabelAlign : function()
7374     {
7375         var parent = this;
7376         while (parent.parent()) {
7377             parent = parent.parent();
7378             if (typeof(parent.labelAlign) !='undefined') {
7379                 return parent.labelAlign;
7380             }
7381         }
7382         return 'left';
7383         
7384     },
7385     
7386     getAutoCreate : function(){
7387         
7388         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7389         
7390         var id = Roo.id();
7391         
7392         var cfg = {};
7393         
7394         if(this.inputType != 'hidden'){
7395             cfg.cls = 'form-group' //input-group
7396         }
7397         
7398         var input =  {
7399             tag: 'input',
7400             id : id,
7401             type : this.inputType,
7402             value : this.value,
7403             cls : 'form-control',
7404             placeholder : this.placeholder || '',
7405             autocomplete : this.autocomplete || 'new-password'
7406         };
7407         
7408         
7409         if(this.align){
7410             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7411         }
7412         
7413         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7414             input.maxLength = this.maxLength;
7415         }
7416         
7417         if (this.disabled) {
7418             input.disabled=true;
7419         }
7420         
7421         if (this.readOnly) {
7422             input.readonly=true;
7423         }
7424         
7425         if (this.name) {
7426             input.name = this.name;
7427         }
7428         if (this.size) {
7429             input.cls += ' input-' + this.size;
7430         }
7431         var settings=this;
7432         ['xs','sm','md','lg'].map(function(size){
7433             if (settings[size]) {
7434                 cfg.cls += ' col-' + size + '-' + settings[size];
7435             }
7436         });
7437         
7438         var inputblock = input;
7439         
7440         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7441             
7442             var feedback = {
7443                 tag: 'span',
7444                 cls: 'glyphicon form-control-feedback'
7445             };
7446
7447             inputblock = {
7448                 cls : 'has-feedback',
7449                 cn :  [
7450                     input,
7451                     feedback
7452                 ] 
7453             };  
7454         }
7455          
7456 //        var inputblock = input;
7457         
7458         if (this.before || this.after) {
7459             
7460             inputblock = {
7461                 cls : 'input-group',
7462                 cn :  [] 
7463             };
7464             
7465             if (this.before && typeof(this.before) == 'string') {
7466                 
7467                 inputblock.cn.push({
7468                     tag :'span',
7469                     cls : 'roo-input-before input-group-addon',
7470                     html : this.before
7471                 });
7472             }
7473             if (this.before && typeof(this.before) == 'object') {
7474                 this.before = Roo.factory(this.before);
7475                 Roo.log(this.before);
7476                 inputblock.cn.push({
7477                     tag :'span',
7478                     cls : 'roo-input-before input-group-' +
7479                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7480                 });
7481             }
7482             
7483             inputblock.cn.push(input);
7484             
7485             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7486                 inputblock.cls += ' has-feedback';
7487                 inputblock.cn.push(feedback);
7488             }
7489             
7490             if (this.after && typeof(this.after) == 'string') {
7491                 inputblock.cn.push({
7492                     tag :'span',
7493                     cls : 'roo-input-after input-group-addon',
7494                     html : this.after
7495                 });
7496             }
7497             if (this.after && typeof(this.after) == 'object') {
7498                 this.after = Roo.factory(this.after);
7499                 Roo.log(this.after);
7500                 inputblock.cn.push({
7501                     tag :'span',
7502                     cls : 'roo-input-after input-group-' +
7503                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7504                 });
7505             }
7506         };
7507         
7508         if (align ==='left' && this.fieldLabel.length) {
7509                 Roo.log("left and has label");
7510                 cfg.cn = [
7511                     
7512                     {
7513                         tag: 'label',
7514                         'for' :  id,
7515                         cls : 'control-label col-sm-' + this.labelWidth,
7516                         html : this.fieldLabel
7517                         
7518                     },
7519                     {
7520                         cls : "col-sm-" + (12 - this.labelWidth), 
7521                         cn: [
7522                             inputblock
7523                         ]
7524                     }
7525                     
7526                 ];
7527         } else if ( this.fieldLabel.length) {
7528                 Roo.log(" label");
7529                  cfg.cn = [
7530                    
7531                     {
7532                         tag: 'label',
7533                         //cls : 'input-group-addon',
7534                         html : this.fieldLabel
7535                         
7536                     },
7537                     
7538                     inputblock
7539                     
7540                 ];
7541
7542         } else {
7543             
7544                 Roo.log(" no label && no align");
7545                 cfg.cn = [
7546                     
7547                         inputblock
7548                     
7549                 ];
7550                 
7551                 
7552         };
7553         Roo.log('input-parentType: ' + this.parentType);
7554         
7555         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7556            cfg.cls += ' navbar-form';
7557            Roo.log(cfg);
7558         }
7559         
7560         return cfg;
7561         
7562     },
7563     /**
7564      * return the real input element.
7565      */
7566     inputEl: function ()
7567     {
7568         return this.el.select('input.form-control',true).first();
7569     },
7570     
7571     tooltipEl : function()
7572     {
7573         return this.inputEl();
7574     },
7575     
7576     setDisabled : function(v)
7577     {
7578         var i  = this.inputEl().dom;
7579         if (!v) {
7580             i.removeAttribute('disabled');
7581             return;
7582             
7583         }
7584         i.setAttribute('disabled','true');
7585     },
7586     initEvents : function()
7587     {
7588           
7589         this.inputEl().on("keydown" , this.fireKey,  this);
7590         this.inputEl().on("focus", this.onFocus,  this);
7591         this.inputEl().on("blur", this.onBlur,  this);
7592         
7593         this.inputEl().relayEvent('keyup', this);
7594
7595         // reference to original value for reset
7596         this.originalValue = this.getValue();
7597         //Roo.form.TextField.superclass.initEvents.call(this);
7598         if(this.validationEvent == 'keyup'){
7599             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7600             this.inputEl().on('keyup', this.filterValidation, this);
7601         }
7602         else if(this.validationEvent !== false){
7603             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7604         }
7605         
7606         if(this.selectOnFocus){
7607             this.on("focus", this.preFocus, this);
7608             
7609         }
7610         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7611             this.inputEl().on("keypress", this.filterKeys, this);
7612         }
7613        /* if(this.grow){
7614             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7615             this.el.on("click", this.autoSize,  this);
7616         }
7617         */
7618         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7619             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7620         }
7621         
7622         if (typeof(this.before) == 'object') {
7623             this.before.render(this.el.select('.roo-input-before',true).first());
7624         }
7625         if (typeof(this.after) == 'object') {
7626             this.after.render(this.el.select('.roo-input-after',true).first());
7627         }
7628         
7629         
7630     },
7631     filterValidation : function(e){
7632         if(!e.isNavKeyPress()){
7633             this.validationTask.delay(this.validationDelay);
7634         }
7635     },
7636      /**
7637      * Validates the field value
7638      * @return {Boolean} True if the value is valid, else false
7639      */
7640     validate : function(){
7641         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7642         if(this.disabled || this.validateValue(this.getRawValue())){
7643             this.markValid();
7644             return true;
7645         }
7646         
7647         this.markInvalid();
7648         return false;
7649     },
7650     
7651     
7652     /**
7653      * Validates a value according to the field's validation rules and marks the field as invalid
7654      * if the validation fails
7655      * @param {Mixed} value The value to validate
7656      * @return {Boolean} True if the value is valid, else false
7657      */
7658     validateValue : function(value){
7659         if(value.length < 1)  { // if it's blank
7660             if(this.allowBlank){
7661                 return true;
7662             }
7663             return false;
7664         }
7665         
7666         if(value.length < this.minLength){
7667             return false;
7668         }
7669         if(value.length > this.maxLength){
7670             return false;
7671         }
7672         if(this.vtype){
7673             var vt = Roo.form.VTypes;
7674             if(!vt[this.vtype](value, this)){
7675                 return false;
7676             }
7677         }
7678         if(typeof this.validator == "function"){
7679             var msg = this.validator(value);
7680             if(msg !== true){
7681                 return false;
7682             }
7683         }
7684         
7685         if(this.regex && !this.regex.test(value)){
7686             return false;
7687         }
7688         
7689         return true;
7690     },
7691
7692     
7693     
7694      // private
7695     fireKey : function(e){
7696         //Roo.log('field ' + e.getKey());
7697         if(e.isNavKeyPress()){
7698             this.fireEvent("specialkey", this, e);
7699         }
7700     },
7701     focus : function (selectText){
7702         if(this.rendered){
7703             this.inputEl().focus();
7704             if(selectText === true){
7705                 this.inputEl().dom.select();
7706             }
7707         }
7708         return this;
7709     } ,
7710     
7711     onFocus : function(){
7712         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7713            // this.el.addClass(this.focusClass);
7714         }
7715         if(!this.hasFocus){
7716             this.hasFocus = true;
7717             this.startValue = this.getValue();
7718             this.fireEvent("focus", this);
7719         }
7720     },
7721     
7722     beforeBlur : Roo.emptyFn,
7723
7724     
7725     // private
7726     onBlur : function(){
7727         this.beforeBlur();
7728         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7729             //this.el.removeClass(this.focusClass);
7730         }
7731         this.hasFocus = false;
7732         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7733             this.validate();
7734         }
7735         var v = this.getValue();
7736         if(String(v) !== String(this.startValue)){
7737             this.fireEvent('change', this, v, this.startValue);
7738         }
7739         this.fireEvent("blur", this);
7740     },
7741     
7742     /**
7743      * Resets the current field value to the originally loaded value and clears any validation messages
7744      */
7745     reset : function(){
7746         this.setValue(this.originalValue);
7747         this.validate();
7748     },
7749      /**
7750      * Returns the name of the field
7751      * @return {Mixed} name The name field
7752      */
7753     getName: function(){
7754         return this.name;
7755     },
7756      /**
7757      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7758      * @return {Mixed} value The field value
7759      */
7760     getValue : function(){
7761         
7762         var v = this.inputEl().getValue();
7763         
7764         return v;
7765     },
7766     /**
7767      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7768      * @return {Mixed} value The field value
7769      */
7770     getRawValue : function(){
7771         var v = this.inputEl().getValue();
7772         
7773         return v;
7774     },
7775     
7776     /**
7777      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7778      * @param {Mixed} value The value to set
7779      */
7780     setRawValue : function(v){
7781         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7782     },
7783     
7784     selectText : function(start, end){
7785         var v = this.getRawValue();
7786         if(v.length > 0){
7787             start = start === undefined ? 0 : start;
7788             end = end === undefined ? v.length : end;
7789             var d = this.inputEl().dom;
7790             if(d.setSelectionRange){
7791                 d.setSelectionRange(start, end);
7792             }else if(d.createTextRange){
7793                 var range = d.createTextRange();
7794                 range.moveStart("character", start);
7795                 range.moveEnd("character", v.length-end);
7796                 range.select();
7797             }
7798         }
7799     },
7800     
7801     /**
7802      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7803      * @param {Mixed} value The value to set
7804      */
7805     setValue : function(v){
7806         this.value = v;
7807         if(this.rendered){
7808             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7809             this.validate();
7810         }
7811     },
7812     
7813     /*
7814     processValue : function(value){
7815         if(this.stripCharsRe){
7816             var newValue = value.replace(this.stripCharsRe, '');
7817             if(newValue !== value){
7818                 this.setRawValue(newValue);
7819                 return newValue;
7820             }
7821         }
7822         return value;
7823     },
7824   */
7825     preFocus : function(){
7826         
7827         if(this.selectOnFocus){
7828             this.inputEl().dom.select();
7829         }
7830     },
7831     filterKeys : function(e){
7832         var k = e.getKey();
7833         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7834             return;
7835         }
7836         var c = e.getCharCode(), cc = String.fromCharCode(c);
7837         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7838             return;
7839         }
7840         if(!this.maskRe.test(cc)){
7841             e.stopEvent();
7842         }
7843     },
7844      /**
7845      * Clear any invalid styles/messages for this field
7846      */
7847     clearInvalid : function(){
7848         
7849         if(!this.el || this.preventMark){ // not rendered
7850             return;
7851         }
7852         this.el.removeClass(this.invalidClass);
7853         
7854         this.fireEvent('valid', this);
7855     },
7856     
7857      /**
7858      * Mark this field as valid
7859      */
7860     markValid : function(){
7861         if(!this.el  || this.preventMark){ // not rendered
7862             return;
7863         }
7864         
7865         this.el.removeClass([this.invalidClass, this.validClass]);
7866         
7867         if(this.disabled || this.allowBlank){
7868             return;
7869         }
7870         
7871         this.el.addClass(this.validClass);
7872         
7873         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
7874             
7875             var feedback = this.el.select('.form-control-feedback', true).first();
7876             
7877             if(feedback){
7878                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
7879                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
7880             }
7881             
7882         }
7883         
7884         this.fireEvent('valid', this);
7885     },
7886     
7887      /**
7888      * Mark this field as invalid
7889      * @param {String} msg The validation message
7890      */
7891     markInvalid : function(msg){
7892         if(!this.el  || this.preventMark){ // not rendered
7893             return;
7894         }
7895         
7896         this.el.removeClass([this.invalidClass, this.validClass]);
7897         
7898         if(this.disabled || this.allowBlank){
7899             return;
7900         }
7901         
7902         this.el.addClass(this.invalidClass);
7903         
7904         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7905             
7906             var feedback = this.el.select('.form-control-feedback', true).first();
7907             
7908             if(feedback){
7909                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
7910                 
7911                 if(this.getValue().length){
7912                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
7913                 }
7914                 
7915             }
7916             
7917         }
7918         
7919         this.fireEvent('invalid', this, msg);
7920     },
7921     // private
7922     SafariOnKeyDown : function(event)
7923     {
7924         // this is a workaround for a password hang bug on chrome/ webkit.
7925         
7926         var isSelectAll = false;
7927         
7928         if(this.inputEl().dom.selectionEnd > 0){
7929             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7930         }
7931         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7932             event.preventDefault();
7933             this.setValue('');
7934             return;
7935         }
7936         
7937         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
7938             
7939             event.preventDefault();
7940             // this is very hacky as keydown always get's upper case.
7941             //
7942             var cc = String.fromCharCode(event.getCharCode());
7943             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7944             
7945         }
7946     },
7947     adjustWidth : function(tag, w){
7948         tag = tag.toLowerCase();
7949         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7950             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7951                 if(tag == 'input'){
7952                     return w + 2;
7953                 }
7954                 if(tag == 'textarea'){
7955                     return w-2;
7956                 }
7957             }else if(Roo.isOpera){
7958                 if(tag == 'input'){
7959                     return w + 2;
7960                 }
7961                 if(tag == 'textarea'){
7962                     return w-2;
7963                 }
7964             }
7965         }
7966         return w;
7967     }
7968     
7969 });
7970
7971  
7972 /*
7973  * - LGPL
7974  *
7975  * Input
7976  * 
7977  */
7978
7979 /**
7980  * @class Roo.bootstrap.TextArea
7981  * @extends Roo.bootstrap.Input
7982  * Bootstrap TextArea class
7983  * @cfg {Number} cols Specifies the visible width of a text area
7984  * @cfg {Number} rows Specifies the visible number of lines in a text area
7985  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7986  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7987  * @cfg {string} html text
7988  * 
7989  * @constructor
7990  * Create a new TextArea
7991  * @param {Object} config The config object
7992  */
7993
7994 Roo.bootstrap.TextArea = function(config){
7995     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7996    
7997 };
7998
7999 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8000      
8001     cols : false,
8002     rows : 5,
8003     readOnly : false,
8004     warp : 'soft',
8005     resize : false,
8006     value: false,
8007     html: false,
8008     
8009     getAutoCreate : function(){
8010         
8011         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8012         
8013         var id = Roo.id();
8014         
8015         var cfg = {};
8016         
8017         var input =  {
8018             tag: 'textarea',
8019             id : id,
8020             warp : this.warp,
8021             rows : this.rows,
8022             value : this.value || '',
8023             html: this.html || '',
8024             cls : 'form-control',
8025             placeholder : this.placeholder || '' 
8026             
8027         };
8028         
8029         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8030             input.maxLength = this.maxLength;
8031         }
8032         
8033         if(this.resize){
8034             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8035         }
8036         
8037         if(this.cols){
8038             input.cols = this.cols;
8039         }
8040         
8041         if (this.readOnly) {
8042             input.readonly = true;
8043         }
8044         
8045         if (this.name) {
8046             input.name = this.name;
8047         }
8048         
8049         if (this.size) {
8050             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8051         }
8052         
8053         var settings=this;
8054         ['xs','sm','md','lg'].map(function(size){
8055             if (settings[size]) {
8056                 cfg.cls += ' col-' + size + '-' + settings[size];
8057             }
8058         });
8059         
8060         var inputblock = input;
8061         
8062         if(this.hasFeedback && !this.allowBlank){
8063             
8064             var feedback = {
8065                 tag: 'span',
8066                 cls: 'glyphicon form-control-feedback'
8067             };
8068
8069             inputblock = {
8070                 cls : 'has-feedback',
8071                 cn :  [
8072                     input,
8073                     feedback
8074                 ] 
8075             };  
8076         }
8077         
8078         
8079         if (this.before || this.after) {
8080             
8081             inputblock = {
8082                 cls : 'input-group',
8083                 cn :  [] 
8084             };
8085             if (this.before) {
8086                 inputblock.cn.push({
8087                     tag :'span',
8088                     cls : 'input-group-addon',
8089                     html : this.before
8090                 });
8091             }
8092             
8093             inputblock.cn.push(input);
8094             
8095             if(this.hasFeedback && !this.allowBlank){
8096                 inputblock.cls += ' has-feedback';
8097                 inputblock.cn.push(feedback);
8098             }
8099             
8100             if (this.after) {
8101                 inputblock.cn.push({
8102                     tag :'span',
8103                     cls : 'input-group-addon',
8104                     html : this.after
8105                 });
8106             }
8107             
8108         }
8109         
8110         if (align ==='left' && this.fieldLabel.length) {
8111                 Roo.log("left and has label");
8112                 cfg.cn = [
8113                     
8114                     {
8115                         tag: 'label',
8116                         'for' :  id,
8117                         cls : 'control-label col-sm-' + this.labelWidth,
8118                         html : this.fieldLabel
8119                         
8120                     },
8121                     {
8122                         cls : "col-sm-" + (12 - this.labelWidth), 
8123                         cn: [
8124                             inputblock
8125                         ]
8126                     }
8127                     
8128                 ];
8129         } else if ( this.fieldLabel.length) {
8130                 Roo.log(" label");
8131                  cfg.cn = [
8132                    
8133                     {
8134                         tag: 'label',
8135                         //cls : 'input-group-addon',
8136                         html : this.fieldLabel
8137                         
8138                     },
8139                     
8140                     inputblock
8141                     
8142                 ];
8143
8144         } else {
8145             
8146                    Roo.log(" no label && no align");
8147                 cfg.cn = [
8148                     
8149                         inputblock
8150                     
8151                 ];
8152                 
8153                 
8154         }
8155         
8156         if (this.disabled) {
8157             input.disabled=true;
8158         }
8159         
8160         return cfg;
8161         
8162     },
8163     /**
8164      * return the real textarea element.
8165      */
8166     inputEl: function ()
8167     {
8168         return this.el.select('textarea.form-control',true).first();
8169     }
8170 });
8171
8172  
8173 /*
8174  * - LGPL
8175  *
8176  * trigger field - base class for combo..
8177  * 
8178  */
8179  
8180 /**
8181  * @class Roo.bootstrap.TriggerField
8182  * @extends Roo.bootstrap.Input
8183  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8184  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8185  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8186  * for which you can provide a custom implementation.  For example:
8187  * <pre><code>
8188 var trigger = new Roo.bootstrap.TriggerField();
8189 trigger.onTriggerClick = myTriggerFn;
8190 trigger.applyTo('my-field');
8191 </code></pre>
8192  *
8193  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8194  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8195  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8196  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8197  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8198
8199  * @constructor
8200  * Create a new TriggerField.
8201  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8202  * to the base TextField)
8203  */
8204 Roo.bootstrap.TriggerField = function(config){
8205     this.mimicing = false;
8206     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8207 };
8208
8209 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8210     /**
8211      * @cfg {String} triggerClass A CSS class to apply to the trigger
8212      */
8213      /**
8214      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8215      */
8216     hideTrigger:false,
8217
8218     /** @cfg {Boolean} grow @hide */
8219     /** @cfg {Number} growMin @hide */
8220     /** @cfg {Number} growMax @hide */
8221
8222     /**
8223      * @hide 
8224      * @method
8225      */
8226     autoSize: Roo.emptyFn,
8227     // private
8228     monitorTab : true,
8229     // private
8230     deferHeight : true,
8231
8232     
8233     actionMode : 'wrap',
8234     
8235     caret : false,
8236     
8237     
8238     getAutoCreate : function(){
8239        
8240         var align = this.labelAlign || this.parentLabelAlign();
8241         
8242         var id = Roo.id();
8243         
8244         var cfg = {
8245             cls: 'form-group' //input-group
8246         };
8247         
8248         
8249         var input =  {
8250             tag: 'input',
8251             id : id,
8252             type : this.inputType,
8253             cls : 'form-control',
8254             autocomplete: 'new-password',
8255             placeholder : this.placeholder || '' 
8256             
8257         };
8258         if (this.name) {
8259             input.name = this.name;
8260         }
8261         if (this.size) {
8262             input.cls += ' input-' + this.size;
8263         }
8264         
8265         if (this.disabled) {
8266             input.disabled=true;
8267         }
8268         
8269         var inputblock = input;
8270         
8271         if(this.hasFeedback && !this.allowBlank){
8272             
8273             var feedback = {
8274                 tag: 'span',
8275                 cls: 'glyphicon form-control-feedback'
8276             };
8277
8278             inputblock = {
8279                 cls : 'has-feedback',
8280                 cn :  [
8281                     input,
8282                     feedback
8283                 ] 
8284             };  
8285         }
8286         
8287         if (this.before || this.after) {
8288             
8289             inputblock = {
8290                 cls : 'input-group',
8291                 cn :  [] 
8292             };
8293             if (this.before) {
8294                 inputblock.cn.push({
8295                     tag :'span',
8296                     cls : 'input-group-addon',
8297                     html : this.before
8298                 });
8299             }
8300             
8301             inputblock.cn.push(input);
8302             
8303             if(this.hasFeedback && !this.allowBlank){
8304                 inputblock.cls += ' has-feedback';
8305                 inputblock.cn.push(feedback);
8306             }
8307             
8308             if (this.after) {
8309                 inputblock.cn.push({
8310                     tag :'span',
8311                     cls : 'input-group-addon',
8312                     html : this.after
8313                 });
8314             }
8315             
8316         };
8317         
8318         var box = {
8319             tag: 'div',
8320             cn: [
8321                 {
8322                     tag: 'input',
8323                     type : 'hidden',
8324                     cls: 'form-hidden-field'
8325                 },
8326                 inputblock
8327             ]
8328             
8329         };
8330         
8331         if(this.multiple){
8332             Roo.log('multiple');
8333             
8334             box = {
8335                 tag: 'div',
8336                 cn: [
8337                     {
8338                         tag: 'input',
8339                         type : 'hidden',
8340                         cls: 'form-hidden-field'
8341                     },
8342                     {
8343                         tag: 'ul',
8344                         cls: 'select2-choices',
8345                         cn:[
8346                             {
8347                                 tag: 'li',
8348                                 cls: 'select2-search-field',
8349                                 cn: [
8350
8351                                     inputblock
8352                                 ]
8353                             }
8354                         ]
8355                     }
8356                 ]
8357             }
8358         };
8359         
8360         var combobox = {
8361             cls: 'select2-container input-group',
8362             cn: [
8363                 box
8364 //                {
8365 //                    tag: 'ul',
8366 //                    cls: 'typeahead typeahead-long dropdown-menu',
8367 //                    style: 'display:none'
8368 //                }
8369             ]
8370         };
8371         
8372         if(!this.multiple && this.showToggleBtn){
8373             
8374             var caret = {
8375                         tag: 'span',
8376                         cls: 'caret'
8377              };
8378             if (this.caret != false) {
8379                 caret = {
8380                      tag: 'i',
8381                      cls: 'fa fa-' + this.caret
8382                 };
8383                 
8384             }
8385             
8386             combobox.cn.push({
8387                 tag :'span',
8388                 cls : 'input-group-addon btn dropdown-toggle',
8389                 cn : [
8390                     caret,
8391                     {
8392                         tag: 'span',
8393                         cls: 'combobox-clear',
8394                         cn  : [
8395                             {
8396                                 tag : 'i',
8397                                 cls: 'icon-remove'
8398                             }
8399                         ]
8400                     }
8401                 ]
8402
8403             })
8404         }
8405         
8406         if(this.multiple){
8407             combobox.cls += ' select2-container-multi';
8408         }
8409         
8410         if (align ==='left' && this.fieldLabel.length) {
8411             
8412                 Roo.log("left and has label");
8413                 cfg.cn = [
8414                     
8415                     {
8416                         tag: 'label',
8417                         'for' :  id,
8418                         cls : 'control-label col-sm-' + this.labelWidth,
8419                         html : this.fieldLabel
8420                         
8421                     },
8422                     {
8423                         cls : "col-sm-" + (12 - this.labelWidth), 
8424                         cn: [
8425                             combobox
8426                         ]
8427                     }
8428                     
8429                 ];
8430         } else if ( this.fieldLabel.length) {
8431                 Roo.log(" label");
8432                  cfg.cn = [
8433                    
8434                     {
8435                         tag: 'label',
8436                         //cls : 'input-group-addon',
8437                         html : this.fieldLabel
8438                         
8439                     },
8440                     
8441                     combobox
8442                     
8443                 ];
8444
8445         } else {
8446             
8447                 Roo.log(" no label && no align");
8448                 cfg = combobox
8449                      
8450                 
8451         }
8452          
8453         var settings=this;
8454         ['xs','sm','md','lg'].map(function(size){
8455             if (settings[size]) {
8456                 cfg.cls += ' col-' + size + '-' + settings[size];
8457             }
8458         });
8459         
8460         return cfg;
8461         
8462     },
8463     
8464     
8465     
8466     // private
8467     onResize : function(w, h){
8468 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8469 //        if(typeof w == 'number'){
8470 //            var x = w - this.trigger.getWidth();
8471 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8472 //            this.trigger.setStyle('left', x+'px');
8473 //        }
8474     },
8475
8476     // private
8477     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8478
8479     // private
8480     getResizeEl : function(){
8481         return this.inputEl();
8482     },
8483
8484     // private
8485     getPositionEl : function(){
8486         return this.inputEl();
8487     },
8488
8489     // private
8490     alignErrorIcon : function(){
8491         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8492     },
8493
8494     // private
8495     initEvents : function(){
8496         
8497         this.createList();
8498         
8499         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8500         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8501         if(!this.multiple && this.showToggleBtn){
8502             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8503             if(this.hideTrigger){
8504                 this.trigger.setDisplayed(false);
8505             }
8506             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8507         }
8508         
8509         if(this.multiple){
8510             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8511         }
8512         
8513         //this.trigger.addClassOnOver('x-form-trigger-over');
8514         //this.trigger.addClassOnClick('x-form-trigger-click');
8515         
8516         //if(!this.width){
8517         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8518         //}
8519     },
8520     
8521     createList : function()
8522     {
8523         this.list = Roo.get(document.body).createChild({
8524             tag: 'ul',
8525             cls: 'typeahead typeahead-long dropdown-menu',
8526             style: 'display:none'
8527         });
8528         
8529         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8530         
8531     },
8532
8533     // private
8534     initTrigger : function(){
8535        
8536     },
8537
8538     // private
8539     onDestroy : function(){
8540         if(this.trigger){
8541             this.trigger.removeAllListeners();
8542           //  this.trigger.remove();
8543         }
8544         //if(this.wrap){
8545         //    this.wrap.remove();
8546         //}
8547         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8548     },
8549
8550     // private
8551     onFocus : function(){
8552         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8553         /*
8554         if(!this.mimicing){
8555             this.wrap.addClass('x-trigger-wrap-focus');
8556             this.mimicing = true;
8557             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8558             if(this.monitorTab){
8559                 this.el.on("keydown", this.checkTab, this);
8560             }
8561         }
8562         */
8563     },
8564
8565     // private
8566     checkTab : function(e){
8567         if(e.getKey() == e.TAB){
8568             this.triggerBlur();
8569         }
8570     },
8571
8572     // private
8573     onBlur : function(){
8574         // do nothing
8575     },
8576
8577     // private
8578     mimicBlur : function(e, t){
8579         /*
8580         if(!this.wrap.contains(t) && this.validateBlur()){
8581             this.triggerBlur();
8582         }
8583         */
8584     },
8585
8586     // private
8587     triggerBlur : function(){
8588         this.mimicing = false;
8589         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8590         if(this.monitorTab){
8591             this.el.un("keydown", this.checkTab, this);
8592         }
8593         //this.wrap.removeClass('x-trigger-wrap-focus');
8594         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8595     },
8596
8597     // private
8598     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8599     validateBlur : function(e, t){
8600         return true;
8601     },
8602
8603     // private
8604     onDisable : function(){
8605         this.inputEl().dom.disabled = true;
8606         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8607         //if(this.wrap){
8608         //    this.wrap.addClass('x-item-disabled');
8609         //}
8610     },
8611
8612     // private
8613     onEnable : function(){
8614         this.inputEl().dom.disabled = false;
8615         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8616         //if(this.wrap){
8617         //    this.el.removeClass('x-item-disabled');
8618         //}
8619     },
8620
8621     // private
8622     onShow : function(){
8623         var ae = this.getActionEl();
8624         
8625         if(ae){
8626             ae.dom.style.display = '';
8627             ae.dom.style.visibility = 'visible';
8628         }
8629     },
8630
8631     // private
8632     
8633     onHide : function(){
8634         var ae = this.getActionEl();
8635         ae.dom.style.display = 'none';
8636     },
8637
8638     /**
8639      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8640      * by an implementing function.
8641      * @method
8642      * @param {EventObject} e
8643      */
8644     onTriggerClick : Roo.emptyFn
8645 });
8646  /*
8647  * Based on:
8648  * Ext JS Library 1.1.1
8649  * Copyright(c) 2006-2007, Ext JS, LLC.
8650  *
8651  * Originally Released Under LGPL - original licence link has changed is not relivant.
8652  *
8653  * Fork - LGPL
8654  * <script type="text/javascript">
8655  */
8656
8657
8658 /**
8659  * @class Roo.data.SortTypes
8660  * @singleton
8661  * Defines the default sorting (casting?) comparison functions used when sorting data.
8662  */
8663 Roo.data.SortTypes = {
8664     /**
8665      * Default sort that does nothing
8666      * @param {Mixed} s The value being converted
8667      * @return {Mixed} The comparison value
8668      */
8669     none : function(s){
8670         return s;
8671     },
8672     
8673     /**
8674      * The regular expression used to strip tags
8675      * @type {RegExp}
8676      * @property
8677      */
8678     stripTagsRE : /<\/?[^>]+>/gi,
8679     
8680     /**
8681      * Strips all HTML tags to sort on text only
8682      * @param {Mixed} s The value being converted
8683      * @return {String} The comparison value
8684      */
8685     asText : function(s){
8686         return String(s).replace(this.stripTagsRE, "");
8687     },
8688     
8689     /**
8690      * Strips all HTML tags to sort on text only - Case insensitive
8691      * @param {Mixed} s The value being converted
8692      * @return {String} The comparison value
8693      */
8694     asUCText : function(s){
8695         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8696     },
8697     
8698     /**
8699      * Case insensitive string
8700      * @param {Mixed} s The value being converted
8701      * @return {String} The comparison value
8702      */
8703     asUCString : function(s) {
8704         return String(s).toUpperCase();
8705     },
8706     
8707     /**
8708      * Date sorting
8709      * @param {Mixed} s The value being converted
8710      * @return {Number} The comparison value
8711      */
8712     asDate : function(s) {
8713         if(!s){
8714             return 0;
8715         }
8716         if(s instanceof Date){
8717             return s.getTime();
8718         }
8719         return Date.parse(String(s));
8720     },
8721     
8722     /**
8723      * Float sorting
8724      * @param {Mixed} s The value being converted
8725      * @return {Float} The comparison value
8726      */
8727     asFloat : function(s) {
8728         var val = parseFloat(String(s).replace(/,/g, ""));
8729         if(isNaN(val)) val = 0;
8730         return val;
8731     },
8732     
8733     /**
8734      * Integer sorting
8735      * @param {Mixed} s The value being converted
8736      * @return {Number} The comparison value
8737      */
8738     asInt : function(s) {
8739         var val = parseInt(String(s).replace(/,/g, ""));
8740         if(isNaN(val)) val = 0;
8741         return val;
8742     }
8743 };/*
8744  * Based on:
8745  * Ext JS Library 1.1.1
8746  * Copyright(c) 2006-2007, Ext JS, LLC.
8747  *
8748  * Originally Released Under LGPL - original licence link has changed is not relivant.
8749  *
8750  * Fork - LGPL
8751  * <script type="text/javascript">
8752  */
8753
8754 /**
8755 * @class Roo.data.Record
8756  * Instances of this class encapsulate both record <em>definition</em> information, and record
8757  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8758  * to access Records cached in an {@link Roo.data.Store} object.<br>
8759  * <p>
8760  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8761  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8762  * objects.<br>
8763  * <p>
8764  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8765  * @constructor
8766  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8767  * {@link #create}. The parameters are the same.
8768  * @param {Array} data An associative Array of data values keyed by the field name.
8769  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8770  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8771  * not specified an integer id is generated.
8772  */
8773 Roo.data.Record = function(data, id){
8774     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8775     this.data = data;
8776 };
8777
8778 /**
8779  * Generate a constructor for a specific record layout.
8780  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8781  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8782  * Each field definition object may contain the following properties: <ul>
8783  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
8784  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8785  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8786  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8787  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8788  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8789  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8790  * this may be omitted.</p></li>
8791  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8792  * <ul><li>auto (Default, implies no conversion)</li>
8793  * <li>string</li>
8794  * <li>int</li>
8795  * <li>float</li>
8796  * <li>boolean</li>
8797  * <li>date</li></ul></p></li>
8798  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8799  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8800  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8801  * by the Reader into an object that will be stored in the Record. It is passed the
8802  * following parameters:<ul>
8803  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8804  * </ul></p></li>
8805  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8806  * </ul>
8807  * <br>usage:<br><pre><code>
8808 var TopicRecord = Roo.data.Record.create(
8809     {name: 'title', mapping: 'topic_title'},
8810     {name: 'author', mapping: 'username'},
8811     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8812     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8813     {name: 'lastPoster', mapping: 'user2'},
8814     {name: 'excerpt', mapping: 'post_text'}
8815 );
8816
8817 var myNewRecord = new TopicRecord({
8818     title: 'Do my job please',
8819     author: 'noobie',
8820     totalPosts: 1,
8821     lastPost: new Date(),
8822     lastPoster: 'Animal',
8823     excerpt: 'No way dude!'
8824 });
8825 myStore.add(myNewRecord);
8826 </code></pre>
8827  * @method create
8828  * @static
8829  */
8830 Roo.data.Record.create = function(o){
8831     var f = function(){
8832         f.superclass.constructor.apply(this, arguments);
8833     };
8834     Roo.extend(f, Roo.data.Record);
8835     var p = f.prototype;
8836     p.fields = new Roo.util.MixedCollection(false, function(field){
8837         return field.name;
8838     });
8839     for(var i = 0, len = o.length; i < len; i++){
8840         p.fields.add(new Roo.data.Field(o[i]));
8841     }
8842     f.getField = function(name){
8843         return p.fields.get(name);  
8844     };
8845     return f;
8846 };
8847
8848 Roo.data.Record.AUTO_ID = 1000;
8849 Roo.data.Record.EDIT = 'edit';
8850 Roo.data.Record.REJECT = 'reject';
8851 Roo.data.Record.COMMIT = 'commit';
8852
8853 Roo.data.Record.prototype = {
8854     /**
8855      * Readonly flag - true if this record has been modified.
8856      * @type Boolean
8857      */
8858     dirty : false,
8859     editing : false,
8860     error: null,
8861     modified: null,
8862
8863     // private
8864     join : function(store){
8865         this.store = store;
8866     },
8867
8868     /**
8869      * Set the named field to the specified value.
8870      * @param {String} name The name of the field to set.
8871      * @param {Object} value The value to set the field to.
8872      */
8873     set : function(name, value){
8874         if(this.data[name] == value){
8875             return;
8876         }
8877         this.dirty = true;
8878         if(!this.modified){
8879             this.modified = {};
8880         }
8881         if(typeof this.modified[name] == 'undefined'){
8882             this.modified[name] = this.data[name];
8883         }
8884         this.data[name] = value;
8885         if(!this.editing && this.store){
8886             this.store.afterEdit(this);
8887         }       
8888     },
8889
8890     /**
8891      * Get the value of the named field.
8892      * @param {String} name The name of the field to get the value of.
8893      * @return {Object} The value of the field.
8894      */
8895     get : function(name){
8896         return this.data[name]; 
8897     },
8898
8899     // private
8900     beginEdit : function(){
8901         this.editing = true;
8902         this.modified = {}; 
8903     },
8904
8905     // private
8906     cancelEdit : function(){
8907         this.editing = false;
8908         delete this.modified;
8909     },
8910
8911     // private
8912     endEdit : function(){
8913         this.editing = false;
8914         if(this.dirty && this.store){
8915             this.store.afterEdit(this);
8916         }
8917     },
8918
8919     /**
8920      * Usually called by the {@link Roo.data.Store} which owns the Record.
8921      * Rejects all changes made to the Record since either creation, or the last commit operation.
8922      * Modified fields are reverted to their original values.
8923      * <p>
8924      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8925      * of reject operations.
8926      */
8927     reject : function(){
8928         var m = this.modified;
8929         for(var n in m){
8930             if(typeof m[n] != "function"){
8931                 this.data[n] = m[n];
8932             }
8933         }
8934         this.dirty = false;
8935         delete this.modified;
8936         this.editing = false;
8937         if(this.store){
8938             this.store.afterReject(this);
8939         }
8940     },
8941
8942     /**
8943      * Usually called by the {@link Roo.data.Store} which owns the Record.
8944      * Commits all changes made to the Record since either creation, or the last commit operation.
8945      * <p>
8946      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8947      * of commit operations.
8948      */
8949     commit : function(){
8950         this.dirty = false;
8951         delete this.modified;
8952         this.editing = false;
8953         if(this.store){
8954             this.store.afterCommit(this);
8955         }
8956     },
8957
8958     // private
8959     hasError : function(){
8960         return this.error != null;
8961     },
8962
8963     // private
8964     clearError : function(){
8965         this.error = null;
8966     },
8967
8968     /**
8969      * Creates a copy of this record.
8970      * @param {String} id (optional) A new record id if you don't want to use this record's id
8971      * @return {Record}
8972      */
8973     copy : function(newId) {
8974         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8975     }
8976 };/*
8977  * Based on:
8978  * Ext JS Library 1.1.1
8979  * Copyright(c) 2006-2007, Ext JS, LLC.
8980  *
8981  * Originally Released Under LGPL - original licence link has changed is not relivant.
8982  *
8983  * Fork - LGPL
8984  * <script type="text/javascript">
8985  */
8986
8987
8988
8989 /**
8990  * @class Roo.data.Store
8991  * @extends Roo.util.Observable
8992  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8993  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8994  * <p>
8995  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
8996  * has no knowledge of the format of the data returned by the Proxy.<br>
8997  * <p>
8998  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8999  * instances from the data object. These records are cached and made available through accessor functions.
9000  * @constructor
9001  * Creates a new Store.
9002  * @param {Object} config A config object containing the objects needed for the Store to access data,
9003  * and read the data into Records.
9004  */
9005 Roo.data.Store = function(config){
9006     this.data = new Roo.util.MixedCollection(false);
9007     this.data.getKey = function(o){
9008         return o.id;
9009     };
9010     this.baseParams = {};
9011     // private
9012     this.paramNames = {
9013         "start" : "start",
9014         "limit" : "limit",
9015         "sort" : "sort",
9016         "dir" : "dir",
9017         "multisort" : "_multisort"
9018     };
9019
9020     if(config && config.data){
9021         this.inlineData = config.data;
9022         delete config.data;
9023     }
9024
9025     Roo.apply(this, config);
9026     
9027     if(this.reader){ // reader passed
9028         this.reader = Roo.factory(this.reader, Roo.data);
9029         this.reader.xmodule = this.xmodule || false;
9030         if(!this.recordType){
9031             this.recordType = this.reader.recordType;
9032         }
9033         if(this.reader.onMetaChange){
9034             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9035         }
9036     }
9037
9038     if(this.recordType){
9039         this.fields = this.recordType.prototype.fields;
9040     }
9041     this.modified = [];
9042
9043     this.addEvents({
9044         /**
9045          * @event datachanged
9046          * Fires when the data cache has changed, and a widget which is using this Store
9047          * as a Record cache should refresh its view.
9048          * @param {Store} this
9049          */
9050         datachanged : true,
9051         /**
9052          * @event metachange
9053          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9054          * @param {Store} this
9055          * @param {Object} meta The JSON metadata
9056          */
9057         metachange : true,
9058         /**
9059          * @event add
9060          * Fires when Records have been added to the Store
9061          * @param {Store} this
9062          * @param {Roo.data.Record[]} records The array of Records added
9063          * @param {Number} index The index at which the record(s) were added
9064          */
9065         add : true,
9066         /**
9067          * @event remove
9068          * Fires when a Record has been removed from the Store
9069          * @param {Store} this
9070          * @param {Roo.data.Record} record The Record that was removed
9071          * @param {Number} index The index at which the record was removed
9072          */
9073         remove : true,
9074         /**
9075          * @event update
9076          * Fires when a Record has been updated
9077          * @param {Store} this
9078          * @param {Roo.data.Record} record The Record that was updated
9079          * @param {String} operation The update operation being performed.  Value may be one of:
9080          * <pre><code>
9081  Roo.data.Record.EDIT
9082  Roo.data.Record.REJECT
9083  Roo.data.Record.COMMIT
9084          * </code></pre>
9085          */
9086         update : true,
9087         /**
9088          * @event clear
9089          * Fires when the data cache has been cleared.
9090          * @param {Store} this
9091          */
9092         clear : true,
9093         /**
9094          * @event beforeload
9095          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9096          * the load action will be canceled.
9097          * @param {Store} this
9098          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9099          */
9100         beforeload : true,
9101         /**
9102          * @event beforeloadadd
9103          * Fires after a new set of Records has been loaded.
9104          * @param {Store} this
9105          * @param {Roo.data.Record[]} records The Records that were loaded
9106          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9107          */
9108         beforeloadadd : true,
9109         /**
9110          * @event load
9111          * Fires after a new set of Records has been loaded, before they are added to the store.
9112          * @param {Store} this
9113          * @param {Roo.data.Record[]} records The Records that were loaded
9114          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9115          * @params {Object} return from reader
9116          */
9117         load : true,
9118         /**
9119          * @event loadexception
9120          * Fires if an exception occurs in the Proxy during loading.
9121          * Called with the signature of the Proxy's "loadexception" event.
9122          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9123          * 
9124          * @param {Proxy} 
9125          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9126          * @param {Object} load options 
9127          * @param {Object} jsonData from your request (normally this contains the Exception)
9128          */
9129         loadexception : true
9130     });
9131     
9132     if(this.proxy){
9133         this.proxy = Roo.factory(this.proxy, Roo.data);
9134         this.proxy.xmodule = this.xmodule || false;
9135         this.relayEvents(this.proxy,  ["loadexception"]);
9136     }
9137     this.sortToggle = {};
9138     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9139
9140     Roo.data.Store.superclass.constructor.call(this);
9141
9142     if(this.inlineData){
9143         this.loadData(this.inlineData);
9144         delete this.inlineData;
9145     }
9146 };
9147
9148 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9149      /**
9150     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9151     * without a remote query - used by combo/forms at present.
9152     */
9153     
9154     /**
9155     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9156     */
9157     /**
9158     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9159     */
9160     /**
9161     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9162     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9163     */
9164     /**
9165     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9166     * on any HTTP request
9167     */
9168     /**
9169     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9170     */
9171     /**
9172     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9173     */
9174     multiSort: false,
9175     /**
9176     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9177     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9178     */
9179     remoteSort : false,
9180
9181     /**
9182     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9183      * loaded or when a record is removed. (defaults to false).
9184     */
9185     pruneModifiedRecords : false,
9186
9187     // private
9188     lastOptions : null,
9189
9190     /**
9191      * Add Records to the Store and fires the add event.
9192      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9193      */
9194     add : function(records){
9195         records = [].concat(records);
9196         for(var i = 0, len = records.length; i < len; i++){
9197             records[i].join(this);
9198         }
9199         var index = this.data.length;
9200         this.data.addAll(records);
9201         this.fireEvent("add", this, records, index);
9202     },
9203
9204     /**
9205      * Remove a Record from the Store and fires the remove event.
9206      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9207      */
9208     remove : function(record){
9209         var index = this.data.indexOf(record);
9210         this.data.removeAt(index);
9211         if(this.pruneModifiedRecords){
9212             this.modified.remove(record);
9213         }
9214         this.fireEvent("remove", this, record, index);
9215     },
9216
9217     /**
9218      * Remove all Records from the Store and fires the clear event.
9219      */
9220     removeAll : function(){
9221         this.data.clear();
9222         if(this.pruneModifiedRecords){
9223             this.modified = [];
9224         }
9225         this.fireEvent("clear", this);
9226     },
9227
9228     /**
9229      * Inserts Records to the Store at the given index and fires the add event.
9230      * @param {Number} index The start index at which to insert the passed Records.
9231      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9232      */
9233     insert : function(index, records){
9234         records = [].concat(records);
9235         for(var i = 0, len = records.length; i < len; i++){
9236             this.data.insert(index, records[i]);
9237             records[i].join(this);
9238         }
9239         this.fireEvent("add", this, records, index);
9240     },
9241
9242     /**
9243      * Get the index within the cache of the passed Record.
9244      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9245      * @return {Number} The index of the passed Record. Returns -1 if not found.
9246      */
9247     indexOf : function(record){
9248         return this.data.indexOf(record);
9249     },
9250
9251     /**
9252      * Get the index within the cache of the Record with the passed id.
9253      * @param {String} id The id of the Record to find.
9254      * @return {Number} The index of the Record. Returns -1 if not found.
9255      */
9256     indexOfId : function(id){
9257         return this.data.indexOfKey(id);
9258     },
9259
9260     /**
9261      * Get the Record with the specified id.
9262      * @param {String} id The id of the Record to find.
9263      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9264      */
9265     getById : function(id){
9266         return this.data.key(id);
9267     },
9268
9269     /**
9270      * Get the Record at the specified index.
9271      * @param {Number} index The index of the Record to find.
9272      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9273      */
9274     getAt : function(index){
9275         return this.data.itemAt(index);
9276     },
9277
9278     /**
9279      * Returns a range of Records between specified indices.
9280      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9281      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9282      * @return {Roo.data.Record[]} An array of Records
9283      */
9284     getRange : function(start, end){
9285         return this.data.getRange(start, end);
9286     },
9287
9288     // private
9289     storeOptions : function(o){
9290         o = Roo.apply({}, o);
9291         delete o.callback;
9292         delete o.scope;
9293         this.lastOptions = o;
9294     },
9295
9296     /**
9297      * Loads the Record cache from the configured Proxy using the configured Reader.
9298      * <p>
9299      * If using remote paging, then the first load call must specify the <em>start</em>
9300      * and <em>limit</em> properties in the options.params property to establish the initial
9301      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9302      * <p>
9303      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9304      * and this call will return before the new data has been loaded. Perform any post-processing
9305      * in a callback function, or in a "load" event handler.</strong>
9306      * <p>
9307      * @param {Object} options An object containing properties which control loading options:<ul>
9308      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9309      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9310      * passed the following arguments:<ul>
9311      * <li>r : Roo.data.Record[]</li>
9312      * <li>options: Options object from the load call</li>
9313      * <li>success: Boolean success indicator</li></ul></li>
9314      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9315      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9316      * </ul>
9317      */
9318     load : function(options){
9319         options = options || {};
9320         if(this.fireEvent("beforeload", this, options) !== false){
9321             this.storeOptions(options);
9322             var p = Roo.apply(options.params || {}, this.baseParams);
9323             // if meta was not loaded from remote source.. try requesting it.
9324             if (!this.reader.metaFromRemote) {
9325                 p._requestMeta = 1;
9326             }
9327             if(this.sortInfo && this.remoteSort){
9328                 var pn = this.paramNames;
9329                 p[pn["sort"]] = this.sortInfo.field;
9330                 p[pn["dir"]] = this.sortInfo.direction;
9331             }
9332             if (this.multiSort) {
9333                 var pn = this.paramNames;
9334                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9335             }
9336             
9337             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9338         }
9339     },
9340
9341     /**
9342      * Reloads the Record cache from the configured Proxy using the configured Reader and
9343      * the options from the last load operation performed.
9344      * @param {Object} options (optional) An object containing properties which may override the options
9345      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9346      * the most recently used options are reused).
9347      */
9348     reload : function(options){
9349         this.load(Roo.applyIf(options||{}, this.lastOptions));
9350     },
9351
9352     // private
9353     // Called as a callback by the Reader during a load operation.
9354     loadRecords : function(o, options, success){
9355         if(!o || success === false){
9356             if(success !== false){
9357                 this.fireEvent("load", this, [], options, o);
9358             }
9359             if(options.callback){
9360                 options.callback.call(options.scope || this, [], options, false);
9361             }
9362             return;
9363         }
9364         // if data returned failure - throw an exception.
9365         if (o.success === false) {
9366             // show a message if no listener is registered.
9367             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9368                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9369             }
9370             // loadmask wil be hooked into this..
9371             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9372             return;
9373         }
9374         var r = o.records, t = o.totalRecords || r.length;
9375         
9376         this.fireEvent("beforeloadadd", this, r, options, o);
9377         
9378         if(!options || options.add !== true){
9379             if(this.pruneModifiedRecords){
9380                 this.modified = [];
9381             }
9382             for(var i = 0, len = r.length; i < len; i++){
9383                 r[i].join(this);
9384             }
9385             if(this.snapshot){
9386                 this.data = this.snapshot;
9387                 delete this.snapshot;
9388             }
9389             this.data.clear();
9390             this.data.addAll(r);
9391             this.totalLength = t;
9392             this.applySort();
9393             this.fireEvent("datachanged", this);
9394         }else{
9395             this.totalLength = Math.max(t, this.data.length+r.length);
9396             this.add(r);
9397         }
9398         this.fireEvent("load", this, r, options, o);
9399         if(options.callback){
9400             options.callback.call(options.scope || this, r, options, true);
9401         }
9402     },
9403
9404
9405     /**
9406      * Loads data from a passed data block. A Reader which understands the format of the data
9407      * must have been configured in the constructor.
9408      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9409      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9410      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9411      */
9412     loadData : function(o, append){
9413         var r = this.reader.readRecords(o);
9414         this.loadRecords(r, {add: append}, true);
9415     },
9416
9417     /**
9418      * Gets the number of cached records.
9419      * <p>
9420      * <em>If using paging, this may not be the total size of the dataset. If the data object
9421      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9422      * the data set size</em>
9423      */
9424     getCount : function(){
9425         return this.data.length || 0;
9426     },
9427
9428     /**
9429      * Gets the total number of records in the dataset as returned by the server.
9430      * <p>
9431      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9432      * the dataset size</em>
9433      */
9434     getTotalCount : function(){
9435         return this.totalLength || 0;
9436     },
9437
9438     /**
9439      * Returns the sort state of the Store as an object with two properties:
9440      * <pre><code>
9441  field {String} The name of the field by which the Records are sorted
9442  direction {String} The sort order, "ASC" or "DESC"
9443      * </code></pre>
9444      */
9445     getSortState : function(){
9446         return this.sortInfo;
9447     },
9448
9449     // private
9450     applySort : function(){
9451         if(this.sortInfo && !this.remoteSort){
9452             var s = this.sortInfo, f = s.field;
9453             var st = this.fields.get(f).sortType;
9454             var fn = function(r1, r2){
9455                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9456                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9457             };
9458             this.data.sort(s.direction, fn);
9459             if(this.snapshot && this.snapshot != this.data){
9460                 this.snapshot.sort(s.direction, fn);
9461             }
9462         }
9463     },
9464
9465     /**
9466      * Sets the default sort column and order to be used by the next load operation.
9467      * @param {String} fieldName The name of the field to sort by.
9468      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9469      */
9470     setDefaultSort : function(field, dir){
9471         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9472     },
9473
9474     /**
9475      * Sort the Records.
9476      * If remote sorting is used, the sort is performed on the server, and the cache is
9477      * reloaded. If local sorting is used, the cache is sorted internally.
9478      * @param {String} fieldName The name of the field to sort by.
9479      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9480      */
9481     sort : function(fieldName, dir){
9482         var f = this.fields.get(fieldName);
9483         if(!dir){
9484             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9485             
9486             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9487                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9488             }else{
9489                 dir = f.sortDir;
9490             }
9491         }
9492         this.sortToggle[f.name] = dir;
9493         this.sortInfo = {field: f.name, direction: dir};
9494         if(!this.remoteSort){
9495             this.applySort();
9496             this.fireEvent("datachanged", this);
9497         }else{
9498             this.load(this.lastOptions);
9499         }
9500     },
9501
9502     /**
9503      * Calls the specified function for each of the Records in the cache.
9504      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9505      * Returning <em>false</em> aborts and exits the iteration.
9506      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9507      */
9508     each : function(fn, scope){
9509         this.data.each(fn, scope);
9510     },
9511
9512     /**
9513      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9514      * (e.g., during paging).
9515      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9516      */
9517     getModifiedRecords : function(){
9518         return this.modified;
9519     },
9520
9521     // private
9522     createFilterFn : function(property, value, anyMatch){
9523         if(!value.exec){ // not a regex
9524             value = String(value);
9525             if(value.length == 0){
9526                 return false;
9527             }
9528             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9529         }
9530         return function(r){
9531             return value.test(r.data[property]);
9532         };
9533     },
9534
9535     /**
9536      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9537      * @param {String} property A field on your records
9538      * @param {Number} start The record index to start at (defaults to 0)
9539      * @param {Number} end The last record index to include (defaults to length - 1)
9540      * @return {Number} The sum
9541      */
9542     sum : function(property, start, end){
9543         var rs = this.data.items, v = 0;
9544         start = start || 0;
9545         end = (end || end === 0) ? end : rs.length-1;
9546
9547         for(var i = start; i <= end; i++){
9548             v += (rs[i].data[property] || 0);
9549         }
9550         return v;
9551     },
9552
9553     /**
9554      * Filter the records by a specified property.
9555      * @param {String} field A field on your records
9556      * @param {String/RegExp} value Either a string that the field
9557      * should start with or a RegExp to test against the field
9558      * @param {Boolean} anyMatch True to match any part not just the beginning
9559      */
9560     filter : function(property, value, anyMatch){
9561         var fn = this.createFilterFn(property, value, anyMatch);
9562         return fn ? this.filterBy(fn) : this.clearFilter();
9563     },
9564
9565     /**
9566      * Filter by a function. The specified function will be called with each
9567      * record in this data source. If the function returns true the record is included,
9568      * otherwise it is filtered.
9569      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9570      * @param {Object} scope (optional) The scope of the function (defaults to this)
9571      */
9572     filterBy : function(fn, scope){
9573         this.snapshot = this.snapshot || this.data;
9574         this.data = this.queryBy(fn, scope||this);
9575         this.fireEvent("datachanged", this);
9576     },
9577
9578     /**
9579      * Query the records by a specified property.
9580      * @param {String} field A field on your records
9581      * @param {String/RegExp} value Either a string that the field
9582      * should start with or a RegExp to test against the field
9583      * @param {Boolean} anyMatch True to match any part not just the beginning
9584      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9585      */
9586     query : function(property, value, anyMatch){
9587         var fn = this.createFilterFn(property, value, anyMatch);
9588         return fn ? this.queryBy(fn) : this.data.clone();
9589     },
9590
9591     /**
9592      * Query by a function. The specified function will be called with each
9593      * record in this data source. If the function returns true the record is included
9594      * in the results.
9595      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9596      * @param {Object} scope (optional) The scope of the function (defaults to this)
9597       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9598      **/
9599     queryBy : function(fn, scope){
9600         var data = this.snapshot || this.data;
9601         return data.filterBy(fn, scope||this);
9602     },
9603
9604     /**
9605      * Collects unique values for a particular dataIndex from this store.
9606      * @param {String} dataIndex The property to collect
9607      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9608      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9609      * @return {Array} An array of the unique values
9610      **/
9611     collect : function(dataIndex, allowNull, bypassFilter){
9612         var d = (bypassFilter === true && this.snapshot) ?
9613                 this.snapshot.items : this.data.items;
9614         var v, sv, r = [], l = {};
9615         for(var i = 0, len = d.length; i < len; i++){
9616             v = d[i].data[dataIndex];
9617             sv = String(v);
9618             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9619                 l[sv] = true;
9620                 r[r.length] = v;
9621             }
9622         }
9623         return r;
9624     },
9625
9626     /**
9627      * Revert to a view of the Record cache with no filtering applied.
9628      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9629      */
9630     clearFilter : function(suppressEvent){
9631         if(this.snapshot && this.snapshot != this.data){
9632             this.data = this.snapshot;
9633             delete this.snapshot;
9634             if(suppressEvent !== true){
9635                 this.fireEvent("datachanged", this);
9636             }
9637         }
9638     },
9639
9640     // private
9641     afterEdit : function(record){
9642         if(this.modified.indexOf(record) == -1){
9643             this.modified.push(record);
9644         }
9645         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9646     },
9647     
9648     // private
9649     afterReject : function(record){
9650         this.modified.remove(record);
9651         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9652     },
9653
9654     // private
9655     afterCommit : function(record){
9656         this.modified.remove(record);
9657         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9658     },
9659
9660     /**
9661      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9662      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9663      */
9664     commitChanges : function(){
9665         var m = this.modified.slice(0);
9666         this.modified = [];
9667         for(var i = 0, len = m.length; i < len; i++){
9668             m[i].commit();
9669         }
9670     },
9671
9672     /**
9673      * Cancel outstanding changes on all changed records.
9674      */
9675     rejectChanges : function(){
9676         var m = this.modified.slice(0);
9677         this.modified = [];
9678         for(var i = 0, len = m.length; i < len; i++){
9679             m[i].reject();
9680         }
9681     },
9682
9683     onMetaChange : function(meta, rtype, o){
9684         this.recordType = rtype;
9685         this.fields = rtype.prototype.fields;
9686         delete this.snapshot;
9687         this.sortInfo = meta.sortInfo || this.sortInfo;
9688         this.modified = [];
9689         this.fireEvent('metachange', this, this.reader.meta);
9690     },
9691     
9692     moveIndex : function(data, type)
9693     {
9694         var index = this.indexOf(data);
9695         
9696         var newIndex = index + type;
9697         
9698         this.remove(data);
9699         
9700         this.insert(newIndex, data);
9701         
9702     }
9703 });/*
9704  * Based on:
9705  * Ext JS Library 1.1.1
9706  * Copyright(c) 2006-2007, Ext JS, LLC.
9707  *
9708  * Originally Released Under LGPL - original licence link has changed is not relivant.
9709  *
9710  * Fork - LGPL
9711  * <script type="text/javascript">
9712  */
9713
9714 /**
9715  * @class Roo.data.SimpleStore
9716  * @extends Roo.data.Store
9717  * Small helper class to make creating Stores from Array data easier.
9718  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9719  * @cfg {Array} fields An array of field definition objects, or field name strings.
9720  * @cfg {Array} data The multi-dimensional array of data
9721  * @constructor
9722  * @param {Object} config
9723  */
9724 Roo.data.SimpleStore = function(config){
9725     Roo.data.SimpleStore.superclass.constructor.call(this, {
9726         isLocal : true,
9727         reader: new Roo.data.ArrayReader({
9728                 id: config.id
9729             },
9730             Roo.data.Record.create(config.fields)
9731         ),
9732         proxy : new Roo.data.MemoryProxy(config.data)
9733     });
9734     this.load();
9735 };
9736 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9737  * Based on:
9738  * Ext JS Library 1.1.1
9739  * Copyright(c) 2006-2007, Ext JS, LLC.
9740  *
9741  * Originally Released Under LGPL - original licence link has changed is not relivant.
9742  *
9743  * Fork - LGPL
9744  * <script type="text/javascript">
9745  */
9746
9747 /**
9748 /**
9749  * @extends Roo.data.Store
9750  * @class Roo.data.JsonStore
9751  * Small helper class to make creating Stores for JSON data easier. <br/>
9752 <pre><code>
9753 var store = new Roo.data.JsonStore({
9754     url: 'get-images.php',
9755     root: 'images',
9756     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9757 });
9758 </code></pre>
9759  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9760  * JsonReader and HttpProxy (unless inline data is provided).</b>
9761  * @cfg {Array} fields An array of field definition objects, or field name strings.
9762  * @constructor
9763  * @param {Object} config
9764  */
9765 Roo.data.JsonStore = function(c){
9766     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9767         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9768         reader: new Roo.data.JsonReader(c, c.fields)
9769     }));
9770 };
9771 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9772  * Based on:
9773  * Ext JS Library 1.1.1
9774  * Copyright(c) 2006-2007, Ext JS, LLC.
9775  *
9776  * Originally Released Under LGPL - original licence link has changed is not relivant.
9777  *
9778  * Fork - LGPL
9779  * <script type="text/javascript">
9780  */
9781
9782  
9783 Roo.data.Field = function(config){
9784     if(typeof config == "string"){
9785         config = {name: config};
9786     }
9787     Roo.apply(this, config);
9788     
9789     if(!this.type){
9790         this.type = "auto";
9791     }
9792     
9793     var st = Roo.data.SortTypes;
9794     // named sortTypes are supported, here we look them up
9795     if(typeof this.sortType == "string"){
9796         this.sortType = st[this.sortType];
9797     }
9798     
9799     // set default sortType for strings and dates
9800     if(!this.sortType){
9801         switch(this.type){
9802             case "string":
9803                 this.sortType = st.asUCString;
9804                 break;
9805             case "date":
9806                 this.sortType = st.asDate;
9807                 break;
9808             default:
9809                 this.sortType = st.none;
9810         }
9811     }
9812
9813     // define once
9814     var stripRe = /[\$,%]/g;
9815
9816     // prebuilt conversion function for this field, instead of
9817     // switching every time we're reading a value
9818     if(!this.convert){
9819         var cv, dateFormat = this.dateFormat;
9820         switch(this.type){
9821             case "":
9822             case "auto":
9823             case undefined:
9824                 cv = function(v){ return v; };
9825                 break;
9826             case "string":
9827                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9828                 break;
9829             case "int":
9830                 cv = function(v){
9831                     return v !== undefined && v !== null && v !== '' ?
9832                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9833                     };
9834                 break;
9835             case "float":
9836                 cv = function(v){
9837                     return v !== undefined && v !== null && v !== '' ?
9838                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9839                     };
9840                 break;
9841             case "bool":
9842             case "boolean":
9843                 cv = function(v){ return v === true || v === "true" || v == 1; };
9844                 break;
9845             case "date":
9846                 cv = function(v){
9847                     if(!v){
9848                         return '';
9849                     }
9850                     if(v instanceof Date){
9851                         return v;
9852                     }
9853                     if(dateFormat){
9854                         if(dateFormat == "timestamp"){
9855                             return new Date(v*1000);
9856                         }
9857                         return Date.parseDate(v, dateFormat);
9858                     }
9859                     var parsed = Date.parse(v);
9860                     return parsed ? new Date(parsed) : null;
9861                 };
9862              break;
9863             
9864         }
9865         this.convert = cv;
9866     }
9867 };
9868
9869 Roo.data.Field.prototype = {
9870     dateFormat: null,
9871     defaultValue: "",
9872     mapping: null,
9873     sortType : null,
9874     sortDir : "ASC"
9875 };/*
9876  * Based on:
9877  * Ext JS Library 1.1.1
9878  * Copyright(c) 2006-2007, Ext JS, LLC.
9879  *
9880  * Originally Released Under LGPL - original licence link has changed is not relivant.
9881  *
9882  * Fork - LGPL
9883  * <script type="text/javascript">
9884  */
9885  
9886 // Base class for reading structured data from a data source.  This class is intended to be
9887 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9888
9889 /**
9890  * @class Roo.data.DataReader
9891  * Base class for reading structured data from a data source.  This class is intended to be
9892  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9893  */
9894
9895 Roo.data.DataReader = function(meta, recordType){
9896     
9897     this.meta = meta;
9898     
9899     this.recordType = recordType instanceof Array ? 
9900         Roo.data.Record.create(recordType) : recordType;
9901 };
9902
9903 Roo.data.DataReader.prototype = {
9904      /**
9905      * Create an empty record
9906      * @param {Object} data (optional) - overlay some values
9907      * @return {Roo.data.Record} record created.
9908      */
9909     newRow :  function(d) {
9910         var da =  {};
9911         this.recordType.prototype.fields.each(function(c) {
9912             switch( c.type) {
9913                 case 'int' : da[c.name] = 0; break;
9914                 case 'date' : da[c.name] = new Date(); break;
9915                 case 'float' : da[c.name] = 0.0; break;
9916                 case 'boolean' : da[c.name] = false; break;
9917                 default : da[c.name] = ""; break;
9918             }
9919             
9920         });
9921         return new this.recordType(Roo.apply(da, d));
9922     }
9923     
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 /**
9936  * @class Roo.data.DataProxy
9937  * @extends Roo.data.Observable
9938  * This class is an abstract base class for implementations which provide retrieval of
9939  * unformatted data objects.<br>
9940  * <p>
9941  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9942  * (of the appropriate type which knows how to parse the data object) to provide a block of
9943  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9944  * <p>
9945  * Custom implementations must implement the load method as described in
9946  * {@link Roo.data.HttpProxy#load}.
9947  */
9948 Roo.data.DataProxy = function(){
9949     this.addEvents({
9950         /**
9951          * @event beforeload
9952          * Fires before a network request is made to retrieve a data object.
9953          * @param {Object} This DataProxy object.
9954          * @param {Object} params The params parameter to the load function.
9955          */
9956         beforeload : true,
9957         /**
9958          * @event load
9959          * Fires before the load method's callback is called.
9960          * @param {Object} This DataProxy object.
9961          * @param {Object} o The data object.
9962          * @param {Object} arg The callback argument object passed to the load function.
9963          */
9964         load : true,
9965         /**
9966          * @event loadexception
9967          * Fires if an Exception occurs during data retrieval.
9968          * @param {Object} This DataProxy object.
9969          * @param {Object} o The data object.
9970          * @param {Object} arg The callback argument object passed to the load function.
9971          * @param {Object} e The Exception.
9972          */
9973         loadexception : true
9974     });
9975     Roo.data.DataProxy.superclass.constructor.call(this);
9976 };
9977
9978 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9979
9980     /**
9981      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9982      */
9983 /*
9984  * Based on:
9985  * Ext JS Library 1.1.1
9986  * Copyright(c) 2006-2007, Ext JS, LLC.
9987  *
9988  * Originally Released Under LGPL - original licence link has changed is not relivant.
9989  *
9990  * Fork - LGPL
9991  * <script type="text/javascript">
9992  */
9993 /**
9994  * @class Roo.data.MemoryProxy
9995  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9996  * to the Reader when its load method is called.
9997  * @constructor
9998  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9999  */
10000 Roo.data.MemoryProxy = function(data){
10001     if (data.data) {
10002         data = data.data;
10003     }
10004     Roo.data.MemoryProxy.superclass.constructor.call(this);
10005     this.data = data;
10006 };
10007
10008 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10009     /**
10010      * Load data from the requested source (in this case an in-memory
10011      * data object passed to the constructor), read the data object into
10012      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10013      * process that block using the passed callback.
10014      * @param {Object} params This parameter is not used by the MemoryProxy class.
10015      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10016      * object into a block of Roo.data.Records.
10017      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10018      * The function must be passed <ul>
10019      * <li>The Record block object</li>
10020      * <li>The "arg" argument from the load function</li>
10021      * <li>A boolean success indicator</li>
10022      * </ul>
10023      * @param {Object} scope The scope in which to call the callback
10024      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10025      */
10026     load : function(params, reader, callback, scope, arg){
10027         params = params || {};
10028         var result;
10029         try {
10030             result = reader.readRecords(this.data);
10031         }catch(e){
10032             this.fireEvent("loadexception", this, arg, null, e);
10033             callback.call(scope, null, arg, false);
10034             return;
10035         }
10036         callback.call(scope, result, arg, true);
10037     },
10038     
10039     // private
10040     update : function(params, records){
10041         
10042     }
10043 });/*
10044  * Based on:
10045  * Ext JS Library 1.1.1
10046  * Copyright(c) 2006-2007, Ext JS, LLC.
10047  *
10048  * Originally Released Under LGPL - original licence link has changed is not relivant.
10049  *
10050  * Fork - LGPL
10051  * <script type="text/javascript">
10052  */
10053 /**
10054  * @class Roo.data.HttpProxy
10055  * @extends Roo.data.DataProxy
10056  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10057  * configured to reference a certain URL.<br><br>
10058  * <p>
10059  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10060  * from which the running page was served.<br><br>
10061  * <p>
10062  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10063  * <p>
10064  * Be aware that to enable the browser to parse an XML document, the server must set
10065  * the Content-Type header in the HTTP response to "text/xml".
10066  * @constructor
10067  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10068  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10069  * will be used to make the request.
10070  */
10071 Roo.data.HttpProxy = function(conn){
10072     Roo.data.HttpProxy.superclass.constructor.call(this);
10073     // is conn a conn config or a real conn?
10074     this.conn = conn;
10075     this.useAjax = !conn || !conn.events;
10076   
10077 };
10078
10079 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10080     // thse are take from connection...
10081     
10082     /**
10083      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10084      */
10085     /**
10086      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10087      * extra parameters to each request made by this object. (defaults to undefined)
10088      */
10089     /**
10090      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10091      *  to each request made by this object. (defaults to undefined)
10092      */
10093     /**
10094      * @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)
10095      */
10096     /**
10097      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10098      */
10099      /**
10100      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10101      * @type Boolean
10102      */
10103   
10104
10105     /**
10106      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10107      * @type Boolean
10108      */
10109     /**
10110      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10111      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10112      * a finer-grained basis than the DataProxy events.
10113      */
10114     getConnection : function(){
10115         return this.useAjax ? Roo.Ajax : this.conn;
10116     },
10117
10118     /**
10119      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10120      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10121      * process that block using the passed callback.
10122      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10123      * for the request to the remote server.
10124      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10125      * object into a block of Roo.data.Records.
10126      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10127      * The function must be passed <ul>
10128      * <li>The Record block object</li>
10129      * <li>The "arg" argument from the load function</li>
10130      * <li>A boolean success indicator</li>
10131      * </ul>
10132      * @param {Object} scope The scope in which to call the callback
10133      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10134      */
10135     load : function(params, reader, callback, scope, arg){
10136         if(this.fireEvent("beforeload", this, params) !== false){
10137             var  o = {
10138                 params : params || {},
10139                 request: {
10140                     callback : callback,
10141                     scope : scope,
10142                     arg : arg
10143                 },
10144                 reader: reader,
10145                 callback : this.loadResponse,
10146                 scope: this
10147             };
10148             if(this.useAjax){
10149                 Roo.applyIf(o, this.conn);
10150                 if(this.activeRequest){
10151                     Roo.Ajax.abort(this.activeRequest);
10152                 }
10153                 this.activeRequest = Roo.Ajax.request(o);
10154             }else{
10155                 this.conn.request(o);
10156             }
10157         }else{
10158             callback.call(scope||this, null, arg, false);
10159         }
10160     },
10161
10162     // private
10163     loadResponse : function(o, success, response){
10164         delete this.activeRequest;
10165         if(!success){
10166             this.fireEvent("loadexception", this, o, response);
10167             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10168             return;
10169         }
10170         var result;
10171         try {
10172             result = o.reader.read(response);
10173         }catch(e){
10174             this.fireEvent("loadexception", this, o, response, e);
10175             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10176             return;
10177         }
10178         
10179         this.fireEvent("load", this, o, o.request.arg);
10180         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10181     },
10182
10183     // private
10184     update : function(dataSet){
10185
10186     },
10187
10188     // private
10189     updateResponse : function(dataSet){
10190
10191     }
10192 });/*
10193  * Based on:
10194  * Ext JS Library 1.1.1
10195  * Copyright(c) 2006-2007, Ext JS, LLC.
10196  *
10197  * Originally Released Under LGPL - original licence link has changed is not relivant.
10198  *
10199  * Fork - LGPL
10200  * <script type="text/javascript">
10201  */
10202
10203 /**
10204  * @class Roo.data.ScriptTagProxy
10205  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10206  * other than the originating domain of the running page.<br><br>
10207  * <p>
10208  * <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
10209  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10210  * <p>
10211  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10212  * source code that is used as the source inside a &lt;script> tag.<br><br>
10213  * <p>
10214  * In order for the browser to process the returned data, the server must wrap the data object
10215  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10216  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10217  * depending on whether the callback name was passed:
10218  * <p>
10219  * <pre><code>
10220 boolean scriptTag = false;
10221 String cb = request.getParameter("callback");
10222 if (cb != null) {
10223     scriptTag = true;
10224     response.setContentType("text/javascript");
10225 } else {
10226     response.setContentType("application/x-json");
10227 }
10228 Writer out = response.getWriter();
10229 if (scriptTag) {
10230     out.write(cb + "(");
10231 }
10232 out.print(dataBlock.toJsonString());
10233 if (scriptTag) {
10234     out.write(");");
10235 }
10236 </pre></code>
10237  *
10238  * @constructor
10239  * @param {Object} config A configuration object.
10240  */
10241 Roo.data.ScriptTagProxy = function(config){
10242     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10243     Roo.apply(this, config);
10244     this.head = document.getElementsByTagName("head")[0];
10245 };
10246
10247 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10248
10249 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10250     /**
10251      * @cfg {String} url The URL from which to request the data object.
10252      */
10253     /**
10254      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10255      */
10256     timeout : 30000,
10257     /**
10258      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10259      * the server the name of the callback function set up by the load call to process the returned data object.
10260      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10261      * javascript output which calls this named function passing the data object as its only parameter.
10262      */
10263     callbackParam : "callback",
10264     /**
10265      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10266      * name to the request.
10267      */
10268     nocache : true,
10269
10270     /**
10271      * Load data from the configured URL, read the data object into
10272      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10273      * process that block using the passed callback.
10274      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10275      * for the request to the remote server.
10276      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10277      * object into a block of Roo.data.Records.
10278      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10279      * The function must be passed <ul>
10280      * <li>The Record block object</li>
10281      * <li>The "arg" argument from the load function</li>
10282      * <li>A boolean success indicator</li>
10283      * </ul>
10284      * @param {Object} scope The scope in which to call the callback
10285      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10286      */
10287     load : function(params, reader, callback, scope, arg){
10288         if(this.fireEvent("beforeload", this, params) !== false){
10289
10290             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10291
10292             var url = this.url;
10293             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10294             if(this.nocache){
10295                 url += "&_dc=" + (new Date().getTime());
10296             }
10297             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10298             var trans = {
10299                 id : transId,
10300                 cb : "stcCallback"+transId,
10301                 scriptId : "stcScript"+transId,
10302                 params : params,
10303                 arg : arg,
10304                 url : url,
10305                 callback : callback,
10306                 scope : scope,
10307                 reader : reader
10308             };
10309             var conn = this;
10310
10311             window[trans.cb] = function(o){
10312                 conn.handleResponse(o, trans);
10313             };
10314
10315             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10316
10317             if(this.autoAbort !== false){
10318                 this.abort();
10319             }
10320
10321             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10322
10323             var script = document.createElement("script");
10324             script.setAttribute("src", url);
10325             script.setAttribute("type", "text/javascript");
10326             script.setAttribute("id", trans.scriptId);
10327             this.head.appendChild(script);
10328
10329             this.trans = trans;
10330         }else{
10331             callback.call(scope||this, null, arg, false);
10332         }
10333     },
10334
10335     // private
10336     isLoading : function(){
10337         return this.trans ? true : false;
10338     },
10339
10340     /**
10341      * Abort the current server request.
10342      */
10343     abort : function(){
10344         if(this.isLoading()){
10345             this.destroyTrans(this.trans);
10346         }
10347     },
10348
10349     // private
10350     destroyTrans : function(trans, isLoaded){
10351         this.head.removeChild(document.getElementById(trans.scriptId));
10352         clearTimeout(trans.timeoutId);
10353         if(isLoaded){
10354             window[trans.cb] = undefined;
10355             try{
10356                 delete window[trans.cb];
10357             }catch(e){}
10358         }else{
10359             // if hasn't been loaded, wait for load to remove it to prevent script error
10360             window[trans.cb] = function(){
10361                 window[trans.cb] = undefined;
10362                 try{
10363                     delete window[trans.cb];
10364                 }catch(e){}
10365             };
10366         }
10367     },
10368
10369     // private
10370     handleResponse : function(o, trans){
10371         this.trans = false;
10372         this.destroyTrans(trans, true);
10373         var result;
10374         try {
10375             result = trans.reader.readRecords(o);
10376         }catch(e){
10377             this.fireEvent("loadexception", this, o, trans.arg, e);
10378             trans.callback.call(trans.scope||window, null, trans.arg, false);
10379             return;
10380         }
10381         this.fireEvent("load", this, o, trans.arg);
10382         trans.callback.call(trans.scope||window, result, trans.arg, true);
10383     },
10384
10385     // private
10386     handleFailure : function(trans){
10387         this.trans = false;
10388         this.destroyTrans(trans, false);
10389         this.fireEvent("loadexception", this, null, trans.arg);
10390         trans.callback.call(trans.scope||window, null, trans.arg, false);
10391     }
10392 });/*
10393  * Based on:
10394  * Ext JS Library 1.1.1
10395  * Copyright(c) 2006-2007, Ext JS, LLC.
10396  *
10397  * Originally Released Under LGPL - original licence link has changed is not relivant.
10398  *
10399  * Fork - LGPL
10400  * <script type="text/javascript">
10401  */
10402
10403 /**
10404  * @class Roo.data.JsonReader
10405  * @extends Roo.data.DataReader
10406  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10407  * based on mappings in a provided Roo.data.Record constructor.
10408  * 
10409  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10410  * in the reply previously. 
10411  * 
10412  * <p>
10413  * Example code:
10414  * <pre><code>
10415 var RecordDef = Roo.data.Record.create([
10416     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10417     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10418 ]);
10419 var myReader = new Roo.data.JsonReader({
10420     totalProperty: "results",    // The property which contains the total dataset size (optional)
10421     root: "rows",                // The property which contains an Array of row objects
10422     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10423 }, RecordDef);
10424 </code></pre>
10425  * <p>
10426  * This would consume a JSON file like this:
10427  * <pre><code>
10428 { 'results': 2, 'rows': [
10429     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10430     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10431 }
10432 </code></pre>
10433  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10434  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10435  * paged from the remote server.
10436  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10437  * @cfg {String} root name of the property which contains the Array of row objects.
10438  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10439  * @constructor
10440  * Create a new JsonReader
10441  * @param {Object} meta Metadata configuration options
10442  * @param {Object} recordType Either an Array of field definition objects,
10443  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10444  */
10445 Roo.data.JsonReader = function(meta, recordType){
10446     
10447     meta = meta || {};
10448     // set some defaults:
10449     Roo.applyIf(meta, {
10450         totalProperty: 'total',
10451         successProperty : 'success',
10452         root : 'data',
10453         id : 'id'
10454     });
10455     
10456     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10457 };
10458 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10459     
10460     /**
10461      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10462      * Used by Store query builder to append _requestMeta to params.
10463      * 
10464      */
10465     metaFromRemote : false,
10466     /**
10467      * This method is only used by a DataProxy which has retrieved data from a remote server.
10468      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10469      * @return {Object} data A data block which is used by an Roo.data.Store object as
10470      * a cache of Roo.data.Records.
10471      */
10472     read : function(response){
10473         var json = response.responseText;
10474        
10475         var o = /* eval:var:o */ eval("("+json+")");
10476         if(!o) {
10477             throw {message: "JsonReader.read: Json object not found"};
10478         }
10479         
10480         if(o.metaData){
10481             
10482             delete this.ef;
10483             this.metaFromRemote = true;
10484             this.meta = o.metaData;
10485             this.recordType = Roo.data.Record.create(o.metaData.fields);
10486             this.onMetaChange(this.meta, this.recordType, o);
10487         }
10488         return this.readRecords(o);
10489     },
10490
10491     // private function a store will implement
10492     onMetaChange : function(meta, recordType, o){
10493
10494     },
10495
10496     /**
10497          * @ignore
10498          */
10499     simpleAccess: function(obj, subsc) {
10500         return obj[subsc];
10501     },
10502
10503         /**
10504          * @ignore
10505          */
10506     getJsonAccessor: function(){
10507         var re = /[\[\.]/;
10508         return function(expr) {
10509             try {
10510                 return(re.test(expr))
10511                     ? new Function("obj", "return obj." + expr)
10512                     : function(obj){
10513                         return obj[expr];
10514                     };
10515             } catch(e){}
10516             return Roo.emptyFn;
10517         };
10518     }(),
10519
10520     /**
10521      * Create a data block containing Roo.data.Records from an XML document.
10522      * @param {Object} o An object which contains an Array of row objects in the property specified
10523      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10524      * which contains the total size of the dataset.
10525      * @return {Object} data A data block which is used by an Roo.data.Store object as
10526      * a cache of Roo.data.Records.
10527      */
10528     readRecords : function(o){
10529         /**
10530          * After any data loads, the raw JSON data is available for further custom processing.
10531          * @type Object
10532          */
10533         this.o = o;
10534         var s = this.meta, Record = this.recordType,
10535             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10536
10537 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10538         if (!this.ef) {
10539             if(s.totalProperty) {
10540                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10541                 }
10542                 if(s.successProperty) {
10543                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10544                 }
10545                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10546                 if (s.id) {
10547                         var g = this.getJsonAccessor(s.id);
10548                         this.getId = function(rec) {
10549                                 var r = g(rec);  
10550                                 return (r === undefined || r === "") ? null : r;
10551                         };
10552                 } else {
10553                         this.getId = function(){return null;};
10554                 }
10555             this.ef = [];
10556             for(var jj = 0; jj < fl; jj++){
10557                 f = fi[jj];
10558                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10559                 this.ef[jj] = this.getJsonAccessor(map);
10560             }
10561         }
10562
10563         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10564         if(s.totalProperty){
10565             var vt = parseInt(this.getTotal(o), 10);
10566             if(!isNaN(vt)){
10567                 totalRecords = vt;
10568             }
10569         }
10570         if(s.successProperty){
10571             var vs = this.getSuccess(o);
10572             if(vs === false || vs === 'false'){
10573                 success = false;
10574             }
10575         }
10576         var records = [];
10577         for(var i = 0; i < c; i++){
10578                 var n = root[i];
10579             var values = {};
10580             var id = this.getId(n);
10581             for(var j = 0; j < fl; j++){
10582                 f = fi[j];
10583             var v = this.ef[j](n);
10584             if (!f.convert) {
10585                 Roo.log('missing convert for ' + f.name);
10586                 Roo.log(f);
10587                 continue;
10588             }
10589             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10590             }
10591             var record = new Record(values, id);
10592             record.json = n;
10593             records[i] = record;
10594         }
10595         return {
10596             raw : o,
10597             success : success,
10598             records : records,
10599             totalRecords : totalRecords
10600         };
10601     }
10602 });/*
10603  * Based on:
10604  * Ext JS Library 1.1.1
10605  * Copyright(c) 2006-2007, Ext JS, LLC.
10606  *
10607  * Originally Released Under LGPL - original licence link has changed is not relivant.
10608  *
10609  * Fork - LGPL
10610  * <script type="text/javascript">
10611  */
10612
10613 /**
10614  * @class Roo.data.ArrayReader
10615  * @extends Roo.data.DataReader
10616  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10617  * Each element of that Array represents a row of data fields. The
10618  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10619  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10620  * <p>
10621  * Example code:.
10622  * <pre><code>
10623 var RecordDef = Roo.data.Record.create([
10624     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10625     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10626 ]);
10627 var myReader = new Roo.data.ArrayReader({
10628     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10629 }, RecordDef);
10630 </code></pre>
10631  * <p>
10632  * This would consume an Array like this:
10633  * <pre><code>
10634 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10635   </code></pre>
10636  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10637  * @constructor
10638  * Create a new JsonReader
10639  * @param {Object} meta Metadata configuration options.
10640  * @param {Object} recordType Either an Array of field definition objects
10641  * as specified to {@link Roo.data.Record#create},
10642  * or an {@link Roo.data.Record} object
10643  * created using {@link Roo.data.Record#create}.
10644  */
10645 Roo.data.ArrayReader = function(meta, recordType){
10646     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10647 };
10648
10649 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10650     /**
10651      * Create a data block containing Roo.data.Records from an XML document.
10652      * @param {Object} o An Array of row objects which represents the dataset.
10653      * @return {Object} data A data block which is used by an Roo.data.Store object as
10654      * a cache of Roo.data.Records.
10655      */
10656     readRecords : function(o){
10657         var sid = this.meta ? this.meta.id : null;
10658         var recordType = this.recordType, fields = recordType.prototype.fields;
10659         var records = [];
10660         var root = o;
10661             for(var i = 0; i < root.length; i++){
10662                     var n = root[i];
10663                 var values = {};
10664                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10665                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10666                 var f = fields.items[j];
10667                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10668                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10669                 v = f.convert(v);
10670                 values[f.name] = v;
10671             }
10672                 var record = new recordType(values, id);
10673                 record.json = n;
10674                 records[records.length] = record;
10675             }
10676             return {
10677                 records : records,
10678                 totalRecords : records.length
10679             };
10680     }
10681 });/*
10682  * - LGPL
10683  * * 
10684  */
10685
10686 /**
10687  * @class Roo.bootstrap.ComboBox
10688  * @extends Roo.bootstrap.TriggerField
10689  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10690  * @cfg {Boolean} append (true|false) default false
10691  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10692  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10693  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10694  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10695  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10696  * @constructor
10697  * Create a new ComboBox.
10698  * @param {Object} config Configuration options
10699  */
10700 Roo.bootstrap.ComboBox = function(config){
10701     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10702     this.addEvents({
10703         /**
10704          * @event expand
10705          * Fires when the dropdown list is expanded
10706              * @param {Roo.bootstrap.ComboBox} combo This combo box
10707              */
10708         'expand' : true,
10709         /**
10710          * @event collapse
10711          * Fires when the dropdown list is collapsed
10712              * @param {Roo.bootstrap.ComboBox} combo This combo box
10713              */
10714         'collapse' : true,
10715         /**
10716          * @event beforeselect
10717          * Fires before a list item is selected. Return false to cancel the selection.
10718              * @param {Roo.bootstrap.ComboBox} combo This combo box
10719              * @param {Roo.data.Record} record The data record returned from the underlying store
10720              * @param {Number} index The index of the selected item in the dropdown list
10721              */
10722         'beforeselect' : true,
10723         /**
10724          * @event select
10725          * Fires when a list item is selected
10726              * @param {Roo.bootstrap.ComboBox} combo This combo box
10727              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10728              * @param {Number} index The index of the selected item in the dropdown list
10729              */
10730         'select' : true,
10731         /**
10732          * @event beforequery
10733          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10734          * The event object passed has these properties:
10735              * @param {Roo.bootstrap.ComboBox} combo This combo box
10736              * @param {String} query The query
10737              * @param {Boolean} forceAll true to force "all" query
10738              * @param {Boolean} cancel true to cancel the query
10739              * @param {Object} e The query event object
10740              */
10741         'beforequery': true,
10742          /**
10743          * @event add
10744          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10745              * @param {Roo.bootstrap.ComboBox} combo This combo box
10746              */
10747         'add' : true,
10748         /**
10749          * @event edit
10750          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10751              * @param {Roo.bootstrap.ComboBox} combo This combo box
10752              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10753              */
10754         'edit' : true,
10755         /**
10756          * @event remove
10757          * Fires when the remove value from the combobox array
10758              * @param {Roo.bootstrap.ComboBox} combo This combo box
10759              */
10760         'remove' : true
10761         
10762     });
10763     
10764     this.item = [];
10765     this.tickItems = [];
10766     
10767     this.selectedIndex = -1;
10768     if(this.mode == 'local'){
10769         if(config.queryDelay === undefined){
10770             this.queryDelay = 10;
10771         }
10772         if(config.minChars === undefined){
10773             this.minChars = 0;
10774         }
10775     }
10776 };
10777
10778 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10779      
10780     /**
10781      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10782      * rendering into an Roo.Editor, defaults to false)
10783      */
10784     /**
10785      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10786      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10787      */
10788     /**
10789      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10790      */
10791     /**
10792      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10793      * the dropdown list (defaults to undefined, with no header element)
10794      */
10795
10796      /**
10797      * @cfg {String/Roo.Template} tpl The template to use to render the output
10798      */
10799      
10800      /**
10801      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10802      */
10803     listWidth: undefined,
10804     /**
10805      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10806      * mode = 'remote' or 'text' if mode = 'local')
10807      */
10808     displayField: undefined,
10809     /**
10810      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10811      * mode = 'remote' or 'value' if mode = 'local'). 
10812      * Note: use of a valueField requires the user make a selection
10813      * in order for a value to be mapped.
10814      */
10815     valueField: undefined,
10816     
10817     
10818     /**
10819      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10820      * field's data value (defaults to the underlying DOM element's name)
10821      */
10822     hiddenName: undefined,
10823     /**
10824      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10825      */
10826     listClass: '',
10827     /**
10828      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10829      */
10830     selectedClass: 'active',
10831     
10832     /**
10833      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10834      */
10835     shadow:'sides',
10836     /**
10837      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10838      * anchor positions (defaults to 'tl-bl')
10839      */
10840     listAlign: 'tl-bl?',
10841     /**
10842      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10843      */
10844     maxHeight: 300,
10845     /**
10846      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10847      * query specified by the allQuery config option (defaults to 'query')
10848      */
10849     triggerAction: 'query',
10850     /**
10851      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10852      * (defaults to 4, does not apply if editable = false)
10853      */
10854     minChars : 4,
10855     /**
10856      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10857      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10858      */
10859     typeAhead: false,
10860     /**
10861      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10862      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10863      */
10864     queryDelay: 500,
10865     /**
10866      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10867      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10868      */
10869     pageSize: 0,
10870     /**
10871      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10872      * when editable = true (defaults to false)
10873      */
10874     selectOnFocus:false,
10875     /**
10876      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10877      */
10878     queryParam: 'query',
10879     /**
10880      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10881      * when mode = 'remote' (defaults to 'Loading...')
10882      */
10883     loadingText: 'Loading...',
10884     /**
10885      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10886      */
10887     resizable: false,
10888     /**
10889      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10890      */
10891     handleHeight : 8,
10892     /**
10893      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10894      * traditional select (defaults to true)
10895      */
10896     editable: true,
10897     /**
10898      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10899      */
10900     allQuery: '',
10901     /**
10902      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10903      */
10904     mode: 'remote',
10905     /**
10906      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10907      * listWidth has a higher value)
10908      */
10909     minListWidth : 70,
10910     /**
10911      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10912      * allow the user to set arbitrary text into the field (defaults to false)
10913      */
10914     forceSelection:false,
10915     /**
10916      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10917      * if typeAhead = true (defaults to 250)
10918      */
10919     typeAheadDelay : 250,
10920     /**
10921      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10922      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10923      */
10924     valueNotFoundText : undefined,
10925     /**
10926      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10927      */
10928     blockFocus : false,
10929     
10930     /**
10931      * @cfg {Boolean} disableClear Disable showing of clear button.
10932      */
10933     disableClear : false,
10934     /**
10935      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10936      */
10937     alwaysQuery : false,
10938     
10939     /**
10940      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10941      */
10942     multiple : false,
10943     
10944     /**
10945      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
10946      */
10947     invalidClass : "has-warning",
10948     
10949     /**
10950      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
10951      */
10952     validClass : "has-success",
10953     
10954     //private
10955     addicon : false,
10956     editicon: false,
10957     
10958     page: 0,
10959     hasQuery: false,
10960     append: false,
10961     loadNext: false,
10962     autoFocus : true,
10963     tickable : false,
10964     btnPosition : 'right',
10965     triggerList : true,
10966     showToggleBtn : true,
10967     // element that contains real text value.. (when hidden is used..)
10968     
10969     getAutoCreate : function()
10970     {
10971         var cfg = false;
10972         
10973         /*
10974          *  Normal ComboBox
10975          */
10976         if(!this.tickable){
10977             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10978             return cfg;
10979         }
10980         
10981         /*
10982          *  ComboBox with tickable selections
10983          */
10984              
10985         var align = this.labelAlign || this.parentLabelAlign();
10986         
10987         cfg = {
10988             cls : 'form-group roo-combobox-tickable' //input-group
10989         };
10990         
10991         var buttons = {
10992             tag : 'div',
10993             cls : 'tickable-buttons',
10994             cn : [
10995                 {
10996                     tag : 'button',
10997                     type : 'button',
10998                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10999                     html : 'Edit'
11000                 },
11001                 {
11002                     tag : 'button',
11003                     type : 'button',
11004                     name : 'ok',
11005                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11006                     html : 'Done'
11007                 },
11008                 {
11009                     tag : 'button',
11010                     type : 'button',
11011                     name : 'cancel',
11012                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11013                     html : 'Cancel'
11014                 }
11015             ]
11016         };
11017         
11018         if(this.editable){
11019             buttons.cn.unshift({
11020                 tag: 'input',
11021                 cls: 'select2-search-field-input'
11022             });
11023         }
11024         
11025         var _this = this;
11026         
11027         Roo.each(buttons.cn, function(c){
11028             if (_this.size) {
11029                 c.cls += ' btn-' + _this.size;
11030             }
11031
11032             if (_this.disabled) {
11033                 c.disabled = true;
11034             }
11035         });
11036         
11037         var box = {
11038             tag: 'div',
11039             cn: [
11040                 {
11041                     tag: 'input',
11042                     type : 'hidden',
11043                     cls: 'form-hidden-field'
11044                 },
11045                 {
11046                     tag: 'ul',
11047                     cls: 'select2-choices',
11048                     cn:[
11049                         {
11050                             tag: 'li',
11051                             cls: 'select2-search-field',
11052                             cn: [
11053
11054                                 buttons
11055                             ]
11056                         }
11057                     ]
11058                 }
11059             ]
11060         }
11061         
11062         var combobox = {
11063             cls: 'select2-container input-group select2-container-multi',
11064             cn: [
11065                 box
11066 //                {
11067 //                    tag: 'ul',
11068 //                    cls: 'typeahead typeahead-long dropdown-menu',
11069 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11070 //                }
11071             ]
11072         };
11073         
11074         if(this.hasFeedback && !this.allowBlank){
11075             
11076             var feedback = {
11077                 tag: 'span',
11078                 cls: 'glyphicon form-control-feedback'
11079             };
11080
11081             combobox.cn.push(feedback);
11082         }
11083         
11084         if (align ==='left' && this.fieldLabel.length) {
11085             
11086                 Roo.log("left and has label");
11087                 cfg.cn = [
11088                     
11089                     {
11090                         tag: 'label',
11091                         'for' :  id,
11092                         cls : 'control-label col-sm-' + this.labelWidth,
11093                         html : this.fieldLabel
11094                         
11095                     },
11096                     {
11097                         cls : "col-sm-" + (12 - this.labelWidth), 
11098                         cn: [
11099                             combobox
11100                         ]
11101                     }
11102                     
11103                 ];
11104         } else if ( this.fieldLabel.length) {
11105                 Roo.log(" label");
11106                  cfg.cn = [
11107                    
11108                     {
11109                         tag: 'label',
11110                         //cls : 'input-group-addon',
11111                         html : this.fieldLabel
11112                         
11113                     },
11114                     
11115                     combobox
11116                     
11117                 ];
11118
11119         } else {
11120             
11121                 Roo.log(" no label && no align");
11122                 cfg = combobox
11123                      
11124                 
11125         }
11126          
11127         var settings=this;
11128         ['xs','sm','md','lg'].map(function(size){
11129             if (settings[size]) {
11130                 cfg.cls += ' col-' + size + '-' + settings[size];
11131             }
11132         });
11133         
11134         return cfg;
11135         
11136     },
11137     
11138     // private
11139     initEvents: function()
11140     {
11141         
11142         if (!this.store) {
11143             throw "can not find store for combo";
11144         }
11145         this.store = Roo.factory(this.store, Roo.data);
11146         
11147         if(this.tickable){
11148             this.initTickableEvents();
11149             return;
11150         }
11151         
11152         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11153         
11154         if(this.hiddenName){
11155             
11156             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11157             
11158             this.hiddenField.dom.value =
11159                 this.hiddenValue !== undefined ? this.hiddenValue :
11160                 this.value !== undefined ? this.value : '';
11161
11162             // prevent input submission
11163             this.el.dom.removeAttribute('name');
11164             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11165              
11166              
11167         }
11168         //if(Roo.isGecko){
11169         //    this.el.dom.setAttribute('autocomplete', 'off');
11170         //}
11171         
11172         var cls = 'x-combo-list';
11173         
11174         //this.list = new Roo.Layer({
11175         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11176         //});
11177         
11178         var _this = this;
11179         
11180         (function(){
11181             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11182             _this.list.setWidth(lw);
11183         }).defer(100);
11184         
11185         this.list.on('mouseover', this.onViewOver, this);
11186         this.list.on('mousemove', this.onViewMove, this);
11187         
11188         this.list.on('scroll', this.onViewScroll, this);
11189         
11190         /*
11191         this.list.swallowEvent('mousewheel');
11192         this.assetHeight = 0;
11193
11194         if(this.title){
11195             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11196             this.assetHeight += this.header.getHeight();
11197         }
11198
11199         this.innerList = this.list.createChild({cls:cls+'-inner'});
11200         this.innerList.on('mouseover', this.onViewOver, this);
11201         this.innerList.on('mousemove', this.onViewMove, this);
11202         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11203         
11204         if(this.allowBlank && !this.pageSize && !this.disableClear){
11205             this.footer = this.list.createChild({cls:cls+'-ft'});
11206             this.pageTb = new Roo.Toolbar(this.footer);
11207            
11208         }
11209         if(this.pageSize){
11210             this.footer = this.list.createChild({cls:cls+'-ft'});
11211             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11212                     {pageSize: this.pageSize});
11213             
11214         }
11215         
11216         if (this.pageTb && this.allowBlank && !this.disableClear) {
11217             var _this = this;
11218             this.pageTb.add(new Roo.Toolbar.Fill(), {
11219                 cls: 'x-btn-icon x-btn-clear',
11220                 text: '&#160;',
11221                 handler: function()
11222                 {
11223                     _this.collapse();
11224                     _this.clearValue();
11225                     _this.onSelect(false, -1);
11226                 }
11227             });
11228         }
11229         if (this.footer) {
11230             this.assetHeight += this.footer.getHeight();
11231         }
11232         */
11233             
11234         if(!this.tpl){
11235             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11236         }
11237
11238         this.view = new Roo.View(this.list, this.tpl, {
11239             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11240         });
11241         //this.view.wrapEl.setDisplayed(false);
11242         this.view.on('click', this.onViewClick, this);
11243         
11244         
11245         
11246         this.store.on('beforeload', this.onBeforeLoad, this);
11247         this.store.on('load', this.onLoad, this);
11248         this.store.on('loadexception', this.onLoadException, this);
11249         /*
11250         if(this.resizable){
11251             this.resizer = new Roo.Resizable(this.list,  {
11252                pinned:true, handles:'se'
11253             });
11254             this.resizer.on('resize', function(r, w, h){
11255                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11256                 this.listWidth = w;
11257                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11258                 this.restrictHeight();
11259             }, this);
11260             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11261         }
11262         */
11263         if(!this.editable){
11264             this.editable = true;
11265             this.setEditable(false);
11266         }
11267         
11268         /*
11269         
11270         if (typeof(this.events.add.listeners) != 'undefined') {
11271             
11272             this.addicon = this.wrap.createChild(
11273                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11274        
11275             this.addicon.on('click', function(e) {
11276                 this.fireEvent('add', this);
11277             }, this);
11278         }
11279         if (typeof(this.events.edit.listeners) != 'undefined') {
11280             
11281             this.editicon = this.wrap.createChild(
11282                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11283             if (this.addicon) {
11284                 this.editicon.setStyle('margin-left', '40px');
11285             }
11286             this.editicon.on('click', function(e) {
11287                 
11288                 // we fire even  if inothing is selected..
11289                 this.fireEvent('edit', this, this.lastData );
11290                 
11291             }, this);
11292         }
11293         */
11294         
11295         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11296             "up" : function(e){
11297                 this.inKeyMode = true;
11298                 this.selectPrev();
11299             },
11300
11301             "down" : function(e){
11302                 if(!this.isExpanded()){
11303                     this.onTriggerClick();
11304                 }else{
11305                     this.inKeyMode = true;
11306                     this.selectNext();
11307                 }
11308             },
11309
11310             "enter" : function(e){
11311 //                this.onViewClick();
11312                 //return true;
11313                 this.collapse();
11314                 
11315                 if(this.fireEvent("specialkey", this, e)){
11316                     this.onViewClick(false);
11317                 }
11318                 
11319                 return true;
11320             },
11321
11322             "esc" : function(e){
11323                 this.collapse();
11324             },
11325
11326             "tab" : function(e){
11327                 this.collapse();
11328                 
11329                 if(this.fireEvent("specialkey", this, e)){
11330                     this.onViewClick(false);
11331                 }
11332                 
11333                 return true;
11334             },
11335
11336             scope : this,
11337
11338             doRelay : function(foo, bar, hname){
11339                 if(hname == 'down' || this.scope.isExpanded()){
11340                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11341                 }
11342                 return true;
11343             },
11344
11345             forceKeyDown: true
11346         });
11347         
11348         
11349         this.queryDelay = Math.max(this.queryDelay || 10,
11350                 this.mode == 'local' ? 10 : 250);
11351         
11352         
11353         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11354         
11355         if(this.typeAhead){
11356             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11357         }
11358         if(this.editable !== false){
11359             this.inputEl().on("keyup", this.onKeyUp, this);
11360         }
11361         if(this.forceSelection){
11362             this.inputEl().on('blur', this.doForce, this);
11363         }
11364         
11365         if(this.multiple){
11366             this.choices = this.el.select('ul.select2-choices', true).first();
11367             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11368         }
11369     },
11370     
11371     initTickableEvents: function()
11372     {   
11373         this.createList();
11374         
11375         if(this.hiddenName){
11376             
11377             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11378             
11379             this.hiddenField.dom.value =
11380                 this.hiddenValue !== undefined ? this.hiddenValue :
11381                 this.value !== undefined ? this.value : '';
11382
11383             // prevent input submission
11384             this.el.dom.removeAttribute('name');
11385             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11386              
11387              
11388         }
11389         
11390 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11391         
11392         this.choices = this.el.select('ul.select2-choices', true).first();
11393         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11394         if(this.triggerList){
11395             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11396         }
11397          
11398         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11399         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11400         
11401         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11402         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11403         
11404         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11405         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11406         
11407         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11408         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11409         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11410         
11411         this.okBtn.hide();
11412         this.cancelBtn.hide();
11413         
11414         var _this = this;
11415         
11416         (function(){
11417             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11418             _this.list.setWidth(lw);
11419         }).defer(100);
11420         
11421         this.list.on('mouseover', this.onViewOver, this);
11422         this.list.on('mousemove', this.onViewMove, this);
11423         
11424         this.list.on('scroll', this.onViewScroll, this);
11425         
11426         if(!this.tpl){
11427             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>';
11428         }
11429
11430         this.view = new Roo.View(this.list, this.tpl, {
11431             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11432         });
11433         
11434         //this.view.wrapEl.setDisplayed(false);
11435         this.view.on('click', this.onViewClick, this);
11436         
11437         
11438         
11439         this.store.on('beforeload', this.onBeforeLoad, this);
11440         this.store.on('load', this.onLoad, this);
11441         this.store.on('loadexception', this.onLoadException, this);
11442         
11443         if(this.editable){
11444             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11445                 "up" : function(e){
11446                     this.inKeyMode = true;
11447                     this.selectPrev();
11448                 },
11449
11450                 "down" : function(e){
11451                     this.inKeyMode = true;
11452                     this.selectNext();
11453                 },
11454
11455                 "enter" : function(e){
11456                     if(this.fireEvent("specialkey", this, e)){
11457                         this.onViewClick(false);
11458                     }
11459                     
11460                     return true;
11461                 },
11462
11463                 "esc" : function(e){
11464                     this.onTickableFooterButtonClick(e, false, false);
11465                 },
11466
11467                 "tab" : function(e){
11468                     this.fireEvent("specialkey", this, e);
11469                     
11470                     this.onTickableFooterButtonClick(e, false, false);
11471                     
11472                     return true;
11473                 },
11474
11475                 scope : this,
11476
11477                 doRelay : function(e, fn, key){
11478                     if(this.scope.isExpanded()){
11479                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11480                     }
11481                     return true;
11482                 },
11483
11484                 forceKeyDown: true
11485             });
11486         }
11487         
11488         this.queryDelay = Math.max(this.queryDelay || 10,
11489                 this.mode == 'local' ? 10 : 250);
11490         
11491         
11492         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11493         
11494         if(this.typeAhead){
11495             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11496         }
11497         
11498         if(this.editable !== false){
11499             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11500         }
11501         
11502     },
11503
11504     onDestroy : function(){
11505         if(this.view){
11506             this.view.setStore(null);
11507             this.view.el.removeAllListeners();
11508             this.view.el.remove();
11509             this.view.purgeListeners();
11510         }
11511         if(this.list){
11512             this.list.dom.innerHTML  = '';
11513         }
11514         
11515         if(this.store){
11516             this.store.un('beforeload', this.onBeforeLoad, this);
11517             this.store.un('load', this.onLoad, this);
11518             this.store.un('loadexception', this.onLoadException, this);
11519         }
11520         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11521     },
11522
11523     // private
11524     fireKey : function(e){
11525         if(e.isNavKeyPress() && !this.list.isVisible()){
11526             this.fireEvent("specialkey", this, e);
11527         }
11528     },
11529
11530     // private
11531     onResize: function(w, h){
11532 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11533 //        
11534 //        if(typeof w != 'number'){
11535 //            // we do not handle it!?!?
11536 //            return;
11537 //        }
11538 //        var tw = this.trigger.getWidth();
11539 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11540 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11541 //        var x = w - tw;
11542 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11543 //            
11544 //        //this.trigger.setStyle('left', x+'px');
11545 //        
11546 //        if(this.list && this.listWidth === undefined){
11547 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11548 //            this.list.setWidth(lw);
11549 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11550 //        }
11551         
11552     
11553         
11554     },
11555
11556     /**
11557      * Allow or prevent the user from directly editing the field text.  If false is passed,
11558      * the user will only be able to select from the items defined in the dropdown list.  This method
11559      * is the runtime equivalent of setting the 'editable' config option at config time.
11560      * @param {Boolean} value True to allow the user to directly edit the field text
11561      */
11562     setEditable : function(value){
11563         if(value == this.editable){
11564             return;
11565         }
11566         this.editable = value;
11567         if(!value){
11568             this.inputEl().dom.setAttribute('readOnly', true);
11569             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11570             this.inputEl().addClass('x-combo-noedit');
11571         }else{
11572             this.inputEl().dom.setAttribute('readOnly', false);
11573             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11574             this.inputEl().removeClass('x-combo-noedit');
11575         }
11576     },
11577
11578     // private
11579     
11580     onBeforeLoad : function(combo,opts){
11581         if(!this.hasFocus){
11582             return;
11583         }
11584          if (!opts.add) {
11585             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11586          }
11587         this.restrictHeight();
11588         this.selectedIndex = -1;
11589     },
11590
11591     // private
11592     onLoad : function(){
11593         
11594         this.hasQuery = false;
11595         
11596         if(!this.hasFocus){
11597             return;
11598         }
11599         
11600         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11601             this.loading.hide();
11602         }
11603              
11604         if(this.store.getCount() > 0){
11605             this.expand();
11606             this.restrictHeight();
11607             if(this.lastQuery == this.allQuery){
11608                 if(this.editable && !this.tickable){
11609                     this.inputEl().dom.select();
11610                 }
11611                 
11612                 if(
11613                     !this.selectByValue(this.value, true) &&
11614                     this.autoFocus && 
11615                     (
11616                         !this.store.lastOptions ||
11617                         typeof(this.store.lastOptions.add) == 'undefined' || 
11618                         this.store.lastOptions.add != true
11619                     )
11620                 ){
11621                     this.select(0, true);
11622                 }
11623             }else{
11624                 if(this.autoFocus){
11625                     this.selectNext();
11626                 }
11627                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11628                     this.taTask.delay(this.typeAheadDelay);
11629                 }
11630             }
11631         }else{
11632             this.onEmptyResults();
11633         }
11634         
11635         //this.el.focus();
11636     },
11637     // private
11638     onLoadException : function()
11639     {
11640         this.hasQuery = false;
11641         
11642         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11643             this.loading.hide();
11644         }
11645         
11646         if(this.tickable && this.editable){
11647             return;
11648         }
11649         
11650         this.collapse();
11651         
11652         Roo.log(this.store.reader.jsonData);
11653         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11654             // fixme
11655             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11656         }
11657         
11658         
11659     },
11660     // private
11661     onTypeAhead : function(){
11662         if(this.store.getCount() > 0){
11663             var r = this.store.getAt(0);
11664             var newValue = r.data[this.displayField];
11665             var len = newValue.length;
11666             var selStart = this.getRawValue().length;
11667             
11668             if(selStart != len){
11669                 this.setRawValue(newValue);
11670                 this.selectText(selStart, newValue.length);
11671             }
11672         }
11673     },
11674
11675     // private
11676     onSelect : function(record, index){
11677         
11678         if(this.fireEvent('beforeselect', this, record, index) !== false){
11679         
11680             this.setFromData(index > -1 ? record.data : false);
11681             
11682             this.collapse();
11683             this.fireEvent('select', this, record, index);
11684         }
11685     },
11686
11687     /**
11688      * Returns the currently selected field value or empty string if no value is set.
11689      * @return {String} value The selected value
11690      */
11691     getValue : function(){
11692         
11693         if(this.multiple){
11694             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11695         }
11696         
11697         if(this.valueField){
11698             return typeof this.value != 'undefined' ? this.value : '';
11699         }else{
11700             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11701         }
11702     },
11703
11704     /**
11705      * Clears any text/value currently set in the field
11706      */
11707     clearValue : function(){
11708         if(this.hiddenField){
11709             this.hiddenField.dom.value = '';
11710         }
11711         this.value = '';
11712         this.setRawValue('');
11713         this.lastSelectionText = '';
11714         this.lastData = false;
11715         
11716     },
11717
11718     /**
11719      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11720      * will be displayed in the field.  If the value does not match the data value of an existing item,
11721      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11722      * Otherwise the field will be blank (although the value will still be set).
11723      * @param {String} value The value to match
11724      */
11725     setValue : function(v){
11726         if(this.multiple){
11727             this.syncValue();
11728             return;
11729         }
11730         
11731         var text = v;
11732         if(this.valueField){
11733             var r = this.findRecord(this.valueField, v);
11734             if(r){
11735                 text = r.data[this.displayField];
11736             }else if(this.valueNotFoundText !== undefined){
11737                 text = this.valueNotFoundText;
11738             }
11739         }
11740         this.lastSelectionText = text;
11741         if(this.hiddenField){
11742             this.hiddenField.dom.value = v;
11743         }
11744         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11745         this.value = v;
11746     },
11747     /**
11748      * @property {Object} the last set data for the element
11749      */
11750     
11751     lastData : false,
11752     /**
11753      * Sets the value of the field based on a object which is related to the record format for the store.
11754      * @param {Object} value the value to set as. or false on reset?
11755      */
11756     setFromData : function(o){
11757         
11758         if(this.multiple){
11759             this.addItem(o);
11760             return;
11761         }
11762             
11763         var dv = ''; // display value
11764         var vv = ''; // value value..
11765         this.lastData = o;
11766         if (this.displayField) {
11767             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11768         } else {
11769             // this is an error condition!!!
11770             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11771         }
11772         
11773         if(this.valueField){
11774             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11775         }
11776         
11777         if(this.hiddenField){
11778             this.hiddenField.dom.value = vv;
11779             
11780             this.lastSelectionText = dv;
11781             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11782             this.value = vv;
11783             return;
11784         }
11785         // no hidden field.. - we store the value in 'value', but still display
11786         // display field!!!!
11787         this.lastSelectionText = dv;
11788         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11789         this.value = vv;
11790         
11791         
11792     },
11793     // private
11794     reset : function(){
11795         // overridden so that last data is reset..
11796         
11797         if(this.multiple){
11798             this.clearItem();
11799             return;
11800         }
11801         
11802         this.setValue(this.originalValue);
11803         this.clearInvalid();
11804         this.lastData = false;
11805         if (this.view) {
11806             this.view.clearSelections();
11807         }
11808     },
11809     // private
11810     findRecord : function(prop, value){
11811         var record;
11812         if(this.store.getCount() > 0){
11813             this.store.each(function(r){
11814                 if(r.data[prop] == value){
11815                     record = r;
11816                     return false;
11817                 }
11818                 return true;
11819             });
11820         }
11821         return record;
11822     },
11823     
11824     getName: function()
11825     {
11826         // returns hidden if it's set..
11827         if (!this.rendered) {return ''};
11828         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11829         
11830     },
11831     // private
11832     onViewMove : function(e, t){
11833         this.inKeyMode = false;
11834     },
11835
11836     // private
11837     onViewOver : function(e, t){
11838         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11839             return;
11840         }
11841         var item = this.view.findItemFromChild(t);
11842         
11843         if(item){
11844             var index = this.view.indexOf(item);
11845             this.select(index, false);
11846         }
11847     },
11848
11849     // private
11850     onViewClick : function(view, doFocus, el, e)
11851     {
11852         var index = this.view.getSelectedIndexes()[0];
11853         
11854         var r = this.store.getAt(index);
11855         
11856         if(this.tickable){
11857             
11858             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
11859                 return;
11860             }
11861             
11862             var rm = false;
11863             var _this = this;
11864             
11865             Roo.each(this.tickItems, function(v,k){
11866                 
11867                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11868                     _this.tickItems.splice(k, 1);
11869                     
11870                     if(typeof(e) == 'undefined' && view == false){
11871                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
11872                     }
11873                     
11874                     rm = true;
11875                     return;
11876                 }
11877             });
11878             
11879             if(rm){
11880                 return;
11881             }
11882             
11883             this.tickItems.push(r.data);
11884             
11885             if(typeof(e) == 'undefined' && view == false){
11886                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
11887             }
11888                     
11889             return;
11890         }
11891         
11892         if(r){
11893             this.onSelect(r, index);
11894         }
11895         if(doFocus !== false && !this.blockFocus){
11896             this.inputEl().focus();
11897         }
11898     },
11899
11900     // private
11901     restrictHeight : function(){
11902         //this.innerList.dom.style.height = '';
11903         //var inner = this.innerList.dom;
11904         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11905         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11906         //this.list.beginUpdate();
11907         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11908         this.list.alignTo(this.inputEl(), this.listAlign);
11909         this.list.alignTo(this.inputEl(), this.listAlign);
11910         //this.list.endUpdate();
11911     },
11912
11913     // private
11914     onEmptyResults : function(){
11915         
11916         if(this.tickable && this.editable){
11917             this.restrictHeight();
11918             return;
11919         }
11920         
11921         this.collapse();
11922     },
11923
11924     /**
11925      * Returns true if the dropdown list is expanded, else false.
11926      */
11927     isExpanded : function(){
11928         return this.list.isVisible();
11929     },
11930
11931     /**
11932      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11933      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11934      * @param {String} value The data value of the item to select
11935      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11936      * selected item if it is not currently in view (defaults to true)
11937      * @return {Boolean} True if the value matched an item in the list, else false
11938      */
11939     selectByValue : function(v, scrollIntoView){
11940         if(v !== undefined && v !== null){
11941             var r = this.findRecord(this.valueField || this.displayField, v);
11942             if(r){
11943                 this.select(this.store.indexOf(r), scrollIntoView);
11944                 return true;
11945             }
11946         }
11947         return false;
11948     },
11949
11950     /**
11951      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11952      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11953      * @param {Number} index The zero-based index of the list item to select
11954      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11955      * selected item if it is not currently in view (defaults to true)
11956      */
11957     select : function(index, scrollIntoView){
11958         this.selectedIndex = index;
11959         this.view.select(index);
11960         if(scrollIntoView !== false){
11961             var el = this.view.getNode(index);
11962             /*
11963              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
11964              */
11965             if(el){
11966                 this.list.scrollChildIntoView(el, false);
11967             }
11968         }
11969     },
11970
11971     // private
11972     selectNext : function(){
11973         var ct = this.store.getCount();
11974         if(ct > 0){
11975             if(this.selectedIndex == -1){
11976                 this.select(0);
11977             }else if(this.selectedIndex < ct-1){
11978                 this.select(this.selectedIndex+1);
11979             }
11980         }
11981     },
11982
11983     // private
11984     selectPrev : function(){
11985         var ct = this.store.getCount();
11986         if(ct > 0){
11987             if(this.selectedIndex == -1){
11988                 this.select(0);
11989             }else if(this.selectedIndex != 0){
11990                 this.select(this.selectedIndex-1);
11991             }
11992         }
11993     },
11994
11995     // private
11996     onKeyUp : function(e){
11997         if(this.editable !== false && !e.isSpecialKey()){
11998             this.lastKey = e.getKey();
11999             this.dqTask.delay(this.queryDelay);
12000         }
12001     },
12002
12003     // private
12004     validateBlur : function(){
12005         return !this.list || !this.list.isVisible();   
12006     },
12007
12008     // private
12009     initQuery : function(){
12010         
12011         var v = this.getRawValue();
12012         
12013         if(this.tickable && this.editable){
12014             v = this.tickableInputEl().getValue();
12015         }
12016         
12017         this.doQuery(v);
12018     },
12019
12020     // private
12021     doForce : function(){
12022         if(this.inputEl().dom.value.length > 0){
12023             this.inputEl().dom.value =
12024                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12025              
12026         }
12027     },
12028
12029     /**
12030      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12031      * query allowing the query action to be canceled if needed.
12032      * @param {String} query The SQL query to execute
12033      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12034      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12035      * saved in the current store (defaults to false)
12036      */
12037     doQuery : function(q, forceAll){
12038         
12039         if(q === undefined || q === null){
12040             q = '';
12041         }
12042         var qe = {
12043             query: q,
12044             forceAll: forceAll,
12045             combo: this,
12046             cancel:false
12047         };
12048         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12049             return false;
12050         }
12051         q = qe.query;
12052         
12053         forceAll = qe.forceAll;
12054         if(forceAll === true || (q.length >= this.minChars)){
12055             
12056             this.hasQuery = true;
12057             
12058             if(this.lastQuery != q || this.alwaysQuery){
12059                 this.lastQuery = q;
12060                 if(this.mode == 'local'){
12061                     this.selectedIndex = -1;
12062                     if(forceAll){
12063                         this.store.clearFilter();
12064                     }else{
12065                         this.store.filter(this.displayField, q);
12066                     }
12067                     this.onLoad();
12068                 }else{
12069                     
12070                     this.store.baseParams[this.queryParam] = q;
12071                     
12072                     var options = {params : this.getParams(q)};
12073                     
12074                     if(this.loadNext){
12075                         options.add = true;
12076                         options.params.start = this.page * this.pageSize;
12077                     }
12078                     
12079                     this.store.load(options);
12080                     
12081                     /*
12082                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12083                      *  we should expand the list on onLoad
12084                      *  so command out it
12085                      */
12086 //                    this.expand();
12087                 }
12088             }else{
12089                 this.selectedIndex = -1;
12090                 this.onLoad();   
12091             }
12092         }
12093         
12094         this.loadNext = false;
12095     },
12096
12097     // private
12098     getParams : function(q){
12099         var p = {};
12100         //p[this.queryParam] = q;
12101         
12102         if(this.pageSize){
12103             p.start = 0;
12104             p.limit = this.pageSize;
12105         }
12106         return p;
12107     },
12108
12109     /**
12110      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12111      */
12112     collapse : function(){
12113         if(!this.isExpanded()){
12114             return;
12115         }
12116         
12117         this.list.hide();
12118         
12119         if(this.tickable){
12120             this.hasFocus = false;
12121             this.okBtn.hide();
12122             this.cancelBtn.hide();
12123             this.trigger.show();
12124             
12125             if(this.editable){
12126                 this.tickableInputEl().dom.value = '';
12127                 this.tickableInputEl().blur();
12128             }
12129             
12130         }
12131         
12132         Roo.get(document).un('mousedown', this.collapseIf, this);
12133         Roo.get(document).un('mousewheel', this.collapseIf, this);
12134         if (!this.editable) {
12135             Roo.get(document).un('keydown', this.listKeyPress, this);
12136         }
12137         this.fireEvent('collapse', this);
12138     },
12139
12140     // private
12141     collapseIf : function(e){
12142         var in_combo  = e.within(this.el);
12143         var in_list =  e.within(this.list);
12144         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12145         
12146         if (in_combo || in_list || is_list) {
12147             //e.stopPropagation();
12148             return;
12149         }
12150         
12151         if(this.tickable){
12152             this.onTickableFooterButtonClick(e, false, false);
12153         }
12154
12155         this.collapse();
12156         
12157     },
12158
12159     /**
12160      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12161      */
12162     expand : function(){
12163        
12164         if(this.isExpanded() || !this.hasFocus){
12165             return;
12166         }
12167         
12168         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12169         this.list.setWidth(lw);
12170         
12171         
12172          Roo.log('expand');
12173         
12174         this.list.show();
12175         
12176         this.restrictHeight();
12177         
12178         if(this.tickable){
12179             
12180             this.tickItems = Roo.apply([], this.item);
12181             
12182             this.okBtn.show();
12183             this.cancelBtn.show();
12184             this.trigger.hide();
12185             
12186             if(this.editable){
12187                 this.tickableInputEl().focus();
12188             }
12189             
12190         }
12191         
12192         Roo.get(document).on('mousedown', this.collapseIf, this);
12193         Roo.get(document).on('mousewheel', this.collapseIf, this);
12194         if (!this.editable) {
12195             Roo.get(document).on('keydown', this.listKeyPress, this);
12196         }
12197         
12198         this.fireEvent('expand', this);
12199     },
12200
12201     // private
12202     // Implements the default empty TriggerField.onTriggerClick function
12203     onTriggerClick : function(e)
12204     {
12205         Roo.log('trigger click');
12206         
12207         if(this.disabled || !this.triggerList){
12208             return;
12209         }
12210         
12211         this.page = 0;
12212         this.loadNext = false;
12213         
12214         if(this.isExpanded()){
12215             this.collapse();
12216             if (!this.blockFocus) {
12217                 this.inputEl().focus();
12218             }
12219             
12220         }else {
12221             this.hasFocus = true;
12222             if(this.triggerAction == 'all') {
12223                 this.doQuery(this.allQuery, true);
12224             } else {
12225                 this.doQuery(this.getRawValue());
12226             }
12227             if (!this.blockFocus) {
12228                 this.inputEl().focus();
12229             }
12230         }
12231     },
12232     
12233     onTickableTriggerClick : function(e)
12234     {
12235         if(this.disabled){
12236             return;
12237         }
12238         
12239         this.page = 0;
12240         this.loadNext = false;
12241         this.hasFocus = true;
12242         
12243         if(this.triggerAction == 'all') {
12244             this.doQuery(this.allQuery, true);
12245         } else {
12246             this.doQuery(this.getRawValue());
12247         }
12248     },
12249     
12250     onSearchFieldClick : function(e)
12251     {
12252         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12253             this.onTickableFooterButtonClick(e, false, false);
12254             return;
12255         }
12256         
12257         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12258             return;
12259         }
12260         
12261         this.page = 0;
12262         this.loadNext = false;
12263         this.hasFocus = true;
12264         
12265         if(this.triggerAction == 'all') {
12266             this.doQuery(this.allQuery, true);
12267         } else {
12268             this.doQuery(this.getRawValue());
12269         }
12270     },
12271     
12272     listKeyPress : function(e)
12273     {
12274         //Roo.log('listkeypress');
12275         // scroll to first matching element based on key pres..
12276         if (e.isSpecialKey()) {
12277             return false;
12278         }
12279         var k = String.fromCharCode(e.getKey()).toUpperCase();
12280         //Roo.log(k);
12281         var match  = false;
12282         var csel = this.view.getSelectedNodes();
12283         var cselitem = false;
12284         if (csel.length) {
12285             var ix = this.view.indexOf(csel[0]);
12286             cselitem  = this.store.getAt(ix);
12287             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12288                 cselitem = false;
12289             }
12290             
12291         }
12292         
12293         this.store.each(function(v) { 
12294             if (cselitem) {
12295                 // start at existing selection.
12296                 if (cselitem.id == v.id) {
12297                     cselitem = false;
12298                 }
12299                 return true;
12300             }
12301                 
12302             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12303                 match = this.store.indexOf(v);
12304                 return false;
12305             }
12306             return true;
12307         }, this);
12308         
12309         if (match === false) {
12310             return true; // no more action?
12311         }
12312         // scroll to?
12313         this.view.select(match);
12314         var sn = Roo.get(this.view.getSelectedNodes()[0])
12315         sn.scrollIntoView(sn.dom.parentNode, false);
12316     },
12317     
12318     onViewScroll : function(e, t){
12319         
12320         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){
12321             return;
12322         }
12323         
12324         this.hasQuery = true;
12325         
12326         this.loading = this.list.select('.loading', true).first();
12327         
12328         if(this.loading === null){
12329             this.list.createChild({
12330                 tag: 'div',
12331                 cls: 'loading select2-more-results select2-active',
12332                 html: 'Loading more results...'
12333             })
12334             
12335             this.loading = this.list.select('.loading', true).first();
12336             
12337             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12338             
12339             this.loading.hide();
12340         }
12341         
12342         this.loading.show();
12343         
12344         var _combo = this;
12345         
12346         this.page++;
12347         this.loadNext = true;
12348         
12349         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12350         
12351         return;
12352     },
12353     
12354     addItem : function(o)
12355     {   
12356         var dv = ''; // display value
12357         
12358         if (this.displayField) {
12359             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12360         } else {
12361             // this is an error condition!!!
12362             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12363         }
12364         
12365         if(!dv.length){
12366             return;
12367         }
12368         
12369         var choice = this.choices.createChild({
12370             tag: 'li',
12371             cls: 'select2-search-choice',
12372             cn: [
12373                 {
12374                     tag: 'div',
12375                     html: dv
12376                 },
12377                 {
12378                     tag: 'a',
12379                     href: '#',
12380                     cls: 'select2-search-choice-close',
12381                     tabindex: '-1'
12382                 }
12383             ]
12384             
12385         }, this.searchField);
12386         
12387         var close = choice.select('a.select2-search-choice-close', true).first()
12388         
12389         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12390         
12391         this.item.push(o);
12392         
12393         this.lastData = o;
12394         
12395         this.syncValue();
12396         
12397         this.inputEl().dom.value = '';
12398         
12399         this.validate();
12400     },
12401     
12402     onRemoveItem : function(e, _self, o)
12403     {
12404         e.preventDefault();
12405         
12406         this.lastItem = Roo.apply([], this.item);
12407         
12408         var index = this.item.indexOf(o.data) * 1;
12409         
12410         if( index < 0){
12411             Roo.log('not this item?!');
12412             return;
12413         }
12414         
12415         this.item.splice(index, 1);
12416         o.item.remove();
12417         
12418         this.syncValue();
12419         
12420         this.fireEvent('remove', this, e);
12421         
12422         this.validate();
12423         
12424     },
12425     
12426     syncValue : function()
12427     {
12428         if(!this.item.length){
12429             this.clearValue();
12430             return;
12431         }
12432             
12433         var value = [];
12434         var _this = this;
12435         Roo.each(this.item, function(i){
12436             if(_this.valueField){
12437                 value.push(i[_this.valueField]);
12438                 return;
12439             }
12440
12441             value.push(i);
12442         });
12443
12444         this.value = value.join(',');
12445
12446         if(this.hiddenField){
12447             this.hiddenField.dom.value = this.value;
12448         }
12449     },
12450     
12451     clearItem : function()
12452     {
12453         if(!this.multiple){
12454             return;
12455         }
12456         
12457         this.item = [];
12458         
12459         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12460            c.remove();
12461         });
12462         
12463         this.syncValue();
12464         
12465         this.validate();
12466     },
12467     
12468     inputEl: function ()
12469     {
12470         if(this.tickable){
12471             return this.searchField;
12472         }
12473         return this.el.select('input.form-control',true).first();
12474     },
12475     
12476     
12477     onTickableFooterButtonClick : function(e, btn, el)
12478     {
12479         e.preventDefault();
12480         
12481         this.lastItem = Roo.apply([], this.item);
12482         
12483         if(btn && btn.name == 'cancel'){
12484             this.tickItems = Roo.apply([], this.item);
12485             this.collapse();
12486             return;
12487         }
12488         
12489         this.clearItem();
12490         
12491         var _this = this;
12492         
12493         Roo.each(this.tickItems, function(o){
12494             _this.addItem(o);
12495         });
12496         
12497         this.collapse();
12498         
12499     },
12500     
12501     validate : function()
12502     {
12503         var v = this.getRawValue();
12504         
12505         if(this.multiple){
12506             v = this.getValue();
12507         }
12508         
12509         if(this.disabled || this.allowBlank || v.length){
12510             this.markValid();
12511             return true;
12512         }
12513         
12514         this.markInvalid();
12515         return false;
12516     },
12517     
12518     tickableInputEl : function()
12519     {
12520         if(!this.tickable || !this.editable){
12521             return this.inputEl();
12522         }
12523         
12524         return this.inputEl().select('.select2-search-field-input', true).first();
12525     }
12526     
12527     
12528
12529     /** 
12530     * @cfg {Boolean} grow 
12531     * @hide 
12532     */
12533     /** 
12534     * @cfg {Number} growMin 
12535     * @hide 
12536     */
12537     /** 
12538     * @cfg {Number} growMax 
12539     * @hide 
12540     */
12541     /**
12542      * @hide
12543      * @method autoSize
12544      */
12545 });
12546 /*
12547  * Based on:
12548  * Ext JS Library 1.1.1
12549  * Copyright(c) 2006-2007, Ext JS, LLC.
12550  *
12551  * Originally Released Under LGPL - original licence link has changed is not relivant.
12552  *
12553  * Fork - LGPL
12554  * <script type="text/javascript">
12555  */
12556
12557 /**
12558  * @class Roo.View
12559  * @extends Roo.util.Observable
12560  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12561  * This class also supports single and multi selection modes. <br>
12562  * Create a data model bound view:
12563  <pre><code>
12564  var store = new Roo.data.Store(...);
12565
12566  var view = new Roo.View({
12567     el : "my-element",
12568     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12569  
12570     singleSelect: true,
12571     selectedClass: "ydataview-selected",
12572     store: store
12573  });
12574
12575  // listen for node click?
12576  view.on("click", function(vw, index, node, e){
12577  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12578  });
12579
12580  // load XML data
12581  dataModel.load("foobar.xml");
12582  </code></pre>
12583  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12584  * <br><br>
12585  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12586  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12587  * 
12588  * Note: old style constructor is still suported (container, template, config)
12589  * 
12590  * @constructor
12591  * Create a new View
12592  * @param {Object} config The config object
12593  * 
12594  */
12595 Roo.View = function(config, depreciated_tpl, depreciated_config){
12596     
12597     this.parent = false;
12598     
12599     if (typeof(depreciated_tpl) == 'undefined') {
12600         // new way.. - universal constructor.
12601         Roo.apply(this, config);
12602         this.el  = Roo.get(this.el);
12603     } else {
12604         // old format..
12605         this.el  = Roo.get(config);
12606         this.tpl = depreciated_tpl;
12607         Roo.apply(this, depreciated_config);
12608     }
12609     this.wrapEl  = this.el.wrap().wrap();
12610     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12611     
12612     
12613     if(typeof(this.tpl) == "string"){
12614         this.tpl = new Roo.Template(this.tpl);
12615     } else {
12616         // support xtype ctors..
12617         this.tpl = new Roo.factory(this.tpl, Roo);
12618     }
12619     
12620     
12621     this.tpl.compile();
12622     
12623     /** @private */
12624     this.addEvents({
12625         /**
12626          * @event beforeclick
12627          * Fires before a click is processed. Returns false to cancel the default action.
12628          * @param {Roo.View} this
12629          * @param {Number} index The index of the target node
12630          * @param {HTMLElement} node The target node
12631          * @param {Roo.EventObject} e The raw event object
12632          */
12633             "beforeclick" : true,
12634         /**
12635          * @event click
12636          * Fires when a template node is clicked.
12637          * @param {Roo.View} this
12638          * @param {Number} index The index of the target node
12639          * @param {HTMLElement} node The target node
12640          * @param {Roo.EventObject} e The raw event object
12641          */
12642             "click" : true,
12643         /**
12644          * @event dblclick
12645          * Fires when a template node is double clicked.
12646          * @param {Roo.View} this
12647          * @param {Number} index The index of the target node
12648          * @param {HTMLElement} node The target node
12649          * @param {Roo.EventObject} e The raw event object
12650          */
12651             "dblclick" : true,
12652         /**
12653          * @event contextmenu
12654          * Fires when a template node is right clicked.
12655          * @param {Roo.View} this
12656          * @param {Number} index The index of the target node
12657          * @param {HTMLElement} node The target node
12658          * @param {Roo.EventObject} e The raw event object
12659          */
12660             "contextmenu" : true,
12661         /**
12662          * @event selectionchange
12663          * Fires when the selected nodes change.
12664          * @param {Roo.View} this
12665          * @param {Array} selections Array of the selected nodes
12666          */
12667             "selectionchange" : true,
12668     
12669         /**
12670          * @event beforeselect
12671          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12672          * @param {Roo.View} this
12673          * @param {HTMLElement} node The node to be selected
12674          * @param {Array} selections Array of currently selected nodes
12675          */
12676             "beforeselect" : true,
12677         /**
12678          * @event preparedata
12679          * Fires on every row to render, to allow you to change the data.
12680          * @param {Roo.View} this
12681          * @param {Object} data to be rendered (change this)
12682          */
12683           "preparedata" : true
12684           
12685           
12686         });
12687
12688
12689
12690     this.el.on({
12691         "click": this.onClick,
12692         "dblclick": this.onDblClick,
12693         "contextmenu": this.onContextMenu,
12694         scope:this
12695     });
12696
12697     this.selections = [];
12698     this.nodes = [];
12699     this.cmp = new Roo.CompositeElementLite([]);
12700     if(this.store){
12701         this.store = Roo.factory(this.store, Roo.data);
12702         this.setStore(this.store, true);
12703     }
12704     
12705     if ( this.footer && this.footer.xtype) {
12706            
12707          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12708         
12709         this.footer.dataSource = this.store
12710         this.footer.container = fctr;
12711         this.footer = Roo.factory(this.footer, Roo);
12712         fctr.insertFirst(this.el);
12713         
12714         // this is a bit insane - as the paging toolbar seems to detach the el..
12715 //        dom.parentNode.parentNode.parentNode
12716          // they get detached?
12717     }
12718     
12719     
12720     Roo.View.superclass.constructor.call(this);
12721     
12722     
12723 };
12724
12725 Roo.extend(Roo.View, Roo.util.Observable, {
12726     
12727      /**
12728      * @cfg {Roo.data.Store} store Data store to load data from.
12729      */
12730     store : false,
12731     
12732     /**
12733      * @cfg {String|Roo.Element} el The container element.
12734      */
12735     el : '',
12736     
12737     /**
12738      * @cfg {String|Roo.Template} tpl The template used by this View 
12739      */
12740     tpl : false,
12741     /**
12742      * @cfg {String} dataName the named area of the template to use as the data area
12743      *                          Works with domtemplates roo-name="name"
12744      */
12745     dataName: false,
12746     /**
12747      * @cfg {String} selectedClass The css class to add to selected nodes
12748      */
12749     selectedClass : "x-view-selected",
12750      /**
12751      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12752      */
12753     emptyText : "",
12754     
12755     /**
12756      * @cfg {String} text to display on mask (default Loading)
12757      */
12758     mask : false,
12759     /**
12760      * @cfg {Boolean} multiSelect Allow multiple selection
12761      */
12762     multiSelect : false,
12763     /**
12764      * @cfg {Boolean} singleSelect Allow single selection
12765      */
12766     singleSelect:  false,
12767     
12768     /**
12769      * @cfg {Boolean} toggleSelect - selecting 
12770      */
12771     toggleSelect : false,
12772     
12773     /**
12774      * @cfg {Boolean} tickable - selecting 
12775      */
12776     tickable : false,
12777     
12778     /**
12779      * Returns the element this view is bound to.
12780      * @return {Roo.Element}
12781      */
12782     getEl : function(){
12783         return this.wrapEl;
12784     },
12785     
12786     
12787
12788     /**
12789      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12790      */
12791     refresh : function(){
12792         //Roo.log('refresh');
12793         var t = this.tpl;
12794         
12795         // if we are using something like 'domtemplate', then
12796         // the what gets used is:
12797         // t.applySubtemplate(NAME, data, wrapping data..)
12798         // the outer template then get' applied with
12799         //     the store 'extra data'
12800         // and the body get's added to the
12801         //      roo-name="data" node?
12802         //      <span class='roo-tpl-{name}'></span> ?????
12803         
12804         
12805         
12806         this.clearSelections();
12807         this.el.update("");
12808         var html = [];
12809         var records = this.store.getRange();
12810         if(records.length < 1) {
12811             
12812             // is this valid??  = should it render a template??
12813             
12814             this.el.update(this.emptyText);
12815             return;
12816         }
12817         var el = this.el;
12818         if (this.dataName) {
12819             this.el.update(t.apply(this.store.meta)); //????
12820             el = this.el.child('.roo-tpl-' + this.dataName);
12821         }
12822         
12823         for(var i = 0, len = records.length; i < len; i++){
12824             var data = this.prepareData(records[i].data, i, records[i]);
12825             this.fireEvent("preparedata", this, data, i, records[i]);
12826             
12827             var d = Roo.apply({}, data);
12828             
12829             if(this.tickable){
12830                 Roo.apply(d, {'roo-id' : Roo.id()});
12831                 
12832                 var _this = this;
12833             
12834                 Roo.each(this.parent.item, function(item){
12835                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12836                         return;
12837                     }
12838                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12839                 });
12840             }
12841             
12842             html[html.length] = Roo.util.Format.trim(
12843                 this.dataName ?
12844                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12845                     t.apply(d)
12846             );
12847         }
12848         
12849         
12850         
12851         el.update(html.join(""));
12852         this.nodes = el.dom.childNodes;
12853         this.updateIndexes(0);
12854     },
12855     
12856
12857     /**
12858      * Function to override to reformat the data that is sent to
12859      * the template for each node.
12860      * DEPRICATED - use the preparedata event handler.
12861      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12862      * a JSON object for an UpdateManager bound view).
12863      */
12864     prepareData : function(data, index, record)
12865     {
12866         this.fireEvent("preparedata", this, data, index, record);
12867         return data;
12868     },
12869
12870     onUpdate : function(ds, record){
12871         // Roo.log('on update');   
12872         this.clearSelections();
12873         var index = this.store.indexOf(record);
12874         var n = this.nodes[index];
12875         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12876         n.parentNode.removeChild(n);
12877         this.updateIndexes(index, index);
12878     },
12879
12880     
12881     
12882 // --------- FIXME     
12883     onAdd : function(ds, records, index)
12884     {
12885         //Roo.log(['on Add', ds, records, index] );        
12886         this.clearSelections();
12887         if(this.nodes.length == 0){
12888             this.refresh();
12889             return;
12890         }
12891         var n = this.nodes[index];
12892         for(var i = 0, len = records.length; i < len; i++){
12893             var d = this.prepareData(records[i].data, i, records[i]);
12894             if(n){
12895                 this.tpl.insertBefore(n, d);
12896             }else{
12897                 
12898                 this.tpl.append(this.el, d);
12899             }
12900         }
12901         this.updateIndexes(index);
12902     },
12903
12904     onRemove : function(ds, record, index){
12905        // Roo.log('onRemove');
12906         this.clearSelections();
12907         var el = this.dataName  ?
12908             this.el.child('.roo-tpl-' + this.dataName) :
12909             this.el; 
12910         
12911         el.dom.removeChild(this.nodes[index]);
12912         this.updateIndexes(index);
12913     },
12914
12915     /**
12916      * Refresh an individual node.
12917      * @param {Number} index
12918      */
12919     refreshNode : function(index){
12920         this.onUpdate(this.store, this.store.getAt(index));
12921     },
12922
12923     updateIndexes : function(startIndex, endIndex){
12924         var ns = this.nodes;
12925         startIndex = startIndex || 0;
12926         endIndex = endIndex || ns.length - 1;
12927         for(var i = startIndex; i <= endIndex; i++){
12928             ns[i].nodeIndex = i;
12929         }
12930     },
12931
12932     /**
12933      * Changes the data store this view uses and refresh the view.
12934      * @param {Store} store
12935      */
12936     setStore : function(store, initial){
12937         if(!initial && this.store){
12938             this.store.un("datachanged", this.refresh);
12939             this.store.un("add", this.onAdd);
12940             this.store.un("remove", this.onRemove);
12941             this.store.un("update", this.onUpdate);
12942             this.store.un("clear", this.refresh);
12943             this.store.un("beforeload", this.onBeforeLoad);
12944             this.store.un("load", this.onLoad);
12945             this.store.un("loadexception", this.onLoad);
12946         }
12947         if(store){
12948           
12949             store.on("datachanged", this.refresh, this);
12950             store.on("add", this.onAdd, this);
12951             store.on("remove", this.onRemove, this);
12952             store.on("update", this.onUpdate, this);
12953             store.on("clear", this.refresh, this);
12954             store.on("beforeload", this.onBeforeLoad, this);
12955             store.on("load", this.onLoad, this);
12956             store.on("loadexception", this.onLoad, this);
12957         }
12958         
12959         if(store){
12960             this.refresh();
12961         }
12962     },
12963     /**
12964      * onbeforeLoad - masks the loading area.
12965      *
12966      */
12967     onBeforeLoad : function(store,opts)
12968     {
12969          //Roo.log('onBeforeLoad');   
12970         if (!opts.add) {
12971             this.el.update("");
12972         }
12973         this.el.mask(this.mask ? this.mask : "Loading" ); 
12974     },
12975     onLoad : function ()
12976     {
12977         this.el.unmask();
12978     },
12979     
12980
12981     /**
12982      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12983      * @param {HTMLElement} node
12984      * @return {HTMLElement} The template node
12985      */
12986     findItemFromChild : function(node){
12987         var el = this.dataName  ?
12988             this.el.child('.roo-tpl-' + this.dataName,true) :
12989             this.el.dom; 
12990         
12991         if(!node || node.parentNode == el){
12992                     return node;
12993             }
12994             var p = node.parentNode;
12995             while(p && p != el){
12996             if(p.parentNode == el){
12997                 return p;
12998             }
12999             p = p.parentNode;
13000         }
13001             return null;
13002     },
13003
13004     /** @ignore */
13005     onClick : function(e){
13006         var item = this.findItemFromChild(e.getTarget());
13007         if(item){
13008             var index = this.indexOf(item);
13009             if(this.onItemClick(item, index, e) !== false){
13010                 this.fireEvent("click", this, index, item, e);
13011             }
13012         }else{
13013             this.clearSelections();
13014         }
13015     },
13016
13017     /** @ignore */
13018     onContextMenu : function(e){
13019         var item = this.findItemFromChild(e.getTarget());
13020         if(item){
13021             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
13022         }
13023     },
13024
13025     /** @ignore */
13026     onDblClick : function(e){
13027         var item = this.findItemFromChild(e.getTarget());
13028         if(item){
13029             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
13030         }
13031     },
13032
13033     onItemClick : function(item, index, e)
13034     {
13035         if(this.fireEvent("beforeclick", this, index, item, e) === false){
13036             return false;
13037         }
13038         if (this.toggleSelect) {
13039             var m = this.isSelected(item) ? 'unselect' : 'select';
13040             //Roo.log(m);
13041             var _t = this;
13042             _t[m](item, true, false);
13043             return true;
13044         }
13045         if(this.multiSelect || this.singleSelect){
13046             if(this.multiSelect && e.shiftKey && this.lastSelection){
13047                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
13048             }else{
13049                 this.select(item, this.multiSelect && e.ctrlKey);
13050                 this.lastSelection = item;
13051             }
13052             
13053             if(!this.tickable){
13054                 e.preventDefault();
13055             }
13056             
13057         }
13058         return true;
13059     },
13060
13061     /**
13062      * Get the number of selected nodes.
13063      * @return {Number}
13064      */
13065     getSelectionCount : function(){
13066         return this.selections.length;
13067     },
13068
13069     /**
13070      * Get the currently selected nodes.
13071      * @return {Array} An array of HTMLElements
13072      */
13073     getSelectedNodes : function(){
13074         return this.selections;
13075     },
13076
13077     /**
13078      * Get the indexes of the selected nodes.
13079      * @return {Array}
13080      */
13081     getSelectedIndexes : function(){
13082         var indexes = [], s = this.selections;
13083         for(var i = 0, len = s.length; i < len; i++){
13084             indexes.push(s[i].nodeIndex);
13085         }
13086         return indexes;
13087     },
13088
13089     /**
13090      * Clear all selections
13091      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
13092      */
13093     clearSelections : function(suppressEvent){
13094         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
13095             this.cmp.elements = this.selections;
13096             this.cmp.removeClass(this.selectedClass);
13097             this.selections = [];
13098             if(!suppressEvent){
13099                 this.fireEvent("selectionchange", this, this.selections);
13100             }
13101         }
13102     },
13103
13104     /**
13105      * Returns true if the passed node is selected
13106      * @param {HTMLElement/Number} node The node or node index
13107      * @return {Boolean}
13108      */
13109     isSelected : function(node){
13110         var s = this.selections;
13111         if(s.length < 1){
13112             return false;
13113         }
13114         node = this.getNode(node);
13115         return s.indexOf(node) !== -1;
13116     },
13117
13118     /**
13119      * Selects nodes.
13120      * @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
13121      * @param {Boolean} keepExisting (optional) true to keep existing selections
13122      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13123      */
13124     select : function(nodeInfo, keepExisting, suppressEvent){
13125         if(nodeInfo instanceof Array){
13126             if(!keepExisting){
13127                 this.clearSelections(true);
13128             }
13129             for(var i = 0, len = nodeInfo.length; i < len; i++){
13130                 this.select(nodeInfo[i], true, true);
13131             }
13132             return;
13133         } 
13134         var node = this.getNode(nodeInfo);
13135         if(!node || this.isSelected(node)){
13136             return; // already selected.
13137         }
13138         if(!keepExisting){
13139             this.clearSelections(true);
13140         }
13141         
13142         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
13143             Roo.fly(node).addClass(this.selectedClass);
13144             this.selections.push(node);
13145             if(!suppressEvent){
13146                 this.fireEvent("selectionchange", this, this.selections);
13147             }
13148         }
13149         
13150         
13151     },
13152       /**
13153      * Unselects nodes.
13154      * @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
13155      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
13156      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13157      */
13158     unselect : function(nodeInfo, keepExisting, suppressEvent)
13159     {
13160         if(nodeInfo instanceof Array){
13161             Roo.each(this.selections, function(s) {
13162                 this.unselect(s, nodeInfo);
13163             }, this);
13164             return;
13165         }
13166         var node = this.getNode(nodeInfo);
13167         if(!node || !this.isSelected(node)){
13168             //Roo.log("not selected");
13169             return; // not selected.
13170         }
13171         // fireevent???
13172         var ns = [];
13173         Roo.each(this.selections, function(s) {
13174             if (s == node ) {
13175                 Roo.fly(node).removeClass(this.selectedClass);
13176
13177                 return;
13178             }
13179             ns.push(s);
13180         },this);
13181         
13182         this.selections= ns;
13183         this.fireEvent("selectionchange", this, this.selections);
13184     },
13185
13186     /**
13187      * Gets a template node.
13188      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13189      * @return {HTMLElement} The node or null if it wasn't found
13190      */
13191     getNode : function(nodeInfo){
13192         if(typeof nodeInfo == "string"){
13193             return document.getElementById(nodeInfo);
13194         }else if(typeof nodeInfo == "number"){
13195             return this.nodes[nodeInfo];
13196         }
13197         return nodeInfo;
13198     },
13199
13200     /**
13201      * Gets a range template nodes.
13202      * @param {Number} startIndex
13203      * @param {Number} endIndex
13204      * @return {Array} An array of nodes
13205      */
13206     getNodes : function(start, end){
13207         var ns = this.nodes;
13208         start = start || 0;
13209         end = typeof end == "undefined" ? ns.length - 1 : end;
13210         var nodes = [];
13211         if(start <= end){
13212             for(var i = start; i <= end; i++){
13213                 nodes.push(ns[i]);
13214             }
13215         } else{
13216             for(var i = start; i >= end; i--){
13217                 nodes.push(ns[i]);
13218             }
13219         }
13220         return nodes;
13221     },
13222
13223     /**
13224      * Finds the index of the passed node
13225      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13226      * @return {Number} The index of the node or -1
13227      */
13228     indexOf : function(node){
13229         node = this.getNode(node);
13230         if(typeof node.nodeIndex == "number"){
13231             return node.nodeIndex;
13232         }
13233         var ns = this.nodes;
13234         for(var i = 0, len = ns.length; i < len; i++){
13235             if(ns[i] == node){
13236                 return i;
13237             }
13238         }
13239         return -1;
13240     }
13241 });
13242 /*
13243  * - LGPL
13244  *
13245  * based on jquery fullcalendar
13246  * 
13247  */
13248
13249 Roo.bootstrap = Roo.bootstrap || {};
13250 /**
13251  * @class Roo.bootstrap.Calendar
13252  * @extends Roo.bootstrap.Component
13253  * Bootstrap Calendar class
13254  * @cfg {Boolean} loadMask (true|false) default false
13255  * @cfg {Object} header generate the user specific header of the calendar, default false
13256
13257  * @constructor
13258  * Create a new Container
13259  * @param {Object} config The config object
13260  */
13261
13262
13263
13264 Roo.bootstrap.Calendar = function(config){
13265     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
13266      this.addEvents({
13267         /**
13268              * @event select
13269              * Fires when a date is selected
13270              * @param {DatePicker} this
13271              * @param {Date} date The selected date
13272              */
13273         'select': true,
13274         /**
13275              * @event monthchange
13276              * Fires when the displayed month changes 
13277              * @param {DatePicker} this
13278              * @param {Date} date The selected month
13279              */
13280         'monthchange': true,
13281         /**
13282              * @event evententer
13283              * Fires when mouse over an event
13284              * @param {Calendar} this
13285              * @param {event} Event
13286              */
13287         'evententer': true,
13288         /**
13289              * @event eventleave
13290              * Fires when the mouse leaves an
13291              * @param {Calendar} this
13292              * @param {event}
13293              */
13294         'eventleave': true,
13295         /**
13296              * @event eventclick
13297              * Fires when the mouse click an
13298              * @param {Calendar} this
13299              * @param {event}
13300              */
13301         'eventclick': true
13302         
13303     });
13304
13305 };
13306
13307 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
13308     
13309      /**
13310      * @cfg {Number} startDay
13311      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
13312      */
13313     startDay : 0,
13314     
13315     loadMask : false,
13316     
13317     header : false,
13318       
13319     getAutoCreate : function(){
13320         
13321         
13322         var fc_button = function(name, corner, style, content ) {
13323             return Roo.apply({},{
13324                 tag : 'span',
13325                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
13326                          (corner.length ?
13327                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
13328                             ''
13329                         ),
13330                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
13331                 unselectable: 'on'
13332             });
13333         };
13334         
13335         var header = {};
13336         
13337         if(!this.header){
13338             header = {
13339                 tag : 'table',
13340                 cls : 'fc-header',
13341                 style : 'width:100%',
13342                 cn : [
13343                     {
13344                         tag: 'tr',
13345                         cn : [
13346                             {
13347                                 tag : 'td',
13348                                 cls : 'fc-header-left',
13349                                 cn : [
13350                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
13351                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
13352                                     { tag: 'span', cls: 'fc-header-space' },
13353                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
13354
13355
13356                                 ]
13357                             },
13358
13359                             {
13360                                 tag : 'td',
13361                                 cls : 'fc-header-center',
13362                                 cn : [
13363                                     {
13364                                         tag: 'span',
13365                                         cls: 'fc-header-title',
13366                                         cn : {
13367                                             tag: 'H2',
13368                                             html : 'month / year'
13369                                         }
13370                                     }
13371
13372                                 ]
13373                             },
13374                             {
13375                                 tag : 'td',
13376                                 cls : 'fc-header-right',
13377                                 cn : [
13378                               /*      fc_button('month', 'left', '', 'month' ),
13379                                     fc_button('week', '', '', 'week' ),
13380                                     fc_button('day', 'right', '', 'day' )
13381                                 */    
13382
13383                                 ]
13384                             }
13385
13386                         ]
13387                     }
13388                 ]
13389             };
13390         }
13391         
13392         header = this.header;
13393         
13394        
13395         var cal_heads = function() {
13396             var ret = [];
13397             // fixme - handle this.
13398             
13399             for (var i =0; i < Date.dayNames.length; i++) {
13400                 var d = Date.dayNames[i];
13401                 ret.push({
13402                     tag: 'th',
13403                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13404                     html : d.substring(0,3)
13405                 });
13406                 
13407             }
13408             ret[0].cls += ' fc-first';
13409             ret[6].cls += ' fc-last';
13410             return ret;
13411         };
13412         var cal_cell = function(n) {
13413             return  {
13414                 tag: 'td',
13415                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13416                 cn : [
13417                     {
13418                         cn : [
13419                             {
13420                                 cls: 'fc-day-number',
13421                                 html: 'D'
13422                             },
13423                             {
13424                                 cls: 'fc-day-content',
13425                              
13426                                 cn : [
13427                                      {
13428                                         style: 'position: relative;' // height: 17px;
13429                                     }
13430                                 ]
13431                             }
13432                             
13433                             
13434                         ]
13435                     }
13436                 ]
13437                 
13438             }
13439         };
13440         var cal_rows = function() {
13441             
13442             var ret = [];
13443             for (var r = 0; r < 6; r++) {
13444                 var row= {
13445                     tag : 'tr',
13446                     cls : 'fc-week',
13447                     cn : []
13448                 };
13449                 
13450                 for (var i =0; i < Date.dayNames.length; i++) {
13451                     var d = Date.dayNames[i];
13452                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13453
13454                 }
13455                 row.cn[0].cls+=' fc-first';
13456                 row.cn[0].cn[0].style = 'min-height:90px';
13457                 row.cn[6].cls+=' fc-last';
13458                 ret.push(row);
13459                 
13460             }
13461             ret[0].cls += ' fc-first';
13462             ret[4].cls += ' fc-prev-last';
13463             ret[5].cls += ' fc-last';
13464             return ret;
13465             
13466         };
13467         
13468         var cal_table = {
13469             tag: 'table',
13470             cls: 'fc-border-separate',
13471             style : 'width:100%',
13472             cellspacing  : 0,
13473             cn : [
13474                 { 
13475                     tag: 'thead',
13476                     cn : [
13477                         { 
13478                             tag: 'tr',
13479                             cls : 'fc-first fc-last',
13480                             cn : cal_heads()
13481                         }
13482                     ]
13483                 },
13484                 { 
13485                     tag: 'tbody',
13486                     cn : cal_rows()
13487                 }
13488                   
13489             ]
13490         };
13491          
13492          var cfg = {
13493             cls : 'fc fc-ltr',
13494             cn : [
13495                 header,
13496                 {
13497                     cls : 'fc-content',
13498                     style : "position: relative;",
13499                     cn : [
13500                         {
13501                             cls : 'fc-view fc-view-month fc-grid',
13502                             style : 'position: relative',
13503                             unselectable : 'on',
13504                             cn : [
13505                                 {
13506                                     cls : 'fc-event-container',
13507                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13508                                 },
13509                                 cal_table
13510                             ]
13511                         }
13512                     ]
13513     
13514                 }
13515            ] 
13516             
13517         };
13518         
13519          
13520         
13521         return cfg;
13522     },
13523     
13524     
13525     initEvents : function()
13526     {
13527         if(!this.store){
13528             throw "can not find store for calendar";
13529         }
13530         
13531         var mark = {
13532             tag: "div",
13533             cls:"x-dlg-mask",
13534             style: "text-align:center",
13535             cn: [
13536                 {
13537                     tag: "div",
13538                     style: "background-color:white;width:50%;margin:250 auto",
13539                     cn: [
13540                         {
13541                             tag: "img",
13542                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13543                         },
13544                         {
13545                             tag: "span",
13546                             html: "Loading"
13547                         }
13548                         
13549                     ]
13550                 }
13551             ]
13552         }
13553         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13554         
13555         var size = this.el.select('.fc-content', true).first().getSize();
13556         this.maskEl.setSize(size.width, size.height);
13557         this.maskEl.enableDisplayMode("block");
13558         if(!this.loadMask){
13559             this.maskEl.hide();
13560         }
13561         
13562         this.store = Roo.factory(this.store, Roo.data);
13563         this.store.on('load', this.onLoad, this);
13564         this.store.on('beforeload', this.onBeforeLoad, this);
13565         
13566         this.resize();
13567         
13568         this.cells = this.el.select('.fc-day',true);
13569         //Roo.log(this.cells);
13570         this.textNodes = this.el.query('.fc-day-number');
13571         this.cells.addClassOnOver('fc-state-hover');
13572         
13573         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13574         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13575         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13576         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13577         
13578         this.on('monthchange', this.onMonthChange, this);
13579         
13580         this.update(new Date().clearTime());
13581     },
13582     
13583     resize : function() {
13584         var sz  = this.el.getSize();
13585         
13586         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13587         this.el.select('.fc-day-content div',true).setHeight(34);
13588     },
13589     
13590     
13591     // private
13592     showPrevMonth : function(e){
13593         this.update(this.activeDate.add("mo", -1));
13594     },
13595     showToday : function(e){
13596         this.update(new Date().clearTime());
13597     },
13598     // private
13599     showNextMonth : function(e){
13600         this.update(this.activeDate.add("mo", 1));
13601     },
13602
13603     // private
13604     showPrevYear : function(){
13605         this.update(this.activeDate.add("y", -1));
13606     },
13607
13608     // private
13609     showNextYear : function(){
13610         this.update(this.activeDate.add("y", 1));
13611     },
13612
13613     
13614    // private
13615     update : function(date)
13616     {
13617         var vd = this.activeDate;
13618         this.activeDate = date;
13619 //        if(vd && this.el){
13620 //            var t = date.getTime();
13621 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13622 //                Roo.log('using add remove');
13623 //                
13624 //                this.fireEvent('monthchange', this, date);
13625 //                
13626 //                this.cells.removeClass("fc-state-highlight");
13627 //                this.cells.each(function(c){
13628 //                   if(c.dateValue == t){
13629 //                       c.addClass("fc-state-highlight");
13630 //                       setTimeout(function(){
13631 //                            try{c.dom.firstChild.focus();}catch(e){}
13632 //                       }, 50);
13633 //                       return false;
13634 //                   }
13635 //                   return true;
13636 //                });
13637 //                return;
13638 //            }
13639 //        }
13640         
13641         var days = date.getDaysInMonth();
13642         
13643         var firstOfMonth = date.getFirstDateOfMonth();
13644         var startingPos = firstOfMonth.getDay()-this.startDay;
13645         
13646         if(startingPos < this.startDay){
13647             startingPos += 7;
13648         }
13649         
13650         var pm = date.add(Date.MONTH, -1);
13651         var prevStart = pm.getDaysInMonth()-startingPos;
13652 //        
13653         this.cells = this.el.select('.fc-day',true);
13654         this.textNodes = this.el.query('.fc-day-number');
13655         this.cells.addClassOnOver('fc-state-hover');
13656         
13657         var cells = this.cells.elements;
13658         var textEls = this.textNodes;
13659         
13660         Roo.each(cells, function(cell){
13661             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13662         });
13663         
13664         days += startingPos;
13665
13666         // convert everything to numbers so it's fast
13667         var day = 86400000;
13668         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13669         //Roo.log(d);
13670         //Roo.log(pm);
13671         //Roo.log(prevStart);
13672         
13673         var today = new Date().clearTime().getTime();
13674         var sel = date.clearTime().getTime();
13675         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13676         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13677         var ddMatch = this.disabledDatesRE;
13678         var ddText = this.disabledDatesText;
13679         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13680         var ddaysText = this.disabledDaysText;
13681         var format = this.format;
13682         
13683         var setCellClass = function(cal, cell){
13684             cell.row = 0;
13685             cell.events = [];
13686             cell.more = [];
13687             //Roo.log('set Cell Class');
13688             cell.title = "";
13689             var t = d.getTime();
13690             
13691             //Roo.log(d);
13692             
13693             cell.dateValue = t;
13694             if(t == today){
13695                 cell.className += " fc-today";
13696                 cell.className += " fc-state-highlight";
13697                 cell.title = cal.todayText;
13698             }
13699             if(t == sel){
13700                 // disable highlight in other month..
13701                 //cell.className += " fc-state-highlight";
13702                 
13703             }
13704             // disabling
13705             if(t < min) {
13706                 cell.className = " fc-state-disabled";
13707                 cell.title = cal.minText;
13708                 return;
13709             }
13710             if(t > max) {
13711                 cell.className = " fc-state-disabled";
13712                 cell.title = cal.maxText;
13713                 return;
13714             }
13715             if(ddays){
13716                 if(ddays.indexOf(d.getDay()) != -1){
13717                     cell.title = ddaysText;
13718                     cell.className = " fc-state-disabled";
13719                 }
13720             }
13721             if(ddMatch && format){
13722                 var fvalue = d.dateFormat(format);
13723                 if(ddMatch.test(fvalue)){
13724                     cell.title = ddText.replace("%0", fvalue);
13725                     cell.className = " fc-state-disabled";
13726                 }
13727             }
13728             
13729             if (!cell.initialClassName) {
13730                 cell.initialClassName = cell.dom.className;
13731             }
13732             
13733             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13734         };
13735
13736         var i = 0;
13737         
13738         for(; i < startingPos; i++) {
13739             textEls[i].innerHTML = (++prevStart);
13740             d.setDate(d.getDate()+1);
13741             
13742             cells[i].className = "fc-past fc-other-month";
13743             setCellClass(this, cells[i]);
13744         }
13745         
13746         var intDay = 0;
13747         
13748         for(; i < days; i++){
13749             intDay = i - startingPos + 1;
13750             textEls[i].innerHTML = (intDay);
13751             d.setDate(d.getDate()+1);
13752             
13753             cells[i].className = ''; // "x-date-active";
13754             setCellClass(this, cells[i]);
13755         }
13756         var extraDays = 0;
13757         
13758         for(; i < 42; i++) {
13759             textEls[i].innerHTML = (++extraDays);
13760             d.setDate(d.getDate()+1);
13761             
13762             cells[i].className = "fc-future fc-other-month";
13763             setCellClass(this, cells[i]);
13764         }
13765         
13766         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13767         
13768         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13769         
13770         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13771         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13772         
13773         if(totalRows != 6){
13774             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13775             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13776         }
13777         
13778         this.fireEvent('monthchange', this, date);
13779         
13780         
13781         /*
13782         if(!this.internalRender){
13783             var main = this.el.dom.firstChild;
13784             var w = main.offsetWidth;
13785             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13786             Roo.fly(main).setWidth(w);
13787             this.internalRender = true;
13788             // opera does not respect the auto grow header center column
13789             // then, after it gets a width opera refuses to recalculate
13790             // without a second pass
13791             if(Roo.isOpera && !this.secondPass){
13792                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13793                 this.secondPass = true;
13794                 this.update.defer(10, this, [date]);
13795             }
13796         }
13797         */
13798         
13799     },
13800     
13801     findCell : function(dt) {
13802         dt = dt.clearTime().getTime();
13803         var ret = false;
13804         this.cells.each(function(c){
13805             //Roo.log("check " +c.dateValue + '?=' + dt);
13806             if(c.dateValue == dt){
13807                 ret = c;
13808                 return false;
13809             }
13810             return true;
13811         });
13812         
13813         return ret;
13814     },
13815     
13816     findCells : function(ev) {
13817         var s = ev.start.clone().clearTime().getTime();
13818        // Roo.log(s);
13819         var e= ev.end.clone().clearTime().getTime();
13820        // Roo.log(e);
13821         var ret = [];
13822         this.cells.each(function(c){
13823              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13824             
13825             if(c.dateValue > e){
13826                 return ;
13827             }
13828             if(c.dateValue < s){
13829                 return ;
13830             }
13831             ret.push(c);
13832         });
13833         
13834         return ret;    
13835     },
13836     
13837 //    findBestRow: function(cells)
13838 //    {
13839 //        var ret = 0;
13840 //        
13841 //        for (var i =0 ; i < cells.length;i++) {
13842 //            ret  = Math.max(cells[i].rows || 0,ret);
13843 //        }
13844 //        return ret;
13845 //        
13846 //    },
13847     
13848     
13849     addItem : function(ev)
13850     {
13851         // look for vertical location slot in
13852         var cells = this.findCells(ev);
13853         
13854 //        ev.row = this.findBestRow(cells);
13855         
13856         // work out the location.
13857         
13858         var crow = false;
13859         var rows = [];
13860         for(var i =0; i < cells.length; i++) {
13861             
13862             cells[i].row = cells[0].row;
13863             
13864             if(i == 0){
13865                 cells[i].row = cells[i].row + 1;
13866             }
13867             
13868             if (!crow) {
13869                 crow = {
13870                     start : cells[i],
13871                     end :  cells[i]
13872                 };
13873                 continue;
13874             }
13875             if (crow.start.getY() == cells[i].getY()) {
13876                 // on same row.
13877                 crow.end = cells[i];
13878                 continue;
13879             }
13880             // different row.
13881             rows.push(crow);
13882             crow = {
13883                 start: cells[i],
13884                 end : cells[i]
13885             };
13886             
13887         }
13888         
13889         rows.push(crow);
13890         ev.els = [];
13891         ev.rows = rows;
13892         ev.cells = cells;
13893         
13894         cells[0].events.push(ev);
13895         
13896         this.calevents.push(ev);
13897     },
13898     
13899     clearEvents: function() {
13900         
13901         if(!this.calevents){
13902             return;
13903         }
13904         
13905         Roo.each(this.cells.elements, function(c){
13906             c.row = 0;
13907             c.events = [];
13908             c.more = [];
13909         });
13910         
13911         Roo.each(this.calevents, function(e) {
13912             Roo.each(e.els, function(el) {
13913                 el.un('mouseenter' ,this.onEventEnter, this);
13914                 el.un('mouseleave' ,this.onEventLeave, this);
13915                 el.remove();
13916             },this);
13917         },this);
13918         
13919         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13920             e.remove();
13921         });
13922         
13923     },
13924     
13925     renderEvents: function()
13926     {   
13927         var _this = this;
13928         
13929         this.cells.each(function(c) {
13930             
13931             if(c.row < 5){
13932                 return;
13933             }
13934             
13935             var ev = c.events;
13936             
13937             var r = 4;
13938             if(c.row != c.events.length){
13939                 r = 4 - (4 - (c.row - c.events.length));
13940             }
13941             
13942             c.events = ev.slice(0, r);
13943             c.more = ev.slice(r);
13944             
13945             if(c.more.length && c.more.length == 1){
13946                 c.events.push(c.more.pop());
13947             }
13948             
13949             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13950             
13951         });
13952             
13953         this.cells.each(function(c) {
13954             
13955             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13956             
13957             
13958             for (var e = 0; e < c.events.length; e++){
13959                 var ev = c.events[e];
13960                 var rows = ev.rows;
13961                 
13962                 for(var i = 0; i < rows.length; i++) {
13963                 
13964                     // how many rows should it span..
13965
13966                     var  cfg = {
13967                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13968                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13969
13970                         unselectable : "on",
13971                         cn : [
13972                             {
13973                                 cls: 'fc-event-inner',
13974                                 cn : [
13975     //                                {
13976     //                                  tag:'span',
13977     //                                  cls: 'fc-event-time',
13978     //                                  html : cells.length > 1 ? '' : ev.time
13979     //                                },
13980                                     {
13981                                       tag:'span',
13982                                       cls: 'fc-event-title',
13983                                       html : String.format('{0}', ev.title)
13984                                     }
13985
13986
13987                                 ]
13988                             },
13989                             {
13990                                 cls: 'ui-resizable-handle ui-resizable-e',
13991                                 html : '&nbsp;&nbsp;&nbsp'
13992                             }
13993
13994                         ]
13995                     };
13996
13997                     if (i == 0) {
13998                         cfg.cls += ' fc-event-start';
13999                     }
14000                     if ((i+1) == rows.length) {
14001                         cfg.cls += ' fc-event-end';
14002                     }
14003
14004                     var ctr = _this.el.select('.fc-event-container',true).first();
14005                     var cg = ctr.createChild(cfg);
14006
14007                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
14008                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
14009
14010                     var r = (c.more.length) ? 1 : 0;
14011                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
14012                     cg.setWidth(ebox.right - sbox.x -2);
14013
14014                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
14015                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
14016                     cg.on('click', _this.onEventClick, _this, ev);
14017
14018                     ev.els.push(cg);
14019                     
14020                 }
14021                 
14022             }
14023             
14024             
14025             if(c.more.length){
14026                 var  cfg = {
14027                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
14028                     style : 'position: absolute',
14029                     unselectable : "on",
14030                     cn : [
14031                         {
14032                             cls: 'fc-event-inner',
14033                             cn : [
14034                                 {
14035                                   tag:'span',
14036                                   cls: 'fc-event-title',
14037                                   html : 'More'
14038                                 }
14039
14040
14041                             ]
14042                         },
14043                         {
14044                             cls: 'ui-resizable-handle ui-resizable-e',
14045                             html : '&nbsp;&nbsp;&nbsp'
14046                         }
14047
14048                     ]
14049                 };
14050
14051                 var ctr = _this.el.select('.fc-event-container',true).first();
14052                 var cg = ctr.createChild(cfg);
14053
14054                 var sbox = c.select('.fc-day-content',true).first().getBox();
14055                 var ebox = c.select('.fc-day-content',true).first().getBox();
14056                 //Roo.log(cg);
14057                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
14058                 cg.setWidth(ebox.right - sbox.x -2);
14059
14060                 cg.on('click', _this.onMoreEventClick, _this, c.more);
14061                 
14062             }
14063             
14064         });
14065         
14066         
14067         
14068     },
14069     
14070     onEventEnter: function (e, el,event,d) {
14071         this.fireEvent('evententer', this, el, event);
14072     },
14073     
14074     onEventLeave: function (e, el,event,d) {
14075         this.fireEvent('eventleave', this, el, event);
14076     },
14077     
14078     onEventClick: function (e, el,event,d) {
14079         this.fireEvent('eventclick', this, el, event);
14080     },
14081     
14082     onMonthChange: function () {
14083         this.store.load();
14084     },
14085     
14086     onMoreEventClick: function(e, el, more)
14087     {
14088         var _this = this;
14089         
14090         this.calpopover.placement = 'right';
14091         this.calpopover.setTitle('More');
14092         
14093         this.calpopover.setContent('');
14094         
14095         var ctr = this.calpopover.el.select('.popover-content', true).first();
14096         
14097         Roo.each(more, function(m){
14098             var cfg = {
14099                 cls : 'fc-event-hori fc-event-draggable',
14100                 html : m.title
14101             }
14102             var cg = ctr.createChild(cfg);
14103             
14104             cg.on('click', _this.onEventClick, _this, m);
14105         });
14106         
14107         this.calpopover.show(el);
14108         
14109         
14110     },
14111     
14112     onLoad: function () 
14113     {   
14114         this.calevents = [];
14115         var cal = this;
14116         
14117         if(this.store.getCount() > 0){
14118             this.store.data.each(function(d){
14119                cal.addItem({
14120                     id : d.data.id,
14121                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
14122                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
14123                     time : d.data.start_time,
14124                     title : d.data.title,
14125                     description : d.data.description,
14126                     venue : d.data.venue
14127                 });
14128             });
14129         }
14130         
14131         this.renderEvents();
14132         
14133         if(this.calevents.length && this.loadMask){
14134             this.maskEl.hide();
14135         }
14136     },
14137     
14138     onBeforeLoad: function()
14139     {
14140         this.clearEvents();
14141         if(this.loadMask){
14142             this.maskEl.show();
14143         }
14144     }
14145 });
14146
14147  
14148  /*
14149  * - LGPL
14150  *
14151  * element
14152  * 
14153  */
14154
14155 /**
14156  * @class Roo.bootstrap.Popover
14157  * @extends Roo.bootstrap.Component
14158  * Bootstrap Popover class
14159  * @cfg {String} html contents of the popover   (or false to use children..)
14160  * @cfg {String} title of popover (or false to hide)
14161  * @cfg {String} placement how it is placed
14162  * @cfg {String} trigger click || hover (or false to trigger manually)
14163  * @cfg {String} over what (parent or false to trigger manually.)
14164  * @cfg {Number} delay - delay before showing
14165  
14166  * @constructor
14167  * Create a new Popover
14168  * @param {Object} config The config object
14169  */
14170
14171 Roo.bootstrap.Popover = function(config){
14172     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
14173 };
14174
14175 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
14176     
14177     title: 'Fill in a title',
14178     html: false,
14179     
14180     placement : 'right',
14181     trigger : 'hover', // hover
14182     
14183     delay : 0,
14184     
14185     over: 'parent',
14186     
14187     can_build_overlaid : false,
14188     
14189     getChildContainer : function()
14190     {
14191         return this.el.select('.popover-content',true).first();
14192     },
14193     
14194     getAutoCreate : function(){
14195          Roo.log('make popover?');
14196         var cfg = {
14197            cls : 'popover roo-dynamic',
14198            style: 'display:block',
14199            cn : [
14200                 {
14201                     cls : 'arrow'
14202                 },
14203                 {
14204                     cls : 'popover-inner',
14205                     cn : [
14206                         {
14207                             tag: 'h3',
14208                             cls: 'popover-title',
14209                             html : this.title
14210                         },
14211                         {
14212                             cls : 'popover-content',
14213                             html : this.html
14214                         }
14215                     ]
14216                     
14217                 }
14218            ]
14219         };
14220         
14221         return cfg;
14222     },
14223     setTitle: function(str)
14224     {
14225         this.el.select('.popover-title',true).first().dom.innerHTML = str;
14226     },
14227     setContent: function(str)
14228     {
14229         this.el.select('.popover-content',true).first().dom.innerHTML = str;
14230     },
14231     // as it get's added to the bottom of the page.
14232     onRender : function(ct, position)
14233     {
14234         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
14235         if(!this.el){
14236             var cfg = Roo.apply({},  this.getAutoCreate());
14237             cfg.id = Roo.id();
14238             
14239             if (this.cls) {
14240                 cfg.cls += ' ' + this.cls;
14241             }
14242             if (this.style) {
14243                 cfg.style = this.style;
14244             }
14245             Roo.log("adding to ")
14246             this.el = Roo.get(document.body).createChild(cfg, position);
14247             Roo.log(this.el);
14248         }
14249         this.initEvents();
14250     },
14251     
14252     initEvents : function()
14253     {
14254         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
14255         this.el.enableDisplayMode('block');
14256         this.el.hide();
14257         if (this.over === false) {
14258             return; 
14259         }
14260         if (this.triggers === false) {
14261             return;
14262         }
14263         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14264         var triggers = this.trigger ? this.trigger.split(' ') : [];
14265         Roo.each(triggers, function(trigger) {
14266         
14267             if (trigger == 'click') {
14268                 on_el.on('click', this.toggle, this);
14269             } else if (trigger != 'manual') {
14270                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
14271                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
14272       
14273                 on_el.on(eventIn  ,this.enter, this);
14274                 on_el.on(eventOut, this.leave, this);
14275             }
14276         }, this);
14277         
14278     },
14279     
14280     
14281     // private
14282     timeout : null,
14283     hoverState : null,
14284     
14285     toggle : function () {
14286         this.hoverState == 'in' ? this.leave() : this.enter();
14287     },
14288     
14289     enter : function () {
14290        
14291     
14292         clearTimeout(this.timeout);
14293     
14294         this.hoverState = 'in';
14295     
14296         if (!this.delay || !this.delay.show) {
14297             this.show();
14298             return;
14299         }
14300         var _t = this;
14301         this.timeout = setTimeout(function () {
14302             if (_t.hoverState == 'in') {
14303                 _t.show();
14304             }
14305         }, this.delay.show)
14306     },
14307     leave : function() {
14308         clearTimeout(this.timeout);
14309     
14310         this.hoverState = 'out';
14311     
14312         if (!this.delay || !this.delay.hide) {
14313             this.hide();
14314             return;
14315         }
14316         var _t = this;
14317         this.timeout = setTimeout(function () {
14318             if (_t.hoverState == 'out') {
14319                 _t.hide();
14320             }
14321         }, this.delay.hide)
14322     },
14323     
14324     show : function (on_el)
14325     {
14326         if (!on_el) {
14327             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14328         }
14329         // set content.
14330         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
14331         if (this.html !== false) {
14332             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
14333         }
14334         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
14335         if (!this.title.length) {
14336             this.el.select('.popover-title',true).hide();
14337         }
14338         
14339         var placement = typeof this.placement == 'function' ?
14340             this.placement.call(this, this.el, on_el) :
14341             this.placement;
14342             
14343         var autoToken = /\s?auto?\s?/i;
14344         var autoPlace = autoToken.test(placement);
14345         if (autoPlace) {
14346             placement = placement.replace(autoToken, '') || 'top';
14347         }
14348         
14349         //this.el.detach()
14350         //this.el.setXY([0,0]);
14351         this.el.show();
14352         this.el.dom.style.display='block';
14353         this.el.addClass(placement);
14354         
14355         //this.el.appendTo(on_el);
14356         
14357         var p = this.getPosition();
14358         var box = this.el.getBox();
14359         
14360         if (autoPlace) {
14361             // fixme..
14362         }
14363         var align = Roo.bootstrap.Popover.alignment[placement];
14364         this.el.alignTo(on_el, align[0],align[1]);
14365         //var arrow = this.el.select('.arrow',true).first();
14366         //arrow.set(align[2], 
14367         
14368         this.el.addClass('in');
14369         this.hoverState = null;
14370         
14371         if (this.el.hasClass('fade')) {
14372             // fade it?
14373         }
14374         
14375     },
14376     hide : function()
14377     {
14378         this.el.setXY([0,0]);
14379         this.el.removeClass('in');
14380         this.el.hide();
14381         
14382     }
14383     
14384 });
14385
14386 Roo.bootstrap.Popover.alignment = {
14387     'left' : ['r-l', [-10,0], 'right'],
14388     'right' : ['l-r', [10,0], 'left'],
14389     'bottom' : ['t-b', [0,10], 'top'],
14390     'top' : [ 'b-t', [0,-10], 'bottom']
14391 };
14392
14393  /*
14394  * - LGPL
14395  *
14396  * Progress
14397  * 
14398  */
14399
14400 /**
14401  * @class Roo.bootstrap.Progress
14402  * @extends Roo.bootstrap.Component
14403  * Bootstrap Progress class
14404  * @cfg {Boolean} striped striped of the progress bar
14405  * @cfg {Boolean} active animated of the progress bar
14406  * 
14407  * 
14408  * @constructor
14409  * Create a new Progress
14410  * @param {Object} config The config object
14411  */
14412
14413 Roo.bootstrap.Progress = function(config){
14414     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14415 };
14416
14417 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14418     
14419     striped : false,
14420     active: false,
14421     
14422     getAutoCreate : function(){
14423         var cfg = {
14424             tag: 'div',
14425             cls: 'progress'
14426         };
14427         
14428         
14429         if(this.striped){
14430             cfg.cls += ' progress-striped';
14431         }
14432       
14433         if(this.active){
14434             cfg.cls += ' active';
14435         }
14436         
14437         
14438         return cfg;
14439     }
14440    
14441 });
14442
14443  
14444
14445  /*
14446  * - LGPL
14447  *
14448  * ProgressBar
14449  * 
14450  */
14451
14452 /**
14453  * @class Roo.bootstrap.ProgressBar
14454  * @extends Roo.bootstrap.Component
14455  * Bootstrap ProgressBar class
14456  * @cfg {Number} aria_valuenow aria-value now
14457  * @cfg {Number} aria_valuemin aria-value min
14458  * @cfg {Number} aria_valuemax aria-value max
14459  * @cfg {String} label label for the progress bar
14460  * @cfg {String} panel (success | info | warning | danger )
14461  * @cfg {String} role role of the progress bar
14462  * @cfg {String} sr_only text
14463  * 
14464  * 
14465  * @constructor
14466  * Create a new ProgressBar
14467  * @param {Object} config The config object
14468  */
14469
14470 Roo.bootstrap.ProgressBar = function(config){
14471     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14472 };
14473
14474 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14475     
14476     aria_valuenow : 0,
14477     aria_valuemin : 0,
14478     aria_valuemax : 100,
14479     label : false,
14480     panel : false,
14481     role : false,
14482     sr_only: false,
14483     
14484     getAutoCreate : function()
14485     {
14486         
14487         var cfg = {
14488             tag: 'div',
14489             cls: 'progress-bar',
14490             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14491         };
14492         
14493         if(this.sr_only){
14494             cfg.cn = {
14495                 tag: 'span',
14496                 cls: 'sr-only',
14497                 html: this.sr_only
14498             }
14499         }
14500         
14501         if(this.role){
14502             cfg.role = this.role;
14503         }
14504         
14505         if(this.aria_valuenow){
14506             cfg['aria-valuenow'] = this.aria_valuenow;
14507         }
14508         
14509         if(this.aria_valuemin){
14510             cfg['aria-valuemin'] = this.aria_valuemin;
14511         }
14512         
14513         if(this.aria_valuemax){
14514             cfg['aria-valuemax'] = this.aria_valuemax;
14515         }
14516         
14517         if(this.label && !this.sr_only){
14518             cfg.html = this.label;
14519         }
14520         
14521         if(this.panel){
14522             cfg.cls += ' progress-bar-' + this.panel;
14523         }
14524         
14525         return cfg;
14526     },
14527     
14528     update : function(aria_valuenow)
14529     {
14530         this.aria_valuenow = aria_valuenow;
14531         
14532         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14533     }
14534    
14535 });
14536
14537  
14538
14539  /*
14540  * - LGPL
14541  *
14542  * column
14543  * 
14544  */
14545
14546 /**
14547  * @class Roo.bootstrap.TabGroup
14548  * @extends Roo.bootstrap.Column
14549  * Bootstrap Column class
14550  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14551  * @cfg {Boolean} carousel true to make the group behave like a carousel
14552  * @cfg {Number} bullets show the panel pointer.. default 0
14553  * @cfg {Boolena} autoslide (true|false) auto slide .. default false
14554  * @cfg {Number} timer auto slide timer .. default 0 millisecond
14555  * 
14556  * @constructor
14557  * Create a new TabGroup
14558  * @param {Object} config The config object
14559  */
14560
14561 Roo.bootstrap.TabGroup = function(config){
14562     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14563     if (!this.navId) {
14564         this.navId = Roo.id();
14565     }
14566     this.tabs = [];
14567     Roo.bootstrap.TabGroup.register(this);
14568     
14569 };
14570
14571 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14572     
14573     carousel : false,
14574     transition : false,
14575     bullets : 0,
14576     timer : 0,
14577     autoslide : false,
14578     slideFn : false,
14579     
14580     getAutoCreate : function()
14581     {
14582         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14583         
14584         cfg.cls += ' tab-content';
14585         
14586         if (this.carousel) {
14587             cfg.cls += ' carousel slide';
14588             
14589             cfg.cn = [{
14590                cls : 'carousel-inner'
14591             }];
14592         
14593             if(this.bullets > 0){
14594                 
14595                 var bullets = {
14596                     cls : 'carousel-bullets',
14597                     cn : []
14598                 };
14599                 
14600                 for (var i = 0; i < this.bullets; i++){
14601                     bullets.cn.push({
14602                         cls : 'bullet bullet-' + i
14603                     });
14604                 }
14605                 
14606                 bullets.cn.push({
14607                     cls : 'clear'
14608                 });
14609                 
14610                 cfg.cn[0].cn = bullets;
14611             }
14612         }
14613         
14614         return cfg;
14615     },
14616     
14617     initEvents:  function()
14618     {
14619         Roo.log('-------- init events on tab group ---------');
14620         
14621         var _this = this;
14622         
14623         if(this.bullets > 0){
14624             
14625             for (var i = 0; i < this.bullets; i++){
14626                 var bullet = this.el.select('.bullet-' + i, true).first();
14627                 
14628                 if(!bullet){
14629                     continue;
14630                 }
14631                 
14632                 bullet.on('click', (function(e, el, o, ii, t){
14633                     
14634                     e.preventDefault();
14635                     
14636                     _this.showPanel(ii);
14637                     
14638                 }).createDelegate(this, [i, bullet], true));
14639                 
14640             }
14641         }
14642         
14643         if(this.autoslide){
14644             this.slideFn = window.setInterval(function() {
14645                 _this.showPanelNext();
14646             }, this.timer);
14647         }
14648     },
14649     
14650     getChildContainer : function()
14651     {
14652         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14653     },
14654     
14655     /**
14656     * register a Navigation item
14657     * @param {Roo.bootstrap.NavItem} the navitem to add
14658     */
14659     register : function(item)
14660     {
14661         this.tabs.push( item);
14662         item.navId = this.navId; // not really needed..
14663     
14664     },
14665     
14666     getActivePanel : function()
14667     {
14668         var r = false;
14669         Roo.each(this.tabs, function(t) {
14670             if (t.active) {
14671                 r = t;
14672                 return false;
14673             }
14674             return null;
14675         });
14676         return r;
14677         
14678     },
14679     getPanelByName : function(n)
14680     {
14681         var r = false;
14682         Roo.each(this.tabs, function(t) {
14683             if (t.tabId == n) {
14684                 r = t;
14685                 return false;
14686             }
14687             return null;
14688         });
14689         return r;
14690     },
14691     indexOfPanel : function(p)
14692     {
14693         var r = false;
14694         Roo.each(this.tabs, function(t,i) {
14695             if (t.tabId == p.tabId) {
14696                 r = i;
14697                 return false;
14698             }
14699             return null;
14700         });
14701         return r;
14702     },
14703     /**
14704      * show a specific panel
14705      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14706      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14707      */
14708     showPanel : function (pan)
14709     {
14710         if(this.transition){
14711             Roo.log("waiting for the transitionend");
14712             return;
14713         }
14714         
14715         if (typeof(pan) == 'number') {
14716             pan = this.tabs[pan];
14717         }
14718         if (typeof(pan) == 'string') {
14719             pan = this.getPanelByName(pan);
14720         }
14721         if (pan.tabId == this.getActivePanel().tabId) {
14722             return true;
14723         }
14724         var cur = this.getActivePanel();
14725         
14726         if (false === cur.fireEvent('beforedeactivate')) {
14727             return false;
14728         }
14729         
14730         if(this.bullets > 0){
14731             this.setActiveBullet(this.indexOfPanel(pan));
14732         }
14733         
14734         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
14735             
14736             this.transition = true;
14737             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14738             var lr = dir == 'next' ? 'left' : 'right';
14739             pan.el.addClass(dir); // or prev
14740             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14741             cur.el.addClass(lr); // or right
14742             pan.el.addClass(lr);
14743             
14744             var _this = this;
14745             cur.el.on('transitionend', function() {
14746                 Roo.log("trans end?");
14747                 
14748                 pan.el.removeClass([lr,dir]);
14749                 pan.setActive(true);
14750                 
14751                 cur.el.removeClass([lr]);
14752                 cur.setActive(false);
14753                 
14754                 _this.transition = false;
14755                 
14756             }, this, { single:  true } );
14757             
14758             return true;
14759         }
14760         
14761         cur.setActive(false);
14762         pan.setActive(true);
14763         
14764         return true;
14765         
14766     },
14767     showPanelNext : function()
14768     {
14769         var i = this.indexOfPanel(this.getActivePanel());
14770         
14771         if (i >= this.tabs.length - 1 && !this.autoslide) {
14772             return;
14773         }
14774         
14775         if (i >= this.tabs.length - 1 && this.autoslide) {
14776             i = -1;
14777         }
14778         
14779         this.showPanel(this.tabs[i+1]);
14780     },
14781     
14782     showPanelPrev : function()
14783     {
14784         var i = this.indexOfPanel(this.getActivePanel());
14785         
14786         if (i  < 1 && !this.autoslide) {
14787             return;
14788         }
14789         
14790         if (i < 1 && this.autoslide) {
14791             i = this.tabs.length;
14792         }
14793         
14794         this.showPanel(this.tabs[i-1]);
14795     },
14796     
14797     setActiveBullet : function(i)
14798     {
14799         Roo.each(this.el.select('.bullet', true).elements, function(el){
14800             el.removeClass('selected');
14801         });
14802
14803         var bullet = this.el.select('.bullet-' + i, true).first();
14804         
14805         if(!bullet){
14806             return;
14807         }
14808         
14809         bullet.addClass('selected');
14810     }
14811     
14812     
14813   
14814 });
14815
14816  
14817
14818  
14819  
14820 Roo.apply(Roo.bootstrap.TabGroup, {
14821     
14822     groups: {},
14823      /**
14824     * register a Navigation Group
14825     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14826     */
14827     register : function(navgrp)
14828     {
14829         this.groups[navgrp.navId] = navgrp;
14830         
14831     },
14832     /**
14833     * fetch a Navigation Group based on the navigation ID
14834     * if one does not exist , it will get created.
14835     * @param {string} the navgroup to add
14836     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14837     */
14838     get: function(navId) {
14839         if (typeof(this.groups[navId]) == 'undefined') {
14840             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14841         }
14842         return this.groups[navId] ;
14843     }
14844     
14845     
14846     
14847 });
14848
14849  /*
14850  * - LGPL
14851  *
14852  * TabPanel
14853  * 
14854  */
14855
14856 /**
14857  * @class Roo.bootstrap.TabPanel
14858  * @extends Roo.bootstrap.Component
14859  * Bootstrap TabPanel class
14860  * @cfg {Boolean} active panel active
14861  * @cfg {String} html panel content
14862  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14863  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14864  * 
14865  * 
14866  * @constructor
14867  * Create a new TabPanel
14868  * @param {Object} config The config object
14869  */
14870
14871 Roo.bootstrap.TabPanel = function(config){
14872     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14873     this.addEvents({
14874         /**
14875              * @event changed
14876              * Fires when the active status changes
14877              * @param {Roo.bootstrap.TabPanel} this
14878              * @param {Boolean} state the new state
14879             
14880          */
14881         'changed': true,
14882         /**
14883              * @event beforedeactivate
14884              * Fires before a tab is de-activated - can be used to do validation on a form.
14885              * @param {Roo.bootstrap.TabPanel} this
14886              * @return {Boolean} false if there is an error
14887             
14888          */
14889         'beforedeactivate': true
14890      });
14891     
14892     this.tabId = this.tabId || Roo.id();
14893   
14894 };
14895
14896 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14897     
14898     active: false,
14899     html: false,
14900     tabId: false,
14901     navId : false,
14902     
14903     getAutoCreate : function(){
14904         var cfg = {
14905             tag: 'div',
14906             // item is needed for carousel - not sure if it has any effect otherwise
14907             cls: 'tab-pane item',
14908             html: this.html || ''
14909         };
14910         
14911         if(this.active){
14912             cfg.cls += ' active';
14913         }
14914         
14915         if(this.tabId){
14916             cfg.tabId = this.tabId;
14917         }
14918         
14919         
14920         return cfg;
14921     },
14922     
14923     initEvents:  function()
14924     {
14925         Roo.log('-------- init events on tab panel ---------');
14926         
14927         var p = this.parent();
14928         this.navId = this.navId || p.navId;
14929         
14930         if (typeof(this.navId) != 'undefined') {
14931             // not really needed.. but just in case.. parent should be a NavGroup.
14932             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14933             Roo.log(['register', tg, this]);
14934             tg.register(this);
14935             
14936             var i = tg.tabs.length - 1;
14937             
14938             if(this.active && tg.bullets > 0 && i < tg.bullets){
14939                 tg.setActiveBullet(i);
14940             }
14941         }
14942         
14943     },
14944     
14945     
14946     onRender : function(ct, position)
14947     {
14948        // Roo.log("Call onRender: " + this.xtype);
14949         
14950         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14951         
14952         
14953         
14954         
14955         
14956     },
14957     
14958     setActive: function(state)
14959     {
14960         Roo.log("panel - set active " + this.tabId + "=" + state);
14961         
14962         this.active = state;
14963         if (!state) {
14964             this.el.removeClass('active');
14965             
14966         } else  if (!this.el.hasClass('active')) {
14967             this.el.addClass('active');
14968         }
14969         
14970         this.fireEvent('changed', this, state);
14971     }
14972     
14973     
14974 });
14975  
14976
14977  
14978
14979  /*
14980  * - LGPL
14981  *
14982  * DateField
14983  * 
14984  */
14985
14986 /**
14987  * @class Roo.bootstrap.DateField
14988  * @extends Roo.bootstrap.Input
14989  * Bootstrap DateField class
14990  * @cfg {Number} weekStart default 0
14991  * @cfg {String} viewMode default empty, (months|years)
14992  * @cfg {String} minViewMode default empty, (months|years)
14993  * @cfg {Number} startDate default -Infinity
14994  * @cfg {Number} endDate default Infinity
14995  * @cfg {Boolean} todayHighlight default false
14996  * @cfg {Boolean} todayBtn default false
14997  * @cfg {Boolean} calendarWeeks default false
14998  * @cfg {Object} daysOfWeekDisabled default empty
14999  * @cfg {Boolean} singleMode default false (true | false)
15000  * 
15001  * @cfg {Boolean} keyboardNavigation default true
15002  * @cfg {String} language default en
15003  * 
15004  * @constructor
15005  * Create a new DateField
15006  * @param {Object} config The config object
15007  */
15008
15009 Roo.bootstrap.DateField = function(config){
15010     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
15011      this.addEvents({
15012             /**
15013              * @event show
15014              * Fires when this field show.
15015              * @param {Roo.bootstrap.DateField} this
15016              * @param {Mixed} date The date value
15017              */
15018             show : true,
15019             /**
15020              * @event show
15021              * Fires when this field hide.
15022              * @param {Roo.bootstrap.DateField} this
15023              * @param {Mixed} date The date value
15024              */
15025             hide : true,
15026             /**
15027              * @event select
15028              * Fires when select a date.
15029              * @param {Roo.bootstrap.DateField} this
15030              * @param {Mixed} date The date value
15031              */
15032             select : true
15033         });
15034 };
15035
15036 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
15037     
15038     /**
15039      * @cfg {String} format
15040      * The default date format string which can be overriden for localization support.  The format must be
15041      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
15042      */
15043     format : "m/d/y",
15044     /**
15045      * @cfg {String} altFormats
15046      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
15047      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
15048      */
15049     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
15050     
15051     weekStart : 0,
15052     
15053     viewMode : '',
15054     
15055     minViewMode : '',
15056     
15057     todayHighlight : false,
15058     
15059     todayBtn: false,
15060     
15061     language: 'en',
15062     
15063     keyboardNavigation: true,
15064     
15065     calendarWeeks: false,
15066     
15067     startDate: -Infinity,
15068     
15069     endDate: Infinity,
15070     
15071     daysOfWeekDisabled: [],
15072     
15073     _events: [],
15074     
15075     singleMode : false,
15076     
15077     UTCDate: function()
15078     {
15079         return new Date(Date.UTC.apply(Date, arguments));
15080     },
15081     
15082     UTCToday: function()
15083     {
15084         var today = new Date();
15085         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
15086     },
15087     
15088     getDate: function() {
15089             var d = this.getUTCDate();
15090             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
15091     },
15092     
15093     getUTCDate: function() {
15094             return this.date;
15095     },
15096     
15097     setDate: function(d) {
15098             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
15099     },
15100     
15101     setUTCDate: function(d) {
15102             this.date = d;
15103             this.setValue(this.formatDate(this.date));
15104     },
15105         
15106     onRender: function(ct, position)
15107     {
15108         
15109         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
15110         
15111         this.language = this.language || 'en';
15112         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
15113         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
15114         
15115         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
15116         this.format = this.format || 'm/d/y';
15117         this.isInline = false;
15118         this.isInput = true;
15119         this.component = this.el.select('.add-on', true).first() || false;
15120         this.component = (this.component && this.component.length === 0) ? false : this.component;
15121         this.hasInput = this.component && this.inputEL().length;
15122         
15123         if (typeof(this.minViewMode === 'string')) {
15124             switch (this.minViewMode) {
15125                 case 'months':
15126                     this.minViewMode = 1;
15127                     break;
15128                 case 'years':
15129                     this.minViewMode = 2;
15130                     break;
15131                 default:
15132                     this.minViewMode = 0;
15133                     break;
15134             }
15135         }
15136         
15137         if (typeof(this.viewMode === 'string')) {
15138             switch (this.viewMode) {
15139                 case 'months':
15140                     this.viewMode = 1;
15141                     break;
15142                 case 'years':
15143                     this.viewMode = 2;
15144                     break;
15145                 default:
15146                     this.viewMode = 0;
15147                     break;
15148             }
15149         }
15150                 
15151         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
15152         
15153 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
15154         
15155         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15156         
15157         this.picker().on('mousedown', this.onMousedown, this);
15158         this.picker().on('click', this.onClick, this);
15159         
15160         this.picker().addClass('datepicker-dropdown');
15161         
15162         this.startViewMode = this.viewMode;
15163         
15164         if(this.singleMode){
15165             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
15166                 v.setVisibilityMode(Roo.Element.DISPLAY)
15167                 v.hide();
15168             });
15169             
15170             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
15171                 v.setStyle('width', '189px');
15172             });
15173         }
15174         
15175         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
15176             if(!this.calendarWeeks){
15177                 v.remove();
15178                 return;
15179             }
15180             
15181             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15182             v.attr('colspan', function(i, val){
15183                 return parseInt(val) + 1;
15184             });
15185         })
15186                         
15187         
15188         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
15189         
15190         this.setStartDate(this.startDate);
15191         this.setEndDate(this.endDate);
15192         
15193         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
15194         
15195         this.fillDow();
15196         this.fillMonths();
15197         this.update();
15198         this.showMode();
15199         
15200         if(this.isInline) {
15201             this.show();
15202         }
15203     },
15204     
15205     picker : function()
15206     {
15207         return this.pickerEl;
15208 //        return this.el.select('.datepicker', true).first();
15209     },
15210     
15211     fillDow: function()
15212     {
15213         var dowCnt = this.weekStart;
15214         
15215         var dow = {
15216             tag: 'tr',
15217             cn: [
15218                 
15219             ]
15220         };
15221         
15222         if(this.calendarWeeks){
15223             dow.cn.push({
15224                 tag: 'th',
15225                 cls: 'cw',
15226                 html: '&nbsp;'
15227             })
15228         }
15229         
15230         while (dowCnt < this.weekStart + 7) {
15231             dow.cn.push({
15232                 tag: 'th',
15233                 cls: 'dow',
15234                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
15235             });
15236         }
15237         
15238         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
15239     },
15240     
15241     fillMonths: function()
15242     {    
15243         var i = 0;
15244         var months = this.picker().select('>.datepicker-months td', true).first();
15245         
15246         months.dom.innerHTML = '';
15247         
15248         while (i < 12) {
15249             var month = {
15250                 tag: 'span',
15251                 cls: 'month',
15252                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
15253             }
15254             
15255             months.createChild(month);
15256         }
15257         
15258     },
15259     
15260     update: function()
15261     {
15262         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;
15263         
15264         if (this.date < this.startDate) {
15265             this.viewDate = new Date(this.startDate);
15266         } else if (this.date > this.endDate) {
15267             this.viewDate = new Date(this.endDate);
15268         } else {
15269             this.viewDate = new Date(this.date);
15270         }
15271         
15272         this.fill();
15273     },
15274     
15275     fill: function() 
15276     {
15277         var d = new Date(this.viewDate),
15278                 year = d.getUTCFullYear(),
15279                 month = d.getUTCMonth(),
15280                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
15281                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
15282                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
15283                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
15284                 currentDate = this.date && this.date.valueOf(),
15285                 today = this.UTCToday();
15286         
15287         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
15288         
15289 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15290         
15291 //        this.picker.select('>tfoot th.today').
15292 //                                              .text(dates[this.language].today)
15293 //                                              .toggle(this.todayBtn !== false);
15294     
15295         this.updateNavArrows();
15296         this.fillMonths();
15297                                                 
15298         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
15299         
15300         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
15301          
15302         prevMonth.setUTCDate(day);
15303         
15304         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
15305         
15306         var nextMonth = new Date(prevMonth);
15307         
15308         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
15309         
15310         nextMonth = nextMonth.valueOf();
15311         
15312         var fillMonths = false;
15313         
15314         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
15315         
15316         while(prevMonth.valueOf() < nextMonth) {
15317             var clsName = '';
15318             
15319             if (prevMonth.getUTCDay() === this.weekStart) {
15320                 if(fillMonths){
15321                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
15322                 }
15323                     
15324                 fillMonths = {
15325                     tag: 'tr',
15326                     cn: []
15327                 };
15328                 
15329                 if(this.calendarWeeks){
15330                     // ISO 8601: First week contains first thursday.
15331                     // ISO also states week starts on Monday, but we can be more abstract here.
15332                     var
15333                     // Start of current week: based on weekstart/current date
15334                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
15335                     // Thursday of this week
15336                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
15337                     // First Thursday of year, year from thursday
15338                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
15339                     // Calendar week: ms between thursdays, div ms per day, div 7 days
15340                     calWeek =  (th - yth) / 864e5 / 7 + 1;
15341                     
15342                     fillMonths.cn.push({
15343                         tag: 'td',
15344                         cls: 'cw',
15345                         html: calWeek
15346                     });
15347                 }
15348             }
15349             
15350             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
15351                 clsName += ' old';
15352             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
15353                 clsName += ' new';
15354             }
15355             if (this.todayHighlight &&
15356                 prevMonth.getUTCFullYear() == today.getFullYear() &&
15357                 prevMonth.getUTCMonth() == today.getMonth() &&
15358                 prevMonth.getUTCDate() == today.getDate()) {
15359                 clsName += ' today';
15360             }
15361             
15362             if (currentDate && prevMonth.valueOf() === currentDate) {
15363                 clsName += ' active';
15364             }
15365             
15366             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
15367                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
15368                     clsName += ' disabled';
15369             }
15370             
15371             fillMonths.cn.push({
15372                 tag: 'td',
15373                 cls: 'day ' + clsName,
15374                 html: prevMonth.getDate()
15375             })
15376             
15377             prevMonth.setDate(prevMonth.getDate()+1);
15378         }
15379           
15380         var currentYear = this.date && this.date.getUTCFullYear();
15381         var currentMonth = this.date && this.date.getUTCMonth();
15382         
15383         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
15384         
15385         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
15386             v.removeClass('active');
15387             
15388             if(currentYear === year && k === currentMonth){
15389                 v.addClass('active');
15390             }
15391             
15392             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
15393                 v.addClass('disabled');
15394             }
15395             
15396         });
15397         
15398         
15399         year = parseInt(year/10, 10) * 10;
15400         
15401         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
15402         
15403         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
15404         
15405         year -= 1;
15406         for (var i = -1; i < 11; i++) {
15407             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
15408                 tag: 'span',
15409                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
15410                 html: year
15411             })
15412             
15413             year += 1;
15414         }
15415     },
15416     
15417     showMode: function(dir) 
15418     {
15419         if (dir) {
15420             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15421         }
15422         
15423         Roo.each(this.picker().select('>div',true).elements, function(v){
15424             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15425             v.hide();
15426         });
15427         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15428     },
15429     
15430     place: function()
15431     {
15432         if(this.isInline) return;
15433         
15434         this.picker().removeClass(['bottom', 'top']);
15435         
15436         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15437             /*
15438              * place to the top of element!
15439              *
15440              */
15441             
15442             this.picker().addClass('top');
15443             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15444             
15445             return;
15446         }
15447         
15448         this.picker().addClass('bottom');
15449         
15450         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15451     },
15452     
15453     parseDate : function(value)
15454     {
15455         if(!value || value instanceof Date){
15456             return value;
15457         }
15458         var v = Date.parseDate(value, this.format);
15459         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15460             v = Date.parseDate(value, 'Y-m-d');
15461         }
15462         if(!v && this.altFormats){
15463             if(!this.altFormatsArray){
15464                 this.altFormatsArray = this.altFormats.split("|");
15465             }
15466             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15467                 v = Date.parseDate(value, this.altFormatsArray[i]);
15468             }
15469         }
15470         return v;
15471     },
15472     
15473     formatDate : function(date, fmt)
15474     {   
15475         return (!date || !(date instanceof Date)) ?
15476         date : date.dateFormat(fmt || this.format);
15477     },
15478     
15479     onFocus : function()
15480     {
15481         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15482         this.show();
15483     },
15484     
15485     onBlur : function()
15486     {
15487         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15488         
15489         var d = this.inputEl().getValue();
15490         
15491         this.setValue(d);
15492                 
15493         this.hide();
15494     },
15495     
15496     show : function()
15497     {
15498         this.picker().show();
15499         this.update();
15500         this.place();
15501         
15502         this.fireEvent('show', this, this.date);
15503     },
15504     
15505     hide : function()
15506     {
15507         if(this.isInline) return;
15508         this.picker().hide();
15509         this.viewMode = this.startViewMode;
15510         this.showMode();
15511         
15512         this.fireEvent('hide', this, this.date);
15513         
15514     },
15515     
15516     onMousedown: function(e)
15517     {
15518         e.stopPropagation();
15519         e.preventDefault();
15520     },
15521     
15522     keyup: function(e)
15523     {
15524         Roo.bootstrap.DateField.superclass.keyup.call(this);
15525         this.update();
15526     },
15527
15528     setValue: function(v)
15529     {
15530         
15531         // v can be a string or a date..
15532         
15533         
15534         var d = new Date(this.parseDate(v) ).clearTime();
15535         
15536         if(isNaN(d.getTime())){
15537             this.date = this.viewDate = '';
15538             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15539             return;
15540         }
15541         
15542         v = this.formatDate(d);
15543         
15544         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15545         
15546         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15547      
15548         this.update();
15549
15550         this.fireEvent('select', this, this.date);
15551         
15552     },
15553     
15554     getValue: function()
15555     {
15556         return this.formatDate(this.date);
15557     },
15558     
15559     fireKey: function(e)
15560     {
15561         if (!this.picker().isVisible()){
15562             if (e.keyCode == 27) // allow escape to hide and re-show picker
15563                 this.show();
15564             return;
15565         }
15566         
15567         var dateChanged = false,
15568         dir, day, month,
15569         newDate, newViewDate;
15570         
15571         switch(e.keyCode){
15572             case 27: // escape
15573                 this.hide();
15574                 e.preventDefault();
15575                 break;
15576             case 37: // left
15577             case 39: // right
15578                 if (!this.keyboardNavigation) break;
15579                 dir = e.keyCode == 37 ? -1 : 1;
15580                 
15581                 if (e.ctrlKey){
15582                     newDate = this.moveYear(this.date, dir);
15583                     newViewDate = this.moveYear(this.viewDate, dir);
15584                 } else if (e.shiftKey){
15585                     newDate = this.moveMonth(this.date, dir);
15586                     newViewDate = this.moveMonth(this.viewDate, dir);
15587                 } else {
15588                     newDate = new Date(this.date);
15589                     newDate.setUTCDate(this.date.getUTCDate() + dir);
15590                     newViewDate = new Date(this.viewDate);
15591                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15592                 }
15593                 if (this.dateWithinRange(newDate)){
15594                     this.date = newDate;
15595                     this.viewDate = newViewDate;
15596                     this.setValue(this.formatDate(this.date));
15597 //                    this.update();
15598                     e.preventDefault();
15599                     dateChanged = true;
15600                 }
15601                 break;
15602             case 38: // up
15603             case 40: // down
15604                 if (!this.keyboardNavigation) break;
15605                 dir = e.keyCode == 38 ? -1 : 1;
15606                 if (e.ctrlKey){
15607                     newDate = this.moveYear(this.date, dir);
15608                     newViewDate = this.moveYear(this.viewDate, dir);
15609                 } else if (e.shiftKey){
15610                     newDate = this.moveMonth(this.date, dir);
15611                     newViewDate = this.moveMonth(this.viewDate, dir);
15612                 } else {
15613                     newDate = new Date(this.date);
15614                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15615                     newViewDate = new Date(this.viewDate);
15616                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15617                 }
15618                 if (this.dateWithinRange(newDate)){
15619                     this.date = newDate;
15620                     this.viewDate = newViewDate;
15621                     this.setValue(this.formatDate(this.date));
15622 //                    this.update();
15623                     e.preventDefault();
15624                     dateChanged = true;
15625                 }
15626                 break;
15627             case 13: // enter
15628                 this.setValue(this.formatDate(this.date));
15629                 this.hide();
15630                 e.preventDefault();
15631                 break;
15632             case 9: // tab
15633                 this.setValue(this.formatDate(this.date));
15634                 this.hide();
15635                 break;
15636             case 16: // shift
15637             case 17: // ctrl
15638             case 18: // alt
15639                 break;
15640             default :
15641                 this.hide();
15642                 
15643         }
15644     },
15645     
15646     
15647     onClick: function(e) 
15648     {
15649         e.stopPropagation();
15650         e.preventDefault();
15651         
15652         var target = e.getTarget();
15653         
15654         if(target.nodeName.toLowerCase() === 'i'){
15655             target = Roo.get(target).dom.parentNode;
15656         }
15657         
15658         var nodeName = target.nodeName;
15659         var className = target.className;
15660         var html = target.innerHTML;
15661         //Roo.log(nodeName);
15662         
15663         switch(nodeName.toLowerCase()) {
15664             case 'th':
15665                 switch(className) {
15666                     case 'switch':
15667                         this.showMode(1);
15668                         break;
15669                     case 'prev':
15670                     case 'next':
15671                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15672                         switch(this.viewMode){
15673                                 case 0:
15674                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15675                                         break;
15676                                 case 1:
15677                                 case 2:
15678                                         this.viewDate = this.moveYear(this.viewDate, dir);
15679                                         break;
15680                         }
15681                         this.fill();
15682                         break;
15683                     case 'today':
15684                         var date = new Date();
15685                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15686 //                        this.fill()
15687                         this.setValue(this.formatDate(this.date));
15688                         
15689                         this.hide();
15690                         break;
15691                 }
15692                 break;
15693             case 'span':
15694                 if (className.indexOf('disabled') < 0) {
15695                     this.viewDate.setUTCDate(1);
15696                     if (className.indexOf('month') > -1) {
15697                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15698                     } else {
15699                         var year = parseInt(html, 10) || 0;
15700                         this.viewDate.setUTCFullYear(year);
15701                         
15702                     }
15703                     
15704                     if(this.singleMode){
15705                         this.setValue(this.formatDate(this.viewDate));
15706                         this.hide();
15707                         return;
15708                     }
15709                     
15710                     this.showMode(-1);
15711                     this.fill();
15712                 }
15713                 break;
15714                 
15715             case 'td':
15716                 //Roo.log(className);
15717                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15718                     var day = parseInt(html, 10) || 1;
15719                     var year = this.viewDate.getUTCFullYear(),
15720                         month = this.viewDate.getUTCMonth();
15721
15722                     if (className.indexOf('old') > -1) {
15723                         if(month === 0 ){
15724                             month = 11;
15725                             year -= 1;
15726                         }else{
15727                             month -= 1;
15728                         }
15729                     } else if (className.indexOf('new') > -1) {
15730                         if (month == 11) {
15731                             month = 0;
15732                             year += 1;
15733                         } else {
15734                             month += 1;
15735                         }
15736                     }
15737                     //Roo.log([year,month,day]);
15738                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15739                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15740 //                    this.fill();
15741                     //Roo.log(this.formatDate(this.date));
15742                     this.setValue(this.formatDate(this.date));
15743                     this.hide();
15744                 }
15745                 break;
15746         }
15747     },
15748     
15749     setStartDate: function(startDate)
15750     {
15751         this.startDate = startDate || -Infinity;
15752         if (this.startDate !== -Infinity) {
15753             this.startDate = this.parseDate(this.startDate);
15754         }
15755         this.update();
15756         this.updateNavArrows();
15757     },
15758
15759     setEndDate: function(endDate)
15760     {
15761         this.endDate = endDate || Infinity;
15762         if (this.endDate !== Infinity) {
15763             this.endDate = this.parseDate(this.endDate);
15764         }
15765         this.update();
15766         this.updateNavArrows();
15767     },
15768     
15769     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15770     {
15771         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15772         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15773             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15774         }
15775         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15776             return parseInt(d, 10);
15777         });
15778         this.update();
15779         this.updateNavArrows();
15780     },
15781     
15782     updateNavArrows: function() 
15783     {
15784         if(this.singleMode){
15785             return;
15786         }
15787         
15788         var d = new Date(this.viewDate),
15789         year = d.getUTCFullYear(),
15790         month = d.getUTCMonth();
15791         
15792         Roo.each(this.picker().select('.prev', true).elements, function(v){
15793             v.show();
15794             switch (this.viewMode) {
15795                 case 0:
15796
15797                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15798                         v.hide();
15799                     }
15800                     break;
15801                 case 1:
15802                 case 2:
15803                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15804                         v.hide();
15805                     }
15806                     break;
15807             }
15808         });
15809         
15810         Roo.each(this.picker().select('.next', true).elements, function(v){
15811             v.show();
15812             switch (this.viewMode) {
15813                 case 0:
15814
15815                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15816                         v.hide();
15817                     }
15818                     break;
15819                 case 1:
15820                 case 2:
15821                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15822                         v.hide();
15823                     }
15824                     break;
15825             }
15826         })
15827     },
15828     
15829     moveMonth: function(date, dir)
15830     {
15831         if (!dir) return date;
15832         var new_date = new Date(date.valueOf()),
15833         day = new_date.getUTCDate(),
15834         month = new_date.getUTCMonth(),
15835         mag = Math.abs(dir),
15836         new_month, test;
15837         dir = dir > 0 ? 1 : -1;
15838         if (mag == 1){
15839             test = dir == -1
15840             // If going back one month, make sure month is not current month
15841             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15842             ? function(){
15843                 return new_date.getUTCMonth() == month;
15844             }
15845             // If going forward one month, make sure month is as expected
15846             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15847             : function(){
15848                 return new_date.getUTCMonth() != new_month;
15849             };
15850             new_month = month + dir;
15851             new_date.setUTCMonth(new_month);
15852             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15853             if (new_month < 0 || new_month > 11)
15854                 new_month = (new_month + 12) % 12;
15855         } else {
15856             // For magnitudes >1, move one month at a time...
15857             for (var i=0; i<mag; i++)
15858                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15859                 new_date = this.moveMonth(new_date, dir);
15860             // ...then reset the day, keeping it in the new month
15861             new_month = new_date.getUTCMonth();
15862             new_date.setUTCDate(day);
15863             test = function(){
15864                 return new_month != new_date.getUTCMonth();
15865             };
15866         }
15867         // Common date-resetting loop -- if date is beyond end of month, make it
15868         // end of month
15869         while (test()){
15870             new_date.setUTCDate(--day);
15871             new_date.setUTCMonth(new_month);
15872         }
15873         return new_date;
15874     },
15875
15876     moveYear: function(date, dir)
15877     {
15878         return this.moveMonth(date, dir*12);
15879     },
15880
15881     dateWithinRange: function(date)
15882     {
15883         return date >= this.startDate && date <= this.endDate;
15884     },
15885
15886     
15887     remove: function() 
15888     {
15889         this.picker().remove();
15890     }
15891    
15892 });
15893
15894 Roo.apply(Roo.bootstrap.DateField,  {
15895     
15896     head : {
15897         tag: 'thead',
15898         cn: [
15899         {
15900             tag: 'tr',
15901             cn: [
15902             {
15903                 tag: 'th',
15904                 cls: 'prev',
15905                 html: '<i class="fa fa-arrow-left"/>'
15906             },
15907             {
15908                 tag: 'th',
15909                 cls: 'switch',
15910                 colspan: '5'
15911             },
15912             {
15913                 tag: 'th',
15914                 cls: 'next',
15915                 html: '<i class="fa fa-arrow-right"/>'
15916             }
15917
15918             ]
15919         }
15920         ]
15921     },
15922     
15923     content : {
15924         tag: 'tbody',
15925         cn: [
15926         {
15927             tag: 'tr',
15928             cn: [
15929             {
15930                 tag: 'td',
15931                 colspan: '7'
15932             }
15933             ]
15934         }
15935         ]
15936     },
15937     
15938     footer : {
15939         tag: 'tfoot',
15940         cn: [
15941         {
15942             tag: 'tr',
15943             cn: [
15944             {
15945                 tag: 'th',
15946                 colspan: '7',
15947                 cls: 'today'
15948             }
15949                     
15950             ]
15951         }
15952         ]
15953     },
15954     
15955     dates:{
15956         en: {
15957             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15958             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15959             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15960             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15961             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15962             today: "Today"
15963         }
15964     },
15965     
15966     modes: [
15967     {
15968         clsName: 'days',
15969         navFnc: 'Month',
15970         navStep: 1
15971     },
15972     {
15973         clsName: 'months',
15974         navFnc: 'FullYear',
15975         navStep: 1
15976     },
15977     {
15978         clsName: 'years',
15979         navFnc: 'FullYear',
15980         navStep: 10
15981     }]
15982 });
15983
15984 Roo.apply(Roo.bootstrap.DateField,  {
15985   
15986     template : {
15987         tag: 'div',
15988         cls: 'datepicker dropdown-menu roo-dynamic',
15989         cn: [
15990         {
15991             tag: 'div',
15992             cls: 'datepicker-days',
15993             cn: [
15994             {
15995                 tag: 'table',
15996                 cls: 'table-condensed',
15997                 cn:[
15998                 Roo.bootstrap.DateField.head,
15999                 {
16000                     tag: 'tbody'
16001                 },
16002                 Roo.bootstrap.DateField.footer
16003                 ]
16004             }
16005             ]
16006         },
16007         {
16008             tag: 'div',
16009             cls: 'datepicker-months',
16010             cn: [
16011             {
16012                 tag: 'table',
16013                 cls: 'table-condensed',
16014                 cn:[
16015                 Roo.bootstrap.DateField.head,
16016                 Roo.bootstrap.DateField.content,
16017                 Roo.bootstrap.DateField.footer
16018                 ]
16019             }
16020             ]
16021         },
16022         {
16023             tag: 'div',
16024             cls: 'datepicker-years',
16025             cn: [
16026             {
16027                 tag: 'table',
16028                 cls: 'table-condensed',
16029                 cn:[
16030                 Roo.bootstrap.DateField.head,
16031                 Roo.bootstrap.DateField.content,
16032                 Roo.bootstrap.DateField.footer
16033                 ]
16034             }
16035             ]
16036         }
16037         ]
16038     }
16039 });
16040
16041  
16042
16043  /*
16044  * - LGPL
16045  *
16046  * TimeField
16047  * 
16048  */
16049
16050 /**
16051  * @class Roo.bootstrap.TimeField
16052  * @extends Roo.bootstrap.Input
16053  * Bootstrap DateField class
16054  * 
16055  * 
16056  * @constructor
16057  * Create a new TimeField
16058  * @param {Object} config The config object
16059  */
16060
16061 Roo.bootstrap.TimeField = function(config){
16062     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
16063     this.addEvents({
16064             /**
16065              * @event show
16066              * Fires when this field show.
16067              * @param {Roo.bootstrap.DateField} thisthis
16068              * @param {Mixed} date The date value
16069              */
16070             show : true,
16071             /**
16072              * @event show
16073              * Fires when this field hide.
16074              * @param {Roo.bootstrap.DateField} this
16075              * @param {Mixed} date The date value
16076              */
16077             hide : true,
16078             /**
16079              * @event select
16080              * Fires when select a date.
16081              * @param {Roo.bootstrap.DateField} this
16082              * @param {Mixed} date The date value
16083              */
16084             select : true
16085         });
16086 };
16087
16088 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
16089     
16090     /**
16091      * @cfg {String} format
16092      * The default time format string which can be overriden for localization support.  The format must be
16093      * valid according to {@link Date#parseDate} (defaults to 'H:i').
16094      */
16095     format : "H:i",
16096        
16097     onRender: function(ct, position)
16098     {
16099         
16100         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
16101                 
16102         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
16103         
16104         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16105         
16106         this.pop = this.picker().select('>.datepicker-time',true).first();
16107         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16108         
16109         this.picker().on('mousedown', this.onMousedown, this);
16110         this.picker().on('click', this.onClick, this);
16111         
16112         this.picker().addClass('datepicker-dropdown');
16113     
16114         this.fillTime();
16115         this.update();
16116             
16117         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
16118         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
16119         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
16120         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
16121         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
16122         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
16123
16124     },
16125     
16126     fireKey: function(e){
16127         if (!this.picker().isVisible()){
16128             if (e.keyCode == 27) { // allow escape to hide and re-show picker
16129                 this.show();
16130             }
16131             return;
16132         }
16133
16134         e.preventDefault();
16135         
16136         switch(e.keyCode){
16137             case 27: // escape
16138                 this.hide();
16139                 break;
16140             case 37: // left
16141             case 39: // right
16142                 this.onTogglePeriod();
16143                 break;
16144             case 38: // up
16145                 this.onIncrementMinutes();
16146                 break;
16147             case 40: // down
16148                 this.onDecrementMinutes();
16149                 break;
16150             case 13: // enter
16151             case 9: // tab
16152                 this.setTime();
16153                 break;
16154         }
16155     },
16156     
16157     onClick: function(e) {
16158         e.stopPropagation();
16159         e.preventDefault();
16160     },
16161     
16162     picker : function()
16163     {
16164         return this.el.select('.datepicker', true).first();
16165     },
16166     
16167     fillTime: function()
16168     {    
16169         var time = this.pop.select('tbody', true).first();
16170         
16171         time.dom.innerHTML = '';
16172         
16173         time.createChild({
16174             tag: 'tr',
16175             cn: [
16176                 {
16177                     tag: 'td',
16178                     cn: [
16179                         {
16180                             tag: 'a',
16181                             href: '#',
16182                             cls: 'btn',
16183                             cn: [
16184                                 {
16185                                     tag: 'span',
16186                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
16187                                 }
16188                             ]
16189                         } 
16190                     ]
16191                 },
16192                 {
16193                     tag: 'td',
16194                     cls: 'separator'
16195                 },
16196                 {
16197                     tag: 'td',
16198                     cn: [
16199                         {
16200                             tag: 'a',
16201                             href: '#',
16202                             cls: 'btn',
16203                             cn: [
16204                                 {
16205                                     tag: 'span',
16206                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
16207                                 }
16208                             ]
16209                         }
16210                     ]
16211                 },
16212                 {
16213                     tag: 'td',
16214                     cls: 'separator'
16215                 }
16216             ]
16217         });
16218         
16219         time.createChild({
16220             tag: 'tr',
16221             cn: [
16222                 {
16223                     tag: 'td',
16224                     cn: [
16225                         {
16226                             tag: 'span',
16227                             cls: 'timepicker-hour',
16228                             html: '00'
16229                         }  
16230                     ]
16231                 },
16232                 {
16233                     tag: 'td',
16234                     cls: 'separator',
16235                     html: ':'
16236                 },
16237                 {
16238                     tag: 'td',
16239                     cn: [
16240                         {
16241                             tag: 'span',
16242                             cls: 'timepicker-minute',
16243                             html: '00'
16244                         }  
16245                     ]
16246                 },
16247                 {
16248                     tag: 'td',
16249                     cls: 'separator'
16250                 },
16251                 {
16252                     tag: 'td',
16253                     cn: [
16254                         {
16255                             tag: 'button',
16256                             type: 'button',
16257                             cls: 'btn btn-primary period',
16258                             html: 'AM'
16259                             
16260                         }
16261                     ]
16262                 }
16263             ]
16264         });
16265         
16266         time.createChild({
16267             tag: 'tr',
16268             cn: [
16269                 {
16270                     tag: 'td',
16271                     cn: [
16272                         {
16273                             tag: 'a',
16274                             href: '#',
16275                             cls: 'btn',
16276                             cn: [
16277                                 {
16278                                     tag: 'span',
16279                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
16280                                 }
16281                             ]
16282                         }
16283                     ]
16284                 },
16285                 {
16286                     tag: 'td',
16287                     cls: 'separator'
16288                 },
16289                 {
16290                     tag: 'td',
16291                     cn: [
16292                         {
16293                             tag: 'a',
16294                             href: '#',
16295                             cls: 'btn',
16296                             cn: [
16297                                 {
16298                                     tag: 'span',
16299                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
16300                                 }
16301                             ]
16302                         }
16303                     ]
16304                 },
16305                 {
16306                     tag: 'td',
16307                     cls: 'separator'
16308                 }
16309             ]
16310         });
16311         
16312     },
16313     
16314     update: function()
16315     {
16316         
16317         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
16318         
16319         this.fill();
16320     },
16321     
16322     fill: function() 
16323     {
16324         var hours = this.time.getHours();
16325         var minutes = this.time.getMinutes();
16326         var period = 'AM';
16327         
16328         if(hours > 11){
16329             period = 'PM';
16330         }
16331         
16332         if(hours == 0){
16333             hours = 12;
16334         }
16335         
16336         
16337         if(hours > 12){
16338             hours = hours - 12;
16339         }
16340         
16341         if(hours < 10){
16342             hours = '0' + hours;
16343         }
16344         
16345         if(minutes < 10){
16346             minutes = '0' + minutes;
16347         }
16348         
16349         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
16350         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
16351         this.pop.select('button', true).first().dom.innerHTML = period;
16352         
16353     },
16354     
16355     place: function()
16356     {   
16357         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
16358         
16359         var cls = ['bottom'];
16360         
16361         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
16362             cls.pop();
16363             cls.push('top');
16364         }
16365         
16366         cls.push('right');
16367         
16368         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
16369             cls.pop();
16370             cls.push('left');
16371         }
16372         
16373         this.picker().addClass(cls.join('-'));
16374         
16375         var _this = this;
16376         
16377         Roo.each(cls, function(c){
16378             if(c == 'bottom'){
16379                 _this.picker().setTop(_this.inputEl().getHeight());
16380                 return;
16381             }
16382             if(c == 'top'){
16383                 _this.picker().setTop(0 - _this.picker().getHeight());
16384                 return;
16385             }
16386             
16387             if(c == 'left'){
16388                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
16389                 return;
16390             }
16391             if(c == 'right'){
16392                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
16393                 return;
16394             }
16395         });
16396         
16397     },
16398   
16399     onFocus : function()
16400     {
16401         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
16402         this.show();
16403     },
16404     
16405     onBlur : function()
16406     {
16407         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
16408         this.hide();
16409     },
16410     
16411     show : function()
16412     {
16413         this.picker().show();
16414         this.pop.show();
16415         this.update();
16416         this.place();
16417         
16418         this.fireEvent('show', this, this.date);
16419     },
16420     
16421     hide : function()
16422     {
16423         this.picker().hide();
16424         this.pop.hide();
16425         
16426         this.fireEvent('hide', this, this.date);
16427     },
16428     
16429     setTime : function()
16430     {
16431         this.hide();
16432         this.setValue(this.time.format(this.format));
16433         
16434         this.fireEvent('select', this, this.date);
16435         
16436         
16437     },
16438     
16439     onMousedown: function(e){
16440         e.stopPropagation();
16441         e.preventDefault();
16442     },
16443     
16444     onIncrementHours: function()
16445     {
16446         Roo.log('onIncrementHours');
16447         this.time = this.time.add(Date.HOUR, 1);
16448         this.update();
16449         
16450     },
16451     
16452     onDecrementHours: function()
16453     {
16454         Roo.log('onDecrementHours');
16455         this.time = this.time.add(Date.HOUR, -1);
16456         this.update();
16457     },
16458     
16459     onIncrementMinutes: function()
16460     {
16461         Roo.log('onIncrementMinutes');
16462         this.time = this.time.add(Date.MINUTE, 1);
16463         this.update();
16464     },
16465     
16466     onDecrementMinutes: function()
16467     {
16468         Roo.log('onDecrementMinutes');
16469         this.time = this.time.add(Date.MINUTE, -1);
16470         this.update();
16471     },
16472     
16473     onTogglePeriod: function()
16474     {
16475         Roo.log('onTogglePeriod');
16476         this.time = this.time.add(Date.HOUR, 12);
16477         this.update();
16478     }
16479     
16480    
16481 });
16482
16483 Roo.apply(Roo.bootstrap.TimeField,  {
16484     
16485     content : {
16486         tag: 'tbody',
16487         cn: [
16488             {
16489                 tag: 'tr',
16490                 cn: [
16491                 {
16492                     tag: 'td',
16493                     colspan: '7'
16494                 }
16495                 ]
16496             }
16497         ]
16498     },
16499     
16500     footer : {
16501         tag: 'tfoot',
16502         cn: [
16503             {
16504                 tag: 'tr',
16505                 cn: [
16506                 {
16507                     tag: 'th',
16508                     colspan: '7',
16509                     cls: '',
16510                     cn: [
16511                         {
16512                             tag: 'button',
16513                             cls: 'btn btn-info ok',
16514                             html: 'OK'
16515                         }
16516                     ]
16517                 }
16518
16519                 ]
16520             }
16521         ]
16522     }
16523 });
16524
16525 Roo.apply(Roo.bootstrap.TimeField,  {
16526   
16527     template : {
16528         tag: 'div',
16529         cls: 'datepicker dropdown-menu',
16530         cn: [
16531             {
16532                 tag: 'div',
16533                 cls: 'datepicker-time',
16534                 cn: [
16535                 {
16536                     tag: 'table',
16537                     cls: 'table-condensed',
16538                     cn:[
16539                     Roo.bootstrap.TimeField.content,
16540                     Roo.bootstrap.TimeField.footer
16541                     ]
16542                 }
16543                 ]
16544             }
16545         ]
16546     }
16547 });
16548
16549  
16550
16551  /*
16552  * - LGPL
16553  *
16554  * MonthField
16555  * 
16556  */
16557
16558 /**
16559  * @class Roo.bootstrap.MonthField
16560  * @extends Roo.bootstrap.Input
16561  * Bootstrap MonthField class
16562  * 
16563  * @cfg {String} language default en
16564  * 
16565  * @constructor
16566  * Create a new MonthField
16567  * @param {Object} config The config object
16568  */
16569
16570 Roo.bootstrap.MonthField = function(config){
16571     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
16572     
16573     this.addEvents({
16574         /**
16575          * @event show
16576          * Fires when this field show.
16577          * @param {Roo.bootstrap.MonthField} this
16578          * @param {Mixed} date The date value
16579          */
16580         show : true,
16581         /**
16582          * @event show
16583          * Fires when this field hide.
16584          * @param {Roo.bootstrap.MonthField} this
16585          * @param {Mixed} date The date value
16586          */
16587         hide : true,
16588         /**
16589          * @event select
16590          * Fires when select a date.
16591          * @param {Roo.bootstrap.MonthField} this
16592          * @param {String} oldvalue The old value
16593          * @param {String} newvalue The new value
16594          */
16595         select : true
16596     });
16597 };
16598
16599 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
16600     
16601     onRender: function(ct, position)
16602     {
16603         
16604         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
16605         
16606         this.language = this.language || 'en';
16607         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
16608         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
16609         
16610         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
16611         this.isInline = false;
16612         this.isInput = true;
16613         this.component = this.el.select('.add-on', true).first() || false;
16614         this.component = (this.component && this.component.length === 0) ? false : this.component;
16615         this.hasInput = this.component && this.inputEL().length;
16616         
16617         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
16618         
16619         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16620         
16621         this.picker().on('mousedown', this.onMousedown, this);
16622         this.picker().on('click', this.onClick, this);
16623         
16624         this.picker().addClass('datepicker-dropdown');
16625         
16626         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16627             v.setStyle('width', '189px');
16628         });
16629         
16630         this.fillMonths();
16631         
16632         this.update();
16633         
16634         if(this.isInline) {
16635             this.show();
16636         }
16637         
16638     },
16639     
16640     setValue: function(v, suppressEvent)
16641     {   
16642         var o = this.getValue();
16643         
16644         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
16645         
16646         this.update();
16647
16648         if(suppressEvent !== true){
16649             this.fireEvent('select', this, o, v);
16650         }
16651         
16652     },
16653     
16654     getValue: function()
16655     {
16656         return this.value;
16657     },
16658     
16659     onClick: function(e) 
16660     {
16661         e.stopPropagation();
16662         e.preventDefault();
16663         
16664         var target = e.getTarget();
16665         
16666         if(target.nodeName.toLowerCase() === 'i'){
16667             target = Roo.get(target).dom.parentNode;
16668         }
16669         
16670         var nodeName = target.nodeName;
16671         var className = target.className;
16672         var html = target.innerHTML;
16673         
16674         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
16675             return;
16676         }
16677         
16678         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
16679         
16680         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16681         
16682         this.hide();
16683                         
16684     },
16685     
16686     picker : function()
16687     {
16688         return this.pickerEl;
16689     },
16690     
16691     fillMonths: function()
16692     {    
16693         var i = 0;
16694         var months = this.picker().select('>.datepicker-months td', true).first();
16695         
16696         months.dom.innerHTML = '';
16697         
16698         while (i < 12) {
16699             var month = {
16700                 tag: 'span',
16701                 cls: 'month',
16702                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
16703             }
16704             
16705             months.createChild(month);
16706         }
16707         
16708     },
16709     
16710     update: function()
16711     {
16712         var _this = this;
16713         
16714         if(typeof(this.vIndex) == 'undefined' && this.value.length){
16715             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
16716         }
16717         
16718         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
16719             e.removeClass('active');
16720             
16721             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
16722                 e.addClass('active');
16723             }
16724         })
16725     },
16726     
16727     place: function()
16728     {
16729         if(this.isInline) return;
16730         
16731         this.picker().removeClass(['bottom', 'top']);
16732         
16733         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16734             /*
16735              * place to the top of element!
16736              *
16737              */
16738             
16739             this.picker().addClass('top');
16740             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16741             
16742             return;
16743         }
16744         
16745         this.picker().addClass('bottom');
16746         
16747         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16748     },
16749     
16750     onFocus : function()
16751     {
16752         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
16753         this.show();
16754     },
16755     
16756     onBlur : function()
16757     {
16758         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
16759         
16760         var d = this.inputEl().getValue();
16761         
16762         this.setValue(d);
16763                 
16764         this.hide();
16765     },
16766     
16767     show : function()
16768     {
16769         this.picker().show();
16770         this.picker().select('>.datepicker-months', true).first().show();
16771         this.update();
16772         this.place();
16773         
16774         this.fireEvent('show', this, this.date);
16775     },
16776     
16777     hide : function()
16778     {
16779         if(this.isInline) return;
16780         this.picker().hide();
16781         this.fireEvent('hide', this, this.date);
16782         
16783     },
16784     
16785     onMousedown: function(e)
16786     {
16787         e.stopPropagation();
16788         e.preventDefault();
16789     },
16790     
16791     keyup: function(e)
16792     {
16793         Roo.bootstrap.MonthField.superclass.keyup.call(this);
16794         this.update();
16795     },
16796
16797     fireKey: function(e)
16798     {
16799         if (!this.picker().isVisible()){
16800             if (e.keyCode == 27) // allow escape to hide and re-show picker
16801                 this.show();
16802             return;
16803         }
16804         
16805         var dir;
16806         
16807         switch(e.keyCode){
16808             case 27: // escape
16809                 this.hide();
16810                 e.preventDefault();
16811                 break;
16812             case 37: // left
16813             case 39: // right
16814                 dir = e.keyCode == 37 ? -1 : 1;
16815                 
16816                 this.vIndex = this.vIndex + dir;
16817                 
16818                 if(this.vIndex < 0){
16819                     this.vIndex = 0;
16820                 }
16821                 
16822                 if(this.vIndex > 11){
16823                     this.vIndex = 11;
16824                 }
16825                 
16826                 if(isNaN(this.vIndex)){
16827                     this.vIndex = 0;
16828                 }
16829                 
16830                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16831                 
16832                 break;
16833             case 38: // up
16834             case 40: // down
16835                 
16836                 dir = e.keyCode == 38 ? -1 : 1;
16837                 
16838                 this.vIndex = this.vIndex + dir * 4;
16839                 
16840                 if(this.vIndex < 0){
16841                     this.vIndex = 0;
16842                 }
16843                 
16844                 if(this.vIndex > 11){
16845                     this.vIndex = 11;
16846                 }
16847                 
16848                 if(isNaN(this.vIndex)){
16849                     this.vIndex = 0;
16850                 }
16851                 
16852                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16853                 break;
16854                 
16855             case 13: // enter
16856                 
16857                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16858                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16859                 }
16860                 
16861                 this.hide();
16862                 e.preventDefault();
16863                 break;
16864             case 9: // tab
16865                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16866                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16867                 }
16868                 this.hide();
16869                 break;
16870             case 16: // shift
16871             case 17: // ctrl
16872             case 18: // alt
16873                 break;
16874             default :
16875                 this.hide();
16876                 
16877         }
16878     },
16879     
16880     remove: function() 
16881     {
16882         this.picker().remove();
16883     }
16884    
16885 });
16886
16887 Roo.apply(Roo.bootstrap.MonthField,  {
16888     
16889     content : {
16890         tag: 'tbody',
16891         cn: [
16892         {
16893             tag: 'tr',
16894             cn: [
16895             {
16896                 tag: 'td',
16897                 colspan: '7'
16898             }
16899             ]
16900         }
16901         ]
16902     },
16903     
16904     dates:{
16905         en: {
16906             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16907             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
16908         }
16909     }
16910 });
16911
16912 Roo.apply(Roo.bootstrap.MonthField,  {
16913   
16914     template : {
16915         tag: 'div',
16916         cls: 'datepicker dropdown-menu roo-dynamic',
16917         cn: [
16918             {
16919                 tag: 'div',
16920                 cls: 'datepicker-months',
16921                 cn: [
16922                 {
16923                     tag: 'table',
16924                     cls: 'table-condensed',
16925                     cn:[
16926                         Roo.bootstrap.DateField.content
16927                     ]
16928                 }
16929                 ]
16930             }
16931         ]
16932     }
16933 });
16934
16935  
16936
16937  
16938  /*
16939  * - LGPL
16940  *
16941  * CheckBox
16942  * 
16943  */
16944
16945 /**
16946  * @class Roo.bootstrap.CheckBox
16947  * @extends Roo.bootstrap.Input
16948  * Bootstrap CheckBox class
16949  * 
16950  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
16951  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
16952  * @cfg {String} boxLabel The text that appears beside the checkbox
16953  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
16954  * @cfg {Boolean} checked initnal the element
16955  * @cfg {Boolean} inline inline the element (default false)
16956  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
16957  * 
16958  * @constructor
16959  * Create a new CheckBox
16960  * @param {Object} config The config object
16961  */
16962
16963 Roo.bootstrap.CheckBox = function(config){
16964     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
16965    
16966     this.addEvents({
16967         /**
16968         * @event check
16969         * Fires when the element is checked or unchecked.
16970         * @param {Roo.bootstrap.CheckBox} this This input
16971         * @param {Boolean} checked The new checked value
16972         */
16973        check : true
16974     });
16975     
16976 };
16977
16978 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
16979   
16980     inputType: 'checkbox',
16981     inputValue: 1,
16982     valueOff: 0,
16983     boxLabel: false,
16984     checked: false,
16985     weight : false,
16986     inline: false,
16987     
16988     getAutoCreate : function()
16989     {
16990         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16991         
16992         var id = Roo.id();
16993         
16994         var cfg = {};
16995         
16996         cfg.cls = 'form-group ' + this.inputType; //input-group
16997         
16998         if(this.inline){
16999             cfg.cls += ' ' + this.inputType + '-inline';
17000         }
17001         
17002         var input =  {
17003             tag: 'input',
17004             id : id,
17005             type : this.inputType,
17006             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
17007             cls : 'roo-' + this.inputType, //'form-box',
17008             placeholder : this.placeholder || ''
17009             
17010         };
17011         
17012         if (this.weight) { // Validity check?
17013             cfg.cls += " " + this.inputType + "-" + this.weight;
17014         }
17015         
17016         if (this.disabled) {
17017             input.disabled=true;
17018         }
17019         
17020         if(this.checked){
17021             input.checked = this.checked;
17022         }
17023         
17024         if (this.name) {
17025             input.name = this.name;
17026         }
17027         
17028         if (this.size) {
17029             input.cls += ' input-' + this.size;
17030         }
17031         
17032         var settings=this;
17033         
17034         ['xs','sm','md','lg'].map(function(size){
17035             if (settings[size]) {
17036                 cfg.cls += ' col-' + size + '-' + settings[size];
17037             }
17038         });
17039         
17040         var inputblock = input;
17041          
17042         if (this.before || this.after) {
17043             
17044             inputblock = {
17045                 cls : 'input-group',
17046                 cn :  [] 
17047             };
17048             
17049             if (this.before) {
17050                 inputblock.cn.push({
17051                     tag :'span',
17052                     cls : 'input-group-addon',
17053                     html : this.before
17054                 });
17055             }
17056             
17057             inputblock.cn.push(input);
17058             
17059             if (this.after) {
17060                 inputblock.cn.push({
17061                     tag :'span',
17062                     cls : 'input-group-addon',
17063                     html : this.after
17064                 });
17065             }
17066             
17067         }
17068         
17069         if (align ==='left' && this.fieldLabel.length) {
17070                 Roo.log("left and has label");
17071                 cfg.cn = [
17072                     
17073                     {
17074                         tag: 'label',
17075                         'for' :  id,
17076                         cls : 'control-label col-md-' + this.labelWidth,
17077                         html : this.fieldLabel
17078                         
17079                     },
17080                     {
17081                         cls : "col-md-" + (12 - this.labelWidth), 
17082                         cn: [
17083                             inputblock
17084                         ]
17085                     }
17086                     
17087                 ];
17088         } else if ( this.fieldLabel.length) {
17089                 Roo.log(" label");
17090                 cfg.cn = [
17091                    
17092                     {
17093                         tag: this.boxLabel ? 'span' : 'label',
17094                         'for': id,
17095                         cls: 'control-label box-input-label',
17096                         //cls : 'input-group-addon',
17097                         html : this.fieldLabel
17098                         
17099                     },
17100                     
17101                     inputblock
17102                     
17103                 ];
17104
17105         } else {
17106             
17107                 Roo.log(" no label && no align");
17108                 cfg.cn = [  inputblock ] ;
17109                 
17110                 
17111         }
17112         if(this.boxLabel){
17113              var boxLabelCfg = {
17114                 tag: 'label',
17115                 //'for': id, // box label is handled by onclick - so no for...
17116                 cls: 'box-label',
17117                 html: this.boxLabel
17118             }
17119             
17120             if(this.tooltip){
17121                 boxLabelCfg.tooltip = this.tooltip;
17122             }
17123              
17124             cfg.cn.push(boxLabelCfg);
17125         }
17126         
17127         
17128        
17129         return cfg;
17130         
17131     },
17132     
17133     /**
17134      * return the real input element.
17135      */
17136     inputEl: function ()
17137     {
17138         return this.el.select('input.roo-' + this.inputType,true).first();
17139     },
17140     
17141     labelEl: function()
17142     {
17143         return this.el.select('label.control-label',true).first();
17144     },
17145     /* depricated... */
17146     
17147     label: function()
17148     {
17149         return this.labelEl();
17150     },
17151     
17152     initEvents : function()
17153     {
17154 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17155         
17156         this.inputEl().on('click', this.onClick,  this);
17157         
17158         if (this.boxLabel) { 
17159             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
17160         }
17161         
17162         this.startValue = this.getValue();
17163         
17164         if(this.groupId){
17165             Roo.bootstrap.CheckBox.register(this);
17166         }
17167     },
17168     
17169     onClick : function()
17170     {   
17171         this.setChecked(!this.checked);
17172     },
17173     
17174     setChecked : function(state,suppressEvent)
17175     {
17176         this.startValue = this.getValue();
17177         
17178         if(this.inputType == 'radio'){
17179             
17180             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17181                 e.dom.checked = false;
17182             });
17183             
17184             this.inputEl().dom.checked = true;
17185             
17186             this.inputEl().dom.value = this.inputValue;
17187             
17188             if(suppressEvent !== true){
17189                 this.fireEvent('check', this, true);
17190             }
17191             
17192             this.validate();
17193             
17194             return;
17195         }
17196         
17197         this.checked = state;
17198         
17199         this.inputEl().dom.checked = state;
17200         
17201         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17202         
17203         if(suppressEvent !== true){
17204             this.fireEvent('check', this, state);
17205         }
17206         
17207         this.validate();
17208     },
17209     
17210     getValue : function()
17211     {
17212         if(this.inputType == 'radio'){
17213             return this.getGroupValue();
17214         }
17215         
17216         return this.inputEl().getValue();
17217         
17218     },
17219     
17220     getGroupValue : function()
17221     {
17222         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
17223             return '';
17224         }
17225         
17226         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
17227     },
17228     
17229     setValue : function(v,suppressEvent)
17230     {
17231         if(this.inputType == 'radio'){
17232             this.setGroupValue(v, suppressEvent);
17233             return;
17234         }
17235         
17236         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
17237         
17238         this.validate();
17239     },
17240     
17241     setGroupValue : function(v, suppressEvent)
17242     {
17243         this.startValue = this.getValue();
17244         
17245         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17246             e.dom.checked = false;
17247             
17248             if(e.dom.value == v){
17249                 e.dom.checked = true;
17250             }
17251         });
17252         
17253         if(suppressEvent !== true){
17254             this.fireEvent('check', this, true);
17255         }
17256
17257         this.validate();
17258         
17259         return;
17260     },
17261     
17262     validate : function()
17263     {
17264         if(
17265                 this.disabled || 
17266                 (this.inputType == 'radio' && this.validateRadio()) ||
17267                 (this.inputType == 'checkbox' && this.validateCheckbox())
17268         ){
17269             this.markValid();
17270             return true;
17271         }
17272         
17273         this.markInvalid();
17274         return false;
17275     },
17276     
17277     validateRadio : function()
17278     {
17279         var valid = false;
17280         
17281         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17282             if(!e.dom.checked){
17283                 return;
17284             }
17285             
17286             valid = true;
17287             
17288             return false;
17289         });
17290         
17291         return valid;
17292     },
17293     
17294     validateCheckbox : function()
17295     {
17296         if(!this.groupId){
17297             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
17298         }
17299         
17300         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17301         
17302         if(!group){
17303             return false;
17304         }
17305         
17306         var r = false;
17307         
17308         for(var i in group){
17309             if(r){
17310                 break;
17311             }
17312             
17313             r = (group[i].getValue() == group[i].inputValue) ? true : false;
17314         }
17315         
17316         return r;
17317     },
17318     
17319     /**
17320      * Mark this field as valid
17321      */
17322     markValid : function()
17323     {
17324         if(this.allowBlank){
17325             return;
17326         }
17327         
17328         var _this = this;
17329         
17330         this.fireEvent('valid', this);
17331         
17332         if(this.inputType == 'radio'){
17333             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17334                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17335                 e.findParent('.form-group', false, true).addClass(_this.validClass);
17336             });
17337             
17338             return;
17339         }
17340         
17341         if(!this.groupId){
17342             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17343             this.el.findParent('.form-group', false, true).addClass(this.validClass);
17344             return;
17345         }
17346         
17347         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17348             
17349         if(!group){
17350             return;
17351         }
17352         
17353         for(var i in group){
17354             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17355             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
17356         }
17357     },
17358     
17359      /**
17360      * Mark this field as invalid
17361      * @param {String} msg The validation message
17362      */
17363     markInvalid : function(msg)
17364     {
17365         if(this.allowBlank){
17366             return;
17367         }
17368         
17369         var _this = this;
17370         
17371         this.fireEvent('invalid', this, msg);
17372         
17373         if(this.inputType == 'radio'){
17374             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17375                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17376                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
17377             });
17378             
17379             return;
17380         }
17381         
17382         if(!this.groupId){
17383             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17384             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
17385             return;
17386         }
17387         
17388         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17389             
17390         if(!group){
17391             return;
17392         }
17393         
17394         for(var i in group){
17395             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17396             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
17397         }
17398         
17399     }
17400     
17401 });
17402
17403 Roo.apply(Roo.bootstrap.CheckBox, {
17404     
17405     groups: {},
17406     
17407      /**
17408     * register a CheckBox Group
17409     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
17410     */
17411     register : function(checkbox)
17412     {
17413         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
17414             this.groups[checkbox.groupId] = {};
17415         }
17416         
17417         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
17418             return;
17419         }
17420         
17421         this.groups[checkbox.groupId][checkbox.name] = checkbox;
17422         
17423     },
17424     /**
17425     * fetch a CheckBox Group based on the group ID
17426     * @param {string} the group ID
17427     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
17428     */
17429     get: function(groupId) {
17430         if (typeof(this.groups[groupId]) == 'undefined') {
17431             return false;
17432         }
17433         
17434         return this.groups[groupId] ;
17435     }
17436     
17437     
17438 });
17439 /*
17440  * - LGPL
17441  *
17442  * Radio
17443  *
17444  *
17445  * not inline
17446  *<div class="radio">
17447   <label>
17448     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
17449     Option one is this and that&mdash;be sure to include why it's great
17450   </label>
17451 </div>
17452  *
17453  *
17454  *inline
17455  *<span>
17456  *<label class="radio-inline">fieldLabel</label>
17457  *<label class="radio-inline">
17458   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
17459 </label>
17460 <span>
17461  * 
17462  * 
17463  */
17464
17465 /**
17466  * @class Roo.bootstrap.Radio
17467  * @extends Roo.bootstrap.CheckBox
17468  * Bootstrap Radio class
17469
17470  * @constructor
17471  * Create a new Radio
17472  * @param {Object} config The config object
17473  */
17474
17475 Roo.bootstrap.Radio = function(config){
17476     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
17477    
17478 };
17479
17480 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
17481     
17482     inputType: 'radio',
17483     inputValue: '',
17484     valueOff: '',
17485     
17486     getAutoCreate : function()
17487     {
17488         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17489         align = align || 'left'; // default...
17490         
17491         
17492         
17493         var id = Roo.id();
17494         
17495         var cfg = {
17496                 tag : this.inline ? 'span' : 'div',
17497                 cls : '',
17498                 cn : []
17499         };
17500         
17501         var inline = this.inline ? ' radio-inline' : '';
17502         
17503         var lbl = {
17504                 tag: 'label' ,
17505                 // does not need for, as we wrap the input with it..
17506                 'for' : id,
17507                 cls : 'control-label box-label' + inline,
17508                 cn : []
17509         };
17510         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
17511         
17512         var fieldLabel = {
17513             tag: 'label' ,
17514             //cls : 'control-label' + inline,
17515             html : this.fieldLabel,
17516             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
17517         };
17518         
17519  
17520         
17521         
17522         var input =  {
17523             tag: 'input',
17524             id : id,
17525             type : this.inputType,
17526             //value : (!this.checked) ? this.valueOff : this.inputValue,
17527             value : this.inputValue,
17528             cls : 'roo-radio',
17529             placeholder : this.placeholder || '' // ?? needed????
17530             
17531         };
17532         if (this.weight) { // Validity check?
17533             input.cls += " radio-" + this.weight;
17534         }
17535         if (this.disabled) {
17536             input.disabled=true;
17537         }
17538         
17539         if(this.checked){
17540             input.checked = this.checked;
17541         }
17542         
17543         if (this.name) {
17544             input.name = this.name;
17545         }
17546         
17547         if (this.size) {
17548             input.cls += ' input-' + this.size;
17549         }
17550         
17551         //?? can span's inline have a width??
17552         
17553         var settings=this;
17554         ['xs','sm','md','lg'].map(function(size){
17555             if (settings[size]) {
17556                 cfg.cls += ' col-' + size + '-' + settings[size];
17557             }
17558         });
17559         
17560         var inputblock = input;
17561         
17562         if (this.before || this.after) {
17563             
17564             inputblock = {
17565                 cls : 'input-group',
17566                 tag : 'span',
17567                 cn :  [] 
17568             };
17569             if (this.before) {
17570                 inputblock.cn.push({
17571                     tag :'span',
17572                     cls : 'input-group-addon',
17573                     html : this.before
17574                 });
17575             }
17576             inputblock.cn.push(input);
17577             if (this.after) {
17578                 inputblock.cn.push({
17579                     tag :'span',
17580                     cls : 'input-group-addon',
17581                     html : this.after
17582                 });
17583             }
17584             
17585         };
17586         
17587         
17588         if (this.fieldLabel && this.fieldLabel.length) {
17589             cfg.cn.push(fieldLabel);
17590         }
17591        
17592         // normal bootstrap puts the input inside the label.
17593         // however with our styled version - it has to go after the input.
17594        
17595         //lbl.cn.push(inputblock);
17596         
17597         var lblwrap =  {
17598             tag: 'span',
17599             cls: 'radio' + inline,
17600             cn: [
17601                 inputblock,
17602                 lbl
17603             ]
17604         };
17605         
17606         cfg.cn.push( lblwrap);
17607         
17608         if(this.boxLabel){
17609             lbl.cn.push({
17610                 tag: 'span',
17611                 html: this.boxLabel
17612             })
17613         }
17614          
17615         
17616         return cfg;
17617         
17618     },
17619     
17620     initEvents : function()
17621     {
17622 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17623         
17624         this.inputEl().on('click', this.onClick,  this);
17625         if (this.boxLabel) {
17626             Roo.log('find label')
17627             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
17628         }
17629         
17630     },
17631     
17632     inputEl: function ()
17633     {
17634         return this.el.select('input.roo-radio',true).first();
17635     },
17636     onClick : function()
17637     {   
17638         Roo.log("click");
17639         this.setChecked(true);
17640     },
17641     
17642     setChecked : function(state,suppressEvent)
17643     {
17644         if(state){
17645             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17646                 v.dom.checked = false;
17647             });
17648         }
17649         Roo.log(this.inputEl().dom);
17650         this.checked = state;
17651         this.inputEl().dom.checked = state;
17652         
17653         if(suppressEvent !== true){
17654             this.fireEvent('check', this, state);
17655         }
17656         
17657         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17658         
17659     },
17660     
17661     getGroupValue : function()
17662     {
17663         var value = '';
17664         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17665             if(v.dom.checked == true){
17666                 value = v.dom.value;
17667             }
17668         });
17669         
17670         return value;
17671     },
17672     
17673     /**
17674      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
17675      * @return {Mixed} value The field value
17676      */
17677     getValue : function(){
17678         return this.getGroupValue();
17679     }
17680     
17681 });
17682
17683  
17684 //<script type="text/javascript">
17685
17686 /*
17687  * Based  Ext JS Library 1.1.1
17688  * Copyright(c) 2006-2007, Ext JS, LLC.
17689  * LGPL
17690  *
17691  */
17692  
17693 /**
17694  * @class Roo.HtmlEditorCore
17695  * @extends Roo.Component
17696  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
17697  *
17698  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
17699  */
17700
17701 Roo.HtmlEditorCore = function(config){
17702     
17703     
17704     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
17705     
17706     
17707     this.addEvents({
17708         /**
17709          * @event initialize
17710          * Fires when the editor is fully initialized (including the iframe)
17711          * @param {Roo.HtmlEditorCore} this
17712          */
17713         initialize: true,
17714         /**
17715          * @event activate
17716          * Fires when the editor is first receives the focus. Any insertion must wait
17717          * until after this event.
17718          * @param {Roo.HtmlEditorCore} this
17719          */
17720         activate: true,
17721          /**
17722          * @event beforesync
17723          * Fires before the textarea is updated with content from the editor iframe. Return false
17724          * to cancel the sync.
17725          * @param {Roo.HtmlEditorCore} this
17726          * @param {String} html
17727          */
17728         beforesync: true,
17729          /**
17730          * @event beforepush
17731          * Fires before the iframe editor is updated with content from the textarea. Return false
17732          * to cancel the push.
17733          * @param {Roo.HtmlEditorCore} this
17734          * @param {String} html
17735          */
17736         beforepush: true,
17737          /**
17738          * @event sync
17739          * Fires when the textarea is updated with content from the editor iframe.
17740          * @param {Roo.HtmlEditorCore} this
17741          * @param {String} html
17742          */
17743         sync: true,
17744          /**
17745          * @event push
17746          * Fires when the iframe editor is updated with content from the textarea.
17747          * @param {Roo.HtmlEditorCore} this
17748          * @param {String} html
17749          */
17750         push: true,
17751         
17752         /**
17753          * @event editorevent
17754          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17755          * @param {Roo.HtmlEditorCore} this
17756          */
17757         editorevent: true
17758         
17759     });
17760     
17761     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
17762     
17763     // defaults : white / black...
17764     this.applyBlacklists();
17765     
17766     
17767     
17768 };
17769
17770
17771 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
17772
17773
17774      /**
17775      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
17776      */
17777     
17778     owner : false,
17779     
17780      /**
17781      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17782      *                        Roo.resizable.
17783      */
17784     resizable : false,
17785      /**
17786      * @cfg {Number} height (in pixels)
17787      */   
17788     height: 300,
17789    /**
17790      * @cfg {Number} width (in pixels)
17791      */   
17792     width: 500,
17793     
17794     /**
17795      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17796      * 
17797      */
17798     stylesheets: false,
17799     
17800     // id of frame..
17801     frameId: false,
17802     
17803     // private properties
17804     validationEvent : false,
17805     deferHeight: true,
17806     initialized : false,
17807     activated : false,
17808     sourceEditMode : false,
17809     onFocus : Roo.emptyFn,
17810     iframePad:3,
17811     hideMode:'offsets',
17812     
17813     clearUp: true,
17814     
17815     // blacklist + whitelisted elements..
17816     black: false,
17817     white: false,
17818      
17819     
17820
17821     /**
17822      * Protected method that will not generally be called directly. It
17823      * is called when the editor initializes the iframe with HTML contents. Override this method if you
17824      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
17825      */
17826     getDocMarkup : function(){
17827         // body styles..
17828         var st = '';
17829         
17830         // inherit styels from page...?? 
17831         if (this.stylesheets === false) {
17832             
17833             Roo.get(document.head).select('style').each(function(node) {
17834                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17835             });
17836             
17837             Roo.get(document.head).select('link').each(function(node) { 
17838                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17839             });
17840             
17841         } else if (!this.stylesheets.length) {
17842                 // simple..
17843                 st = '<style type="text/css">' +
17844                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17845                    '</style>';
17846         } else { 
17847             
17848         }
17849         
17850         st +=  '<style type="text/css">' +
17851             'IMG { cursor: pointer } ' +
17852         '</style>';
17853
17854         
17855         return '<html><head>' + st  +
17856             //<style type="text/css">' +
17857             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17858             //'</style>' +
17859             ' </head><body class="roo-htmleditor-body"></body></html>';
17860     },
17861
17862     // private
17863     onRender : function(ct, position)
17864     {
17865         var _t = this;
17866         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
17867         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
17868         
17869         
17870         this.el.dom.style.border = '0 none';
17871         this.el.dom.setAttribute('tabIndex', -1);
17872         this.el.addClass('x-hidden hide');
17873         
17874         
17875         
17876         if(Roo.isIE){ // fix IE 1px bogus margin
17877             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
17878         }
17879        
17880         
17881         this.frameId = Roo.id();
17882         
17883          
17884         
17885         var iframe = this.owner.wrap.createChild({
17886             tag: 'iframe',
17887             cls: 'form-control', // bootstrap..
17888             id: this.frameId,
17889             name: this.frameId,
17890             frameBorder : 'no',
17891             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
17892         }, this.el
17893         );
17894         
17895         
17896         this.iframe = iframe.dom;
17897
17898          this.assignDocWin();
17899         
17900         this.doc.designMode = 'on';
17901        
17902         this.doc.open();
17903         this.doc.write(this.getDocMarkup());
17904         this.doc.close();
17905
17906         
17907         var task = { // must defer to wait for browser to be ready
17908             run : function(){
17909                 //console.log("run task?" + this.doc.readyState);
17910                 this.assignDocWin();
17911                 if(this.doc.body || this.doc.readyState == 'complete'){
17912                     try {
17913                         this.doc.designMode="on";
17914                     } catch (e) {
17915                         return;
17916                     }
17917                     Roo.TaskMgr.stop(task);
17918                     this.initEditor.defer(10, this);
17919                 }
17920             },
17921             interval : 10,
17922             duration: 10000,
17923             scope: this
17924         };
17925         Roo.TaskMgr.start(task);
17926
17927     },
17928
17929     // private
17930     onResize : function(w, h)
17931     {
17932          Roo.log('resize: ' +w + ',' + h );
17933         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
17934         if(!this.iframe){
17935             return;
17936         }
17937         if(typeof w == 'number'){
17938             
17939             this.iframe.style.width = w + 'px';
17940         }
17941         if(typeof h == 'number'){
17942             
17943             this.iframe.style.height = h + 'px';
17944             if(this.doc){
17945                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
17946             }
17947         }
17948         
17949     },
17950
17951     /**
17952      * Toggles the editor between standard and source edit mode.
17953      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17954      */
17955     toggleSourceEdit : function(sourceEditMode){
17956         
17957         this.sourceEditMode = sourceEditMode === true;
17958         
17959         if(this.sourceEditMode){
17960  
17961             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
17962             
17963         }else{
17964             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
17965             //this.iframe.className = '';
17966             this.deferFocus();
17967         }
17968         //this.setSize(this.owner.wrap.getSize());
17969         //this.fireEvent('editmodechange', this, this.sourceEditMode);
17970     },
17971
17972     
17973   
17974
17975     /**
17976      * Protected method that will not generally be called directly. If you need/want
17977      * custom HTML cleanup, this is the method you should override.
17978      * @param {String} html The HTML to be cleaned
17979      * return {String} The cleaned HTML
17980      */
17981     cleanHtml : function(html){
17982         html = String(html);
17983         if(html.length > 5){
17984             if(Roo.isSafari){ // strip safari nonsense
17985                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
17986             }
17987         }
17988         if(html == '&nbsp;'){
17989             html = '';
17990         }
17991         return html;
17992     },
17993
17994     /**
17995      * HTML Editor -> Textarea
17996      * Protected method that will not generally be called directly. Syncs the contents
17997      * of the editor iframe with the textarea.
17998      */
17999     syncValue : function(){
18000         if(this.initialized){
18001             var bd = (this.doc.body || this.doc.documentElement);
18002             //this.cleanUpPaste(); -- this is done else where and causes havoc..
18003             var html = bd.innerHTML;
18004             if(Roo.isSafari){
18005                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
18006                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
18007                 if(m && m[1]){
18008                     html = '<div style="'+m[0]+'">' + html + '</div>';
18009                 }
18010             }
18011             html = this.cleanHtml(html);
18012             // fix up the special chars.. normaly like back quotes in word...
18013             // however we do not want to do this with chinese..
18014             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
18015                 var cc = b.charCodeAt();
18016                 if (
18017                     (cc >= 0x4E00 && cc < 0xA000 ) ||
18018                     (cc >= 0x3400 && cc < 0x4E00 ) ||
18019                     (cc >= 0xf900 && cc < 0xfb00 )
18020                 ) {
18021                         return b;
18022                 }
18023                 return "&#"+cc+";" 
18024             });
18025             if(this.owner.fireEvent('beforesync', this, html) !== false){
18026                 this.el.dom.value = html;
18027                 this.owner.fireEvent('sync', this, html);
18028             }
18029         }
18030     },
18031
18032     /**
18033      * Protected method that will not generally be called directly. Pushes the value of the textarea
18034      * into the iframe editor.
18035      */
18036     pushValue : function(){
18037         if(this.initialized){
18038             var v = this.el.dom.value.trim();
18039             
18040 //            if(v.length < 1){
18041 //                v = '&#160;';
18042 //            }
18043             
18044             if(this.owner.fireEvent('beforepush', this, v) !== false){
18045                 var d = (this.doc.body || this.doc.documentElement);
18046                 d.innerHTML = v;
18047                 this.cleanUpPaste();
18048                 this.el.dom.value = d.innerHTML;
18049                 this.owner.fireEvent('push', this, v);
18050             }
18051         }
18052     },
18053
18054     // private
18055     deferFocus : function(){
18056         this.focus.defer(10, this);
18057     },
18058
18059     // doc'ed in Field
18060     focus : function(){
18061         if(this.win && !this.sourceEditMode){
18062             this.win.focus();
18063         }else{
18064             this.el.focus();
18065         }
18066     },
18067     
18068     assignDocWin: function()
18069     {
18070         var iframe = this.iframe;
18071         
18072          if(Roo.isIE){
18073             this.doc = iframe.contentWindow.document;
18074             this.win = iframe.contentWindow;
18075         } else {
18076 //            if (!Roo.get(this.frameId)) {
18077 //                return;
18078 //            }
18079 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18080 //            this.win = Roo.get(this.frameId).dom.contentWindow;
18081             
18082             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
18083                 return;
18084             }
18085             
18086             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18087             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
18088         }
18089     },
18090     
18091     // private
18092     initEditor : function(){
18093         //console.log("INIT EDITOR");
18094         this.assignDocWin();
18095         
18096         
18097         
18098         this.doc.designMode="on";
18099         this.doc.open();
18100         this.doc.write(this.getDocMarkup());
18101         this.doc.close();
18102         
18103         var dbody = (this.doc.body || this.doc.documentElement);
18104         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
18105         // this copies styles from the containing element into thsi one..
18106         // not sure why we need all of this..
18107         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
18108         
18109         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
18110         //ss['background-attachment'] = 'fixed'; // w3c
18111         dbody.bgProperties = 'fixed'; // ie
18112         //Roo.DomHelper.applyStyles(dbody, ss);
18113         Roo.EventManager.on(this.doc, {
18114             //'mousedown': this.onEditorEvent,
18115             'mouseup': this.onEditorEvent,
18116             'dblclick': this.onEditorEvent,
18117             'click': this.onEditorEvent,
18118             'keyup': this.onEditorEvent,
18119             buffer:100,
18120             scope: this
18121         });
18122         if(Roo.isGecko){
18123             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
18124         }
18125         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
18126             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
18127         }
18128         this.initialized = true;
18129
18130         this.owner.fireEvent('initialize', this);
18131         this.pushValue();
18132     },
18133
18134     // private
18135     onDestroy : function(){
18136         
18137         
18138         
18139         if(this.rendered){
18140             
18141             //for (var i =0; i < this.toolbars.length;i++) {
18142             //    // fixme - ask toolbars for heights?
18143             //    this.toolbars[i].onDestroy();
18144            // }
18145             
18146             //this.wrap.dom.innerHTML = '';
18147             //this.wrap.remove();
18148         }
18149     },
18150
18151     // private
18152     onFirstFocus : function(){
18153         
18154         this.assignDocWin();
18155         
18156         
18157         this.activated = true;
18158          
18159     
18160         if(Roo.isGecko){ // prevent silly gecko errors
18161             this.win.focus();
18162             var s = this.win.getSelection();
18163             if(!s.focusNode || s.focusNode.nodeType != 3){
18164                 var r = s.getRangeAt(0);
18165                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
18166                 r.collapse(true);
18167                 this.deferFocus();
18168             }
18169             try{
18170                 this.execCmd('useCSS', true);
18171                 this.execCmd('styleWithCSS', false);
18172             }catch(e){}
18173         }
18174         this.owner.fireEvent('activate', this);
18175     },
18176
18177     // private
18178     adjustFont: function(btn){
18179         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
18180         //if(Roo.isSafari){ // safari
18181         //    adjust *= 2;
18182        // }
18183         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
18184         if(Roo.isSafari){ // safari
18185             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
18186             v =  (v < 10) ? 10 : v;
18187             v =  (v > 48) ? 48 : v;
18188             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
18189             
18190         }
18191         
18192         
18193         v = Math.max(1, v+adjust);
18194         
18195         this.execCmd('FontSize', v  );
18196     },
18197
18198     onEditorEvent : function(e){
18199         this.owner.fireEvent('editorevent', this, e);
18200       //  this.updateToolbar();
18201         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
18202     },
18203
18204     insertTag : function(tg)
18205     {
18206         // could be a bit smarter... -> wrap the current selected tRoo..
18207         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
18208             
18209             range = this.createRange(this.getSelection());
18210             var wrappingNode = this.doc.createElement(tg.toLowerCase());
18211             wrappingNode.appendChild(range.extractContents());
18212             range.insertNode(wrappingNode);
18213
18214             return;
18215             
18216             
18217             
18218         }
18219         this.execCmd("formatblock",   tg);
18220         
18221     },
18222     
18223     insertText : function(txt)
18224     {
18225         
18226         
18227         var range = this.createRange();
18228         range.deleteContents();
18229                //alert(Sender.getAttribute('label'));
18230                
18231         range.insertNode(this.doc.createTextNode(txt));
18232     } ,
18233     
18234      
18235
18236     /**
18237      * Executes a Midas editor command on the editor document and performs necessary focus and
18238      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
18239      * @param {String} cmd The Midas command
18240      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18241      */
18242     relayCmd : function(cmd, value){
18243         this.win.focus();
18244         this.execCmd(cmd, value);
18245         this.owner.fireEvent('editorevent', this);
18246         //this.updateToolbar();
18247         this.owner.deferFocus();
18248     },
18249
18250     /**
18251      * Executes a Midas editor command directly on the editor document.
18252      * For visual commands, you should use {@link #relayCmd} instead.
18253      * <b>This should only be called after the editor is initialized.</b>
18254      * @param {String} cmd The Midas command
18255      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18256      */
18257     execCmd : function(cmd, value){
18258         this.doc.execCommand(cmd, false, value === undefined ? null : value);
18259         this.syncValue();
18260     },
18261  
18262  
18263    
18264     /**
18265      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
18266      * to insert tRoo.
18267      * @param {String} text | dom node.. 
18268      */
18269     insertAtCursor : function(text)
18270     {
18271         
18272         
18273         
18274         if(!this.activated){
18275             return;
18276         }
18277         /*
18278         if(Roo.isIE){
18279             this.win.focus();
18280             var r = this.doc.selection.createRange();
18281             if(r){
18282                 r.collapse(true);
18283                 r.pasteHTML(text);
18284                 this.syncValue();
18285                 this.deferFocus();
18286             
18287             }
18288             return;
18289         }
18290         */
18291         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
18292             this.win.focus();
18293             
18294             
18295             // from jquery ui (MIT licenced)
18296             var range, node;
18297             var win = this.win;
18298             
18299             if (win.getSelection && win.getSelection().getRangeAt) {
18300                 range = win.getSelection().getRangeAt(0);
18301                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
18302                 range.insertNode(node);
18303             } else if (win.document.selection && win.document.selection.createRange) {
18304                 // no firefox support
18305                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18306                 win.document.selection.createRange().pasteHTML(txt);
18307             } else {
18308                 // no firefox support
18309                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18310                 this.execCmd('InsertHTML', txt);
18311             } 
18312             
18313             this.syncValue();
18314             
18315             this.deferFocus();
18316         }
18317     },
18318  // private
18319     mozKeyPress : function(e){
18320         if(e.ctrlKey){
18321             var c = e.getCharCode(), cmd;
18322           
18323             if(c > 0){
18324                 c = String.fromCharCode(c).toLowerCase();
18325                 switch(c){
18326                     case 'b':
18327                         cmd = 'bold';
18328                         break;
18329                     case 'i':
18330                         cmd = 'italic';
18331                         break;
18332                     
18333                     case 'u':
18334                         cmd = 'underline';
18335                         break;
18336                     
18337                     case 'v':
18338                         this.cleanUpPaste.defer(100, this);
18339                         return;
18340                         
18341                 }
18342                 if(cmd){
18343                     this.win.focus();
18344                     this.execCmd(cmd);
18345                     this.deferFocus();
18346                     e.preventDefault();
18347                 }
18348                 
18349             }
18350         }
18351     },
18352
18353     // private
18354     fixKeys : function(){ // load time branching for fastest keydown performance
18355         if(Roo.isIE){
18356             return function(e){
18357                 var k = e.getKey(), r;
18358                 if(k == e.TAB){
18359                     e.stopEvent();
18360                     r = this.doc.selection.createRange();
18361                     if(r){
18362                         r.collapse(true);
18363                         r.pasteHTML('&#160;&#160;&#160;&#160;');
18364                         this.deferFocus();
18365                     }
18366                     return;
18367                 }
18368                 
18369                 if(k == e.ENTER){
18370                     r = this.doc.selection.createRange();
18371                     if(r){
18372                         var target = r.parentElement();
18373                         if(!target || target.tagName.toLowerCase() != 'li'){
18374                             e.stopEvent();
18375                             r.pasteHTML('<br />');
18376                             r.collapse(false);
18377                             r.select();
18378                         }
18379                     }
18380                 }
18381                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18382                     this.cleanUpPaste.defer(100, this);
18383                     return;
18384                 }
18385                 
18386                 
18387             };
18388         }else if(Roo.isOpera){
18389             return function(e){
18390                 var k = e.getKey();
18391                 if(k == e.TAB){
18392                     e.stopEvent();
18393                     this.win.focus();
18394                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
18395                     this.deferFocus();
18396                 }
18397                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18398                     this.cleanUpPaste.defer(100, this);
18399                     return;
18400                 }
18401                 
18402             };
18403         }else if(Roo.isSafari){
18404             return function(e){
18405                 var k = e.getKey();
18406                 
18407                 if(k == e.TAB){
18408                     e.stopEvent();
18409                     this.execCmd('InsertText','\t');
18410                     this.deferFocus();
18411                     return;
18412                 }
18413                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18414                     this.cleanUpPaste.defer(100, this);
18415                     return;
18416                 }
18417                 
18418              };
18419         }
18420     }(),
18421     
18422     getAllAncestors: function()
18423     {
18424         var p = this.getSelectedNode();
18425         var a = [];
18426         if (!p) {
18427             a.push(p); // push blank onto stack..
18428             p = this.getParentElement();
18429         }
18430         
18431         
18432         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
18433             a.push(p);
18434             p = p.parentNode;
18435         }
18436         a.push(this.doc.body);
18437         return a;
18438     },
18439     lastSel : false,
18440     lastSelNode : false,
18441     
18442     
18443     getSelection : function() 
18444     {
18445         this.assignDocWin();
18446         return Roo.isIE ? this.doc.selection : this.win.getSelection();
18447     },
18448     
18449     getSelectedNode: function() 
18450     {
18451         // this may only work on Gecko!!!
18452         
18453         // should we cache this!!!!
18454         
18455         
18456         
18457          
18458         var range = this.createRange(this.getSelection()).cloneRange();
18459         
18460         if (Roo.isIE) {
18461             var parent = range.parentElement();
18462             while (true) {
18463                 var testRange = range.duplicate();
18464                 testRange.moveToElementText(parent);
18465                 if (testRange.inRange(range)) {
18466                     break;
18467                 }
18468                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
18469                     break;
18470                 }
18471                 parent = parent.parentElement;
18472             }
18473             return parent;
18474         }
18475         
18476         // is ancestor a text element.
18477         var ac =  range.commonAncestorContainer;
18478         if (ac.nodeType == 3) {
18479             ac = ac.parentNode;
18480         }
18481         
18482         var ar = ac.childNodes;
18483          
18484         var nodes = [];
18485         var other_nodes = [];
18486         var has_other_nodes = false;
18487         for (var i=0;i<ar.length;i++) {
18488             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
18489                 continue;
18490             }
18491             // fullly contained node.
18492             
18493             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
18494                 nodes.push(ar[i]);
18495                 continue;
18496             }
18497             
18498             // probably selected..
18499             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
18500                 other_nodes.push(ar[i]);
18501                 continue;
18502             }
18503             // outer..
18504             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
18505                 continue;
18506             }
18507             
18508             
18509             has_other_nodes = true;
18510         }
18511         if (!nodes.length && other_nodes.length) {
18512             nodes= other_nodes;
18513         }
18514         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
18515             return false;
18516         }
18517         
18518         return nodes[0];
18519     },
18520     createRange: function(sel)
18521     {
18522         // this has strange effects when using with 
18523         // top toolbar - not sure if it's a great idea.
18524         //this.editor.contentWindow.focus();
18525         if (typeof sel != "undefined") {
18526             try {
18527                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
18528             } catch(e) {
18529                 return this.doc.createRange();
18530             }
18531         } else {
18532             return this.doc.createRange();
18533         }
18534     },
18535     getParentElement: function()
18536     {
18537         
18538         this.assignDocWin();
18539         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
18540         
18541         var range = this.createRange(sel);
18542          
18543         try {
18544             var p = range.commonAncestorContainer;
18545             while (p.nodeType == 3) { // text node
18546                 p = p.parentNode;
18547             }
18548             return p;
18549         } catch (e) {
18550             return null;
18551         }
18552     
18553     },
18554     /***
18555      *
18556      * Range intersection.. the hard stuff...
18557      *  '-1' = before
18558      *  '0' = hits..
18559      *  '1' = after.
18560      *         [ -- selected range --- ]
18561      *   [fail]                        [fail]
18562      *
18563      *    basically..
18564      *      if end is before start or  hits it. fail.
18565      *      if start is after end or hits it fail.
18566      *
18567      *   if either hits (but other is outside. - then it's not 
18568      *   
18569      *    
18570      **/
18571     
18572     
18573     // @see http://www.thismuchiknow.co.uk/?p=64.
18574     rangeIntersectsNode : function(range, node)
18575     {
18576         var nodeRange = node.ownerDocument.createRange();
18577         try {
18578             nodeRange.selectNode(node);
18579         } catch (e) {
18580             nodeRange.selectNodeContents(node);
18581         }
18582     
18583         var rangeStartRange = range.cloneRange();
18584         rangeStartRange.collapse(true);
18585     
18586         var rangeEndRange = range.cloneRange();
18587         rangeEndRange.collapse(false);
18588     
18589         var nodeStartRange = nodeRange.cloneRange();
18590         nodeStartRange.collapse(true);
18591     
18592         var nodeEndRange = nodeRange.cloneRange();
18593         nodeEndRange.collapse(false);
18594     
18595         return rangeStartRange.compareBoundaryPoints(
18596                  Range.START_TO_START, nodeEndRange) == -1 &&
18597                rangeEndRange.compareBoundaryPoints(
18598                  Range.START_TO_START, nodeStartRange) == 1;
18599         
18600          
18601     },
18602     rangeCompareNode : function(range, node)
18603     {
18604         var nodeRange = node.ownerDocument.createRange();
18605         try {
18606             nodeRange.selectNode(node);
18607         } catch (e) {
18608             nodeRange.selectNodeContents(node);
18609         }
18610         
18611         
18612         range.collapse(true);
18613     
18614         nodeRange.collapse(true);
18615      
18616         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
18617         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
18618          
18619         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
18620         
18621         var nodeIsBefore   =  ss == 1;
18622         var nodeIsAfter    = ee == -1;
18623         
18624         if (nodeIsBefore && nodeIsAfter)
18625             return 0; // outer
18626         if (!nodeIsBefore && nodeIsAfter)
18627             return 1; //right trailed.
18628         
18629         if (nodeIsBefore && !nodeIsAfter)
18630             return 2;  // left trailed.
18631         // fully contined.
18632         return 3;
18633     },
18634
18635     // private? - in a new class?
18636     cleanUpPaste :  function()
18637     {
18638         // cleans up the whole document..
18639         Roo.log('cleanuppaste');
18640         
18641         this.cleanUpChildren(this.doc.body);
18642         var clean = this.cleanWordChars(this.doc.body.innerHTML);
18643         if (clean != this.doc.body.innerHTML) {
18644             this.doc.body.innerHTML = clean;
18645         }
18646         
18647     },
18648     
18649     cleanWordChars : function(input) {// change the chars to hex code
18650         var he = Roo.HtmlEditorCore;
18651         
18652         var output = input;
18653         Roo.each(he.swapCodes, function(sw) { 
18654             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
18655             
18656             output = output.replace(swapper, sw[1]);
18657         });
18658         
18659         return output;
18660     },
18661     
18662     
18663     cleanUpChildren : function (n)
18664     {
18665         if (!n.childNodes.length) {
18666             return;
18667         }
18668         for (var i = n.childNodes.length-1; i > -1 ; i--) {
18669            this.cleanUpChild(n.childNodes[i]);
18670         }
18671     },
18672     
18673     
18674         
18675     
18676     cleanUpChild : function (node)
18677     {
18678         var ed = this;
18679         //console.log(node);
18680         if (node.nodeName == "#text") {
18681             // clean up silly Windows -- stuff?
18682             return; 
18683         }
18684         if (node.nodeName == "#comment") {
18685             node.parentNode.removeChild(node);
18686             // clean up silly Windows -- stuff?
18687             return; 
18688         }
18689         var lcname = node.tagName.toLowerCase();
18690         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
18691         // whitelist of tags..
18692         
18693         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
18694             // remove node.
18695             node.parentNode.removeChild(node);
18696             return;
18697             
18698         }
18699         
18700         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
18701         
18702         // remove <a name=....> as rendering on yahoo mailer is borked with this.
18703         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
18704         
18705         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
18706         //    remove_keep_children = true;
18707         //}
18708         
18709         if (remove_keep_children) {
18710             this.cleanUpChildren(node);
18711             // inserts everything just before this node...
18712             while (node.childNodes.length) {
18713                 var cn = node.childNodes[0];
18714                 node.removeChild(cn);
18715                 node.parentNode.insertBefore(cn, node);
18716             }
18717             node.parentNode.removeChild(node);
18718             return;
18719         }
18720         
18721         if (!node.attributes || !node.attributes.length) {
18722             this.cleanUpChildren(node);
18723             return;
18724         }
18725         
18726         function cleanAttr(n,v)
18727         {
18728             
18729             if (v.match(/^\./) || v.match(/^\//)) {
18730                 return;
18731             }
18732             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
18733                 return;
18734             }
18735             if (v.match(/^#/)) {
18736                 return;
18737             }
18738 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
18739             node.removeAttribute(n);
18740             
18741         }
18742         
18743         var cwhite = this.cwhite;
18744         var cblack = this.cblack;
18745             
18746         function cleanStyle(n,v)
18747         {
18748             if (v.match(/expression/)) { //XSS?? should we even bother..
18749                 node.removeAttribute(n);
18750                 return;
18751             }
18752             
18753             var parts = v.split(/;/);
18754             var clean = [];
18755             
18756             Roo.each(parts, function(p) {
18757                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
18758                 if (!p.length) {
18759                     return true;
18760                 }
18761                 var l = p.split(':').shift().replace(/\s+/g,'');
18762                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
18763                 
18764                 if ( cwhite.length && cblack.indexOf(l) > -1) {
18765 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18766                     //node.removeAttribute(n);
18767                     return true;
18768                 }
18769                 //Roo.log()
18770                 // only allow 'c whitelisted system attributes'
18771                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
18772 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18773                     //node.removeAttribute(n);
18774                     return true;
18775                 }
18776                 
18777                 
18778                  
18779                 
18780                 clean.push(p);
18781                 return true;
18782             });
18783             if (clean.length) { 
18784                 node.setAttribute(n, clean.join(';'));
18785             } else {
18786                 node.removeAttribute(n);
18787             }
18788             
18789         }
18790         
18791         
18792         for (var i = node.attributes.length-1; i > -1 ; i--) {
18793             var a = node.attributes[i];
18794             //console.log(a);
18795             
18796             if (a.name.toLowerCase().substr(0,2)=='on')  {
18797                 node.removeAttribute(a.name);
18798                 continue;
18799             }
18800             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
18801                 node.removeAttribute(a.name);
18802                 continue;
18803             }
18804             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
18805                 cleanAttr(a.name,a.value); // fixme..
18806                 continue;
18807             }
18808             if (a.name == 'style') {
18809                 cleanStyle(a.name,a.value);
18810                 continue;
18811             }
18812             /// clean up MS crap..
18813             // tecnically this should be a list of valid class'es..
18814             
18815             
18816             if (a.name == 'class') {
18817                 if (a.value.match(/^Mso/)) {
18818                     node.className = '';
18819                 }
18820                 
18821                 if (a.value.match(/body/)) {
18822                     node.className = '';
18823                 }
18824                 continue;
18825             }
18826             
18827             // style cleanup!?
18828             // class cleanup?
18829             
18830         }
18831         
18832         
18833         this.cleanUpChildren(node);
18834         
18835         
18836     },
18837     /**
18838      * Clean up MS wordisms...
18839      */
18840     cleanWord : function(node)
18841     {
18842         var _t = this;
18843         var cleanWordChildren = function()
18844         {
18845             if (!node.childNodes.length) {
18846                 return;
18847             }
18848             for (var i = node.childNodes.length-1; i > -1 ; i--) {
18849                _t.cleanWord(node.childNodes[i]);
18850             }
18851         }
18852         
18853         
18854         if (!node) {
18855             this.cleanWord(this.doc.body);
18856             return;
18857         }
18858         if (node.nodeName == "#text") {
18859             // clean up silly Windows -- stuff?
18860             return; 
18861         }
18862         if (node.nodeName == "#comment") {
18863             node.parentNode.removeChild(node);
18864             // clean up silly Windows -- stuff?
18865             return; 
18866         }
18867         
18868         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
18869             node.parentNode.removeChild(node);
18870             return;
18871         }
18872         
18873         // remove - but keep children..
18874         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
18875             while (node.childNodes.length) {
18876                 var cn = node.childNodes[0];
18877                 node.removeChild(cn);
18878                 node.parentNode.insertBefore(cn, node);
18879             }
18880             node.parentNode.removeChild(node);
18881             cleanWordChildren();
18882             return;
18883         }
18884         // clean styles
18885         if (node.className.length) {
18886             
18887             var cn = node.className.split(/\W+/);
18888             var cna = [];
18889             Roo.each(cn, function(cls) {
18890                 if (cls.match(/Mso[a-zA-Z]+/)) {
18891                     return;
18892                 }
18893                 cna.push(cls);
18894             });
18895             node.className = cna.length ? cna.join(' ') : '';
18896             if (!cna.length) {
18897                 node.removeAttribute("class");
18898             }
18899         }
18900         
18901         if (node.hasAttribute("lang")) {
18902             node.removeAttribute("lang");
18903         }
18904         
18905         if (node.hasAttribute("style")) {
18906             
18907             var styles = node.getAttribute("style").split(";");
18908             var nstyle = [];
18909             Roo.each(styles, function(s) {
18910                 if (!s.match(/:/)) {
18911                     return;
18912                 }
18913                 var kv = s.split(":");
18914                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
18915                     return;
18916                 }
18917                 // what ever is left... we allow.
18918                 nstyle.push(s);
18919             });
18920             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
18921             if (!nstyle.length) {
18922                 node.removeAttribute('style');
18923             }
18924         }
18925         
18926         cleanWordChildren();
18927         
18928         
18929     },
18930     domToHTML : function(currentElement, depth, nopadtext) {
18931         
18932         depth = depth || 0;
18933         nopadtext = nopadtext || false;
18934     
18935         if (!currentElement) {
18936             return this.domToHTML(this.doc.body);
18937         }
18938         
18939         //Roo.log(currentElement);
18940         var j;
18941         var allText = false;
18942         var nodeName = currentElement.nodeName;
18943         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
18944         
18945         if  (nodeName == '#text') {
18946             
18947             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
18948         }
18949         
18950         
18951         var ret = '';
18952         if (nodeName != 'BODY') {
18953              
18954             var i = 0;
18955             // Prints the node tagName, such as <A>, <IMG>, etc
18956             if (tagName) {
18957                 var attr = [];
18958                 for(i = 0; i < currentElement.attributes.length;i++) {
18959                     // quoting?
18960                     var aname = currentElement.attributes.item(i).name;
18961                     if (!currentElement.attributes.item(i).value.length) {
18962                         continue;
18963                     }
18964                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
18965                 }
18966                 
18967                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
18968             } 
18969             else {
18970                 
18971                 // eack
18972             }
18973         } else {
18974             tagName = false;
18975         }
18976         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
18977             return ret;
18978         }
18979         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
18980             nopadtext = true;
18981         }
18982         
18983         
18984         // Traverse the tree
18985         i = 0;
18986         var currentElementChild = currentElement.childNodes.item(i);
18987         var allText = true;
18988         var innerHTML  = '';
18989         lastnode = '';
18990         while (currentElementChild) {
18991             // Formatting code (indent the tree so it looks nice on the screen)
18992             var nopad = nopadtext;
18993             if (lastnode == 'SPAN') {
18994                 nopad  = true;
18995             }
18996             // text
18997             if  (currentElementChild.nodeName == '#text') {
18998                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
18999                 toadd = nopadtext ? toadd : toadd.trim();
19000                 if (!nopad && toadd.length > 80) {
19001                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
19002                 }
19003                 innerHTML  += toadd;
19004                 
19005                 i++;
19006                 currentElementChild = currentElement.childNodes.item(i);
19007                 lastNode = '';
19008                 continue;
19009             }
19010             allText = false;
19011             
19012             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
19013                 
19014             // Recursively traverse the tree structure of the child node
19015             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
19016             lastnode = currentElementChild.nodeName;
19017             i++;
19018             currentElementChild=currentElement.childNodes.item(i);
19019         }
19020         
19021         ret += innerHTML;
19022         
19023         if (!allText) {
19024                 // The remaining code is mostly for formatting the tree
19025             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
19026         }
19027         
19028         
19029         if (tagName) {
19030             ret+= "</"+tagName+">";
19031         }
19032         return ret;
19033         
19034     },
19035         
19036     applyBlacklists : function()
19037     {
19038         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
19039         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
19040         
19041         this.white = [];
19042         this.black = [];
19043         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
19044             if (b.indexOf(tag) > -1) {
19045                 return;
19046             }
19047             this.white.push(tag);
19048             
19049         }, this);
19050         
19051         Roo.each(w, function(tag) {
19052             if (b.indexOf(tag) > -1) {
19053                 return;
19054             }
19055             if (this.white.indexOf(tag) > -1) {
19056                 return;
19057             }
19058             this.white.push(tag);
19059             
19060         }, this);
19061         
19062         
19063         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
19064             if (w.indexOf(tag) > -1) {
19065                 return;
19066             }
19067             this.black.push(tag);
19068             
19069         }, this);
19070         
19071         Roo.each(b, function(tag) {
19072             if (w.indexOf(tag) > -1) {
19073                 return;
19074             }
19075             if (this.black.indexOf(tag) > -1) {
19076                 return;
19077             }
19078             this.black.push(tag);
19079             
19080         }, this);
19081         
19082         
19083         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
19084         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
19085         
19086         this.cwhite = [];
19087         this.cblack = [];
19088         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
19089             if (b.indexOf(tag) > -1) {
19090                 return;
19091             }
19092             this.cwhite.push(tag);
19093             
19094         }, this);
19095         
19096         Roo.each(w, function(tag) {
19097             if (b.indexOf(tag) > -1) {
19098                 return;
19099             }
19100             if (this.cwhite.indexOf(tag) > -1) {
19101                 return;
19102             }
19103             this.cwhite.push(tag);
19104             
19105         }, this);
19106         
19107         
19108         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
19109             if (w.indexOf(tag) > -1) {
19110                 return;
19111             }
19112             this.cblack.push(tag);
19113             
19114         }, this);
19115         
19116         Roo.each(b, function(tag) {
19117             if (w.indexOf(tag) > -1) {
19118                 return;
19119             }
19120             if (this.cblack.indexOf(tag) > -1) {
19121                 return;
19122             }
19123             this.cblack.push(tag);
19124             
19125         }, this);
19126     },
19127     
19128     setStylesheets : function(stylesheets)
19129     {
19130         if(typeof(stylesheets) == 'string'){
19131             Roo.get(this.iframe.contentDocument.head).createChild({
19132                 tag : 'link',
19133                 rel : 'stylesheet',
19134                 type : 'text/css',
19135                 href : stylesheets
19136             });
19137             
19138             return;
19139         }
19140         var _this = this;
19141      
19142         Roo.each(stylesheets, function(s) {
19143             if(!s.length){
19144                 return;
19145             }
19146             
19147             Roo.get(_this.iframe.contentDocument.head).createChild({
19148                 tag : 'link',
19149                 rel : 'stylesheet',
19150                 type : 'text/css',
19151                 href : s
19152             });
19153         });
19154
19155         
19156     },
19157     
19158     removeStylesheets : function()
19159     {
19160         var _this = this;
19161         
19162         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
19163             s.remove();
19164         });
19165     }
19166     
19167     // hide stuff that is not compatible
19168     /**
19169      * @event blur
19170      * @hide
19171      */
19172     /**
19173      * @event change
19174      * @hide
19175      */
19176     /**
19177      * @event focus
19178      * @hide
19179      */
19180     /**
19181      * @event specialkey
19182      * @hide
19183      */
19184     /**
19185      * @cfg {String} fieldClass @hide
19186      */
19187     /**
19188      * @cfg {String} focusClass @hide
19189      */
19190     /**
19191      * @cfg {String} autoCreate @hide
19192      */
19193     /**
19194      * @cfg {String} inputType @hide
19195      */
19196     /**
19197      * @cfg {String} invalidClass @hide
19198      */
19199     /**
19200      * @cfg {String} invalidText @hide
19201      */
19202     /**
19203      * @cfg {String} msgFx @hide
19204      */
19205     /**
19206      * @cfg {String} validateOnBlur @hide
19207      */
19208 });
19209
19210 Roo.HtmlEditorCore.white = [
19211         'area', 'br', 'img', 'input', 'hr', 'wbr',
19212         
19213        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
19214        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
19215        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
19216        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
19217        'table',   'ul',         'xmp', 
19218        
19219        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
19220       'thead',   'tr', 
19221      
19222       'dir', 'menu', 'ol', 'ul', 'dl',
19223        
19224       'embed',  'object'
19225 ];
19226
19227
19228 Roo.HtmlEditorCore.black = [
19229     //    'embed',  'object', // enable - backend responsiblity to clean thiese
19230         'applet', // 
19231         'base',   'basefont', 'bgsound', 'blink',  'body', 
19232         'frame',  'frameset', 'head',    'html',   'ilayer', 
19233         'iframe', 'layer',  'link',     'meta',    'object',   
19234         'script', 'style' ,'title',  'xml' // clean later..
19235 ];
19236 Roo.HtmlEditorCore.clean = [
19237     'script', 'style', 'title', 'xml'
19238 ];
19239 Roo.HtmlEditorCore.remove = [
19240     'font'
19241 ];
19242 // attributes..
19243
19244 Roo.HtmlEditorCore.ablack = [
19245     'on'
19246 ];
19247     
19248 Roo.HtmlEditorCore.aclean = [ 
19249     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
19250 ];
19251
19252 // protocols..
19253 Roo.HtmlEditorCore.pwhite= [
19254         'http',  'https',  'mailto'
19255 ];
19256
19257 // white listed style attributes.
19258 Roo.HtmlEditorCore.cwhite= [
19259       //  'text-align', /// default is to allow most things..
19260       
19261          
19262 //        'font-size'//??
19263 ];
19264
19265 // black listed style attributes.
19266 Roo.HtmlEditorCore.cblack= [
19267       //  'font-size' -- this can be set by the project 
19268 ];
19269
19270
19271 Roo.HtmlEditorCore.swapCodes   =[ 
19272     [    8211, "--" ], 
19273     [    8212, "--" ], 
19274     [    8216,  "'" ],  
19275     [    8217, "'" ],  
19276     [    8220, '"' ],  
19277     [    8221, '"' ],  
19278     [    8226, "*" ],  
19279     [    8230, "..." ]
19280 ]; 
19281
19282     /*
19283  * - LGPL
19284  *
19285  * HtmlEditor
19286  * 
19287  */
19288
19289 /**
19290  * @class Roo.bootstrap.HtmlEditor
19291  * @extends Roo.bootstrap.TextArea
19292  * Bootstrap HtmlEditor class
19293
19294  * @constructor
19295  * Create a new HtmlEditor
19296  * @param {Object} config The config object
19297  */
19298
19299 Roo.bootstrap.HtmlEditor = function(config){
19300     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
19301     if (!this.toolbars) {
19302         this.toolbars = [];
19303     }
19304     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
19305     this.addEvents({
19306             /**
19307              * @event initialize
19308              * Fires when the editor is fully initialized (including the iframe)
19309              * @param {HtmlEditor} this
19310              */
19311             initialize: true,
19312             /**
19313              * @event activate
19314              * Fires when the editor is first receives the focus. Any insertion must wait
19315              * until after this event.
19316              * @param {HtmlEditor} this
19317              */
19318             activate: true,
19319              /**
19320              * @event beforesync
19321              * Fires before the textarea is updated with content from the editor iframe. Return false
19322              * to cancel the sync.
19323              * @param {HtmlEditor} this
19324              * @param {String} html
19325              */
19326             beforesync: true,
19327              /**
19328              * @event beforepush
19329              * Fires before the iframe editor is updated with content from the textarea. Return false
19330              * to cancel the push.
19331              * @param {HtmlEditor} this
19332              * @param {String} html
19333              */
19334             beforepush: true,
19335              /**
19336              * @event sync
19337              * Fires when the textarea is updated with content from the editor iframe.
19338              * @param {HtmlEditor} this
19339              * @param {String} html
19340              */
19341             sync: true,
19342              /**
19343              * @event push
19344              * Fires when the iframe editor is updated with content from the textarea.
19345              * @param {HtmlEditor} this
19346              * @param {String} html
19347              */
19348             push: true,
19349              /**
19350              * @event editmodechange
19351              * Fires when the editor switches edit modes
19352              * @param {HtmlEditor} this
19353              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
19354              */
19355             editmodechange: true,
19356             /**
19357              * @event editorevent
19358              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19359              * @param {HtmlEditor} this
19360              */
19361             editorevent: true,
19362             /**
19363              * @event firstfocus
19364              * Fires when on first focus - needed by toolbars..
19365              * @param {HtmlEditor} this
19366              */
19367             firstfocus: true,
19368             /**
19369              * @event autosave
19370              * Auto save the htmlEditor value as a file into Events
19371              * @param {HtmlEditor} this
19372              */
19373             autosave: true,
19374             /**
19375              * @event savedpreview
19376              * preview the saved version of htmlEditor
19377              * @param {HtmlEditor} this
19378              */
19379             savedpreview: true
19380         });
19381 };
19382
19383
19384 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
19385     
19386     
19387       /**
19388      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
19389      */
19390     toolbars : false,
19391    
19392      /**
19393      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19394      *                        Roo.resizable.
19395      */
19396     resizable : false,
19397      /**
19398      * @cfg {Number} height (in pixels)
19399      */   
19400     height: 300,
19401    /**
19402      * @cfg {Number} width (in pixels)
19403      */   
19404     width: false,
19405     
19406     /**
19407      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19408      * 
19409      */
19410     stylesheets: false,
19411     
19412     // id of frame..
19413     frameId: false,
19414     
19415     // private properties
19416     validationEvent : false,
19417     deferHeight: true,
19418     initialized : false,
19419     activated : false,
19420     
19421     onFocus : Roo.emptyFn,
19422     iframePad:3,
19423     hideMode:'offsets',
19424     
19425     
19426     tbContainer : false,
19427     
19428     toolbarContainer :function() {
19429         return this.wrap.select('.x-html-editor-tb',true).first();
19430     },
19431
19432     /**
19433      * Protected method that will not generally be called directly. It
19434      * is called when the editor creates its toolbar. Override this method if you need to
19435      * add custom toolbar buttons.
19436      * @param {HtmlEditor} editor
19437      */
19438     createToolbar : function(){
19439         
19440         Roo.log("create toolbars");
19441         
19442         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
19443         this.toolbars[0].render(this.toolbarContainer());
19444         
19445         return;
19446         
19447 //        if (!editor.toolbars || !editor.toolbars.length) {
19448 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
19449 //        }
19450 //        
19451 //        for (var i =0 ; i < editor.toolbars.length;i++) {
19452 //            editor.toolbars[i] = Roo.factory(
19453 //                    typeof(editor.toolbars[i]) == 'string' ?
19454 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
19455 //                Roo.bootstrap.HtmlEditor);
19456 //            editor.toolbars[i].init(editor);
19457 //        }
19458     },
19459
19460      
19461     // private
19462     onRender : function(ct, position)
19463     {
19464        // Roo.log("Call onRender: " + this.xtype);
19465         var _t = this;
19466         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
19467       
19468         this.wrap = this.inputEl().wrap({
19469             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
19470         });
19471         
19472         this.editorcore.onRender(ct, position);
19473          
19474         if (this.resizable) {
19475             this.resizeEl = new Roo.Resizable(this.wrap, {
19476                 pinned : true,
19477                 wrap: true,
19478                 dynamic : true,
19479                 minHeight : this.height,
19480                 height: this.height,
19481                 handles : this.resizable,
19482                 width: this.width,
19483                 listeners : {
19484                     resize : function(r, w, h) {
19485                         _t.onResize(w,h); // -something
19486                     }
19487                 }
19488             });
19489             
19490         }
19491         this.createToolbar(this);
19492        
19493         
19494         if(!this.width && this.resizable){
19495             this.setSize(this.wrap.getSize());
19496         }
19497         if (this.resizeEl) {
19498             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
19499             // should trigger onReize..
19500         }
19501         
19502     },
19503
19504     // private
19505     onResize : function(w, h)
19506     {
19507         Roo.log('resize: ' +w + ',' + h );
19508         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
19509         var ew = false;
19510         var eh = false;
19511         
19512         if(this.inputEl() ){
19513             if(typeof w == 'number'){
19514                 var aw = w - this.wrap.getFrameWidth('lr');
19515                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
19516                 ew = aw;
19517             }
19518             if(typeof h == 'number'){
19519                  var tbh = -11;  // fixme it needs to tool bar size!
19520                 for (var i =0; i < this.toolbars.length;i++) {
19521                     // fixme - ask toolbars for heights?
19522                     tbh += this.toolbars[i].el.getHeight();
19523                     //if (this.toolbars[i].footer) {
19524                     //    tbh += this.toolbars[i].footer.el.getHeight();
19525                     //}
19526                 }
19527               
19528                 
19529                 
19530                 
19531                 
19532                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
19533                 ah -= 5; // knock a few pixes off for look..
19534                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
19535                 var eh = ah;
19536             }
19537         }
19538         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
19539         this.editorcore.onResize(ew,eh);
19540         
19541     },
19542
19543     /**
19544      * Toggles the editor between standard and source edit mode.
19545      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19546      */
19547     toggleSourceEdit : function(sourceEditMode)
19548     {
19549         this.editorcore.toggleSourceEdit(sourceEditMode);
19550         
19551         if(this.editorcore.sourceEditMode){
19552             Roo.log('editor - showing textarea');
19553             
19554 //            Roo.log('in');
19555 //            Roo.log(this.syncValue());
19556             this.syncValue();
19557             this.inputEl().removeClass(['hide', 'x-hidden']);
19558             this.inputEl().dom.removeAttribute('tabIndex');
19559             this.inputEl().focus();
19560         }else{
19561             Roo.log('editor - hiding textarea');
19562 //            Roo.log('out')
19563 //            Roo.log(this.pushValue()); 
19564             this.pushValue();
19565             
19566             this.inputEl().addClass(['hide', 'x-hidden']);
19567             this.inputEl().dom.setAttribute('tabIndex', -1);
19568             //this.deferFocus();
19569         }
19570          
19571         if(this.resizable){
19572             this.setSize(this.wrap.getSize());
19573         }
19574         
19575         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
19576     },
19577  
19578     // private (for BoxComponent)
19579     adjustSize : Roo.BoxComponent.prototype.adjustSize,
19580
19581     // private (for BoxComponent)
19582     getResizeEl : function(){
19583         return this.wrap;
19584     },
19585
19586     // private (for BoxComponent)
19587     getPositionEl : function(){
19588         return this.wrap;
19589     },
19590
19591     // private
19592     initEvents : function(){
19593         this.originalValue = this.getValue();
19594     },
19595
19596 //    /**
19597 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19598 //     * @method
19599 //     */
19600 //    markInvalid : Roo.emptyFn,
19601 //    /**
19602 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19603 //     * @method
19604 //     */
19605 //    clearInvalid : Roo.emptyFn,
19606
19607     setValue : function(v){
19608         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
19609         this.editorcore.pushValue();
19610     },
19611
19612      
19613     // private
19614     deferFocus : function(){
19615         this.focus.defer(10, this);
19616     },
19617
19618     // doc'ed in Field
19619     focus : function(){
19620         this.editorcore.focus();
19621         
19622     },
19623       
19624
19625     // private
19626     onDestroy : function(){
19627         
19628         
19629         
19630         if(this.rendered){
19631             
19632             for (var i =0; i < this.toolbars.length;i++) {
19633                 // fixme - ask toolbars for heights?
19634                 this.toolbars[i].onDestroy();
19635             }
19636             
19637             this.wrap.dom.innerHTML = '';
19638             this.wrap.remove();
19639         }
19640     },
19641
19642     // private
19643     onFirstFocus : function(){
19644         //Roo.log("onFirstFocus");
19645         this.editorcore.onFirstFocus();
19646          for (var i =0; i < this.toolbars.length;i++) {
19647             this.toolbars[i].onFirstFocus();
19648         }
19649         
19650     },
19651     
19652     // private
19653     syncValue : function()
19654     {   
19655         this.editorcore.syncValue();
19656     },
19657     
19658     pushValue : function()
19659     {   
19660         this.editorcore.pushValue();
19661     }
19662      
19663     
19664     // hide stuff that is not compatible
19665     /**
19666      * @event blur
19667      * @hide
19668      */
19669     /**
19670      * @event change
19671      * @hide
19672      */
19673     /**
19674      * @event focus
19675      * @hide
19676      */
19677     /**
19678      * @event specialkey
19679      * @hide
19680      */
19681     /**
19682      * @cfg {String} fieldClass @hide
19683      */
19684     /**
19685      * @cfg {String} focusClass @hide
19686      */
19687     /**
19688      * @cfg {String} autoCreate @hide
19689      */
19690     /**
19691      * @cfg {String} inputType @hide
19692      */
19693     /**
19694      * @cfg {String} invalidClass @hide
19695      */
19696     /**
19697      * @cfg {String} invalidText @hide
19698      */
19699     /**
19700      * @cfg {String} msgFx @hide
19701      */
19702     /**
19703      * @cfg {String} validateOnBlur @hide
19704      */
19705 });
19706  
19707     
19708    
19709    
19710    
19711       
19712 Roo.namespace('Roo.bootstrap.htmleditor');
19713 /**
19714  * @class Roo.bootstrap.HtmlEditorToolbar1
19715  * Basic Toolbar
19716  * 
19717  * Usage:
19718  *
19719  new Roo.bootstrap.HtmlEditor({
19720     ....
19721     toolbars : [
19722         new Roo.bootstrap.HtmlEditorToolbar1({
19723             disable : { fonts: 1 , format: 1, ..., ... , ...],
19724             btns : [ .... ]
19725         })
19726     }
19727      
19728  * 
19729  * @cfg {Object} disable List of elements to disable..
19730  * @cfg {Array} btns List of additional buttons.
19731  * 
19732  * 
19733  * NEEDS Extra CSS? 
19734  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
19735  */
19736  
19737 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
19738 {
19739     
19740     Roo.apply(this, config);
19741     
19742     // default disabled, based on 'good practice'..
19743     this.disable = this.disable || {};
19744     Roo.applyIf(this.disable, {
19745         fontSize : true,
19746         colors : true,
19747         specialElements : true
19748     });
19749     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
19750     
19751     this.editor = config.editor;
19752     this.editorcore = config.editor.editorcore;
19753     
19754     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
19755     
19756     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
19757     // dont call parent... till later.
19758 }
19759 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
19760      
19761     bar : true,
19762     
19763     editor : false,
19764     editorcore : false,
19765     
19766     
19767     formats : [
19768         "p" ,  
19769         "h1","h2","h3","h4","h5","h6", 
19770         "pre", "code", 
19771         "abbr", "acronym", "address", "cite", "samp", "var",
19772         'div','span'
19773     ],
19774     
19775     onRender : function(ct, position)
19776     {
19777        // Roo.log("Call onRender: " + this.xtype);
19778         
19779        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
19780        Roo.log(this.el);
19781        this.el.dom.style.marginBottom = '0';
19782        var _this = this;
19783        var editorcore = this.editorcore;
19784        var editor= this.editor;
19785        
19786        var children = [];
19787        var btn = function(id,cmd , toggle, handler){
19788        
19789             var  event = toggle ? 'toggle' : 'click';
19790        
19791             var a = {
19792                 size : 'sm',
19793                 xtype: 'Button',
19794                 xns: Roo.bootstrap,
19795                 glyphicon : id,
19796                 cmd : id || cmd,
19797                 enableToggle:toggle !== false,
19798                 //html : 'submit'
19799                 pressed : toggle ? false : null,
19800                 listeners : {}
19801             }
19802             a.listeners[toggle ? 'toggle' : 'click'] = function() {
19803                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
19804             }
19805             children.push(a);
19806             return a;
19807        }
19808         
19809         var style = {
19810                 xtype: 'Button',
19811                 size : 'sm',
19812                 xns: Roo.bootstrap,
19813                 glyphicon : 'font',
19814                 //html : 'submit'
19815                 menu : {
19816                     xtype: 'Menu',
19817                     xns: Roo.bootstrap,
19818                     items:  []
19819                 }
19820         };
19821         Roo.each(this.formats, function(f) {
19822             style.menu.items.push({
19823                 xtype :'MenuItem',
19824                 xns: Roo.bootstrap,
19825                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
19826                 tagname : f,
19827                 listeners : {
19828                     click : function()
19829                     {
19830                         editorcore.insertTag(this.tagname);
19831                         editor.focus();
19832                     }
19833                 }
19834                 
19835             });
19836         });
19837          children.push(style);   
19838             
19839             
19840         btn('bold',false,true);
19841         btn('italic',false,true);
19842         btn('align-left', 'justifyleft',true);
19843         btn('align-center', 'justifycenter',true);
19844         btn('align-right' , 'justifyright',true);
19845         btn('link', false, false, function(btn) {
19846             //Roo.log("create link?");
19847             var url = prompt(this.createLinkText, this.defaultLinkValue);
19848             if(url && url != 'http:/'+'/'){
19849                 this.editorcore.relayCmd('createlink', url);
19850             }
19851         }),
19852         btn('list','insertunorderedlist',true);
19853         btn('pencil', false,true, function(btn){
19854                 Roo.log(this);
19855                 
19856                 this.toggleSourceEdit(btn.pressed);
19857         });
19858         /*
19859         var cog = {
19860                 xtype: 'Button',
19861                 size : 'sm',
19862                 xns: Roo.bootstrap,
19863                 glyphicon : 'cog',
19864                 //html : 'submit'
19865                 menu : {
19866                     xtype: 'Menu',
19867                     xns: Roo.bootstrap,
19868                     items:  []
19869                 }
19870         };
19871         
19872         cog.menu.items.push({
19873             xtype :'MenuItem',
19874             xns: Roo.bootstrap,
19875             html : Clean styles,
19876             tagname : f,
19877             listeners : {
19878                 click : function()
19879                 {
19880                     editorcore.insertTag(this.tagname);
19881                     editor.focus();
19882                 }
19883             }
19884             
19885         });
19886        */
19887         
19888          
19889        this.xtype = 'NavSimplebar';
19890         
19891         for(var i=0;i< children.length;i++) {
19892             
19893             this.buttons.add(this.addxtypeChild(children[i]));
19894             
19895         }
19896         
19897         editor.on('editorevent', this.updateToolbar, this);
19898     },
19899     onBtnClick : function(id)
19900     {
19901        this.editorcore.relayCmd(id);
19902        this.editorcore.focus();
19903     },
19904     
19905     /**
19906      * Protected method that will not generally be called directly. It triggers
19907      * a toolbar update by reading the markup state of the current selection in the editor.
19908      */
19909     updateToolbar: function(){
19910
19911         if(!this.editorcore.activated){
19912             this.editor.onFirstFocus(); // is this neeed?
19913             return;
19914         }
19915
19916         var btns = this.buttons; 
19917         var doc = this.editorcore.doc;
19918         btns.get('bold').setActive(doc.queryCommandState('bold'));
19919         btns.get('italic').setActive(doc.queryCommandState('italic'));
19920         //btns.get('underline').setActive(doc.queryCommandState('underline'));
19921         
19922         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
19923         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
19924         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
19925         
19926         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
19927         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
19928          /*
19929         
19930         var ans = this.editorcore.getAllAncestors();
19931         if (this.formatCombo) {
19932             
19933             
19934             var store = this.formatCombo.store;
19935             this.formatCombo.setValue("");
19936             for (var i =0; i < ans.length;i++) {
19937                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
19938                     // select it..
19939                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
19940                     break;
19941                 }
19942             }
19943         }
19944         
19945         
19946         
19947         // hides menus... - so this cant be on a menu...
19948         Roo.bootstrap.MenuMgr.hideAll();
19949         */
19950         Roo.bootstrap.MenuMgr.hideAll();
19951         //this.editorsyncValue();
19952     },
19953     onFirstFocus: function() {
19954         this.buttons.each(function(item){
19955            item.enable();
19956         });
19957     },
19958     toggleSourceEdit : function(sourceEditMode){
19959         
19960           
19961         if(sourceEditMode){
19962             Roo.log("disabling buttons");
19963            this.buttons.each( function(item){
19964                 if(item.cmd != 'pencil'){
19965                     item.disable();
19966                 }
19967             });
19968           
19969         }else{
19970             Roo.log("enabling buttons");
19971             if(this.editorcore.initialized){
19972                 this.buttons.each( function(item){
19973                     item.enable();
19974                 });
19975             }
19976             
19977         }
19978         Roo.log("calling toggole on editor");
19979         // tell the editor that it's been pressed..
19980         this.editor.toggleSourceEdit(sourceEditMode);
19981        
19982     }
19983 });
19984
19985
19986
19987
19988
19989 /**
19990  * @class Roo.bootstrap.Table.AbstractSelectionModel
19991  * @extends Roo.util.Observable
19992  * Abstract base class for grid SelectionModels.  It provides the interface that should be
19993  * implemented by descendant classes.  This class should not be directly instantiated.
19994  * @constructor
19995  */
19996 Roo.bootstrap.Table.AbstractSelectionModel = function(){
19997     this.locked = false;
19998     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
19999 };
20000
20001
20002 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
20003     /** @ignore Called by the grid automatically. Do not call directly. */
20004     init : function(grid){
20005         this.grid = grid;
20006         this.initEvents();
20007     },
20008
20009     /**
20010      * Locks the selections.
20011      */
20012     lock : function(){
20013         this.locked = true;
20014     },
20015
20016     /**
20017      * Unlocks the selections.
20018      */
20019     unlock : function(){
20020         this.locked = false;
20021     },
20022
20023     /**
20024      * Returns true if the selections are locked.
20025      * @return {Boolean}
20026      */
20027     isLocked : function(){
20028         return this.locked;
20029     }
20030 });
20031 /**
20032  * @extends Roo.bootstrap.Table.AbstractSelectionModel
20033  * @class Roo.bootstrap.Table.RowSelectionModel
20034  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
20035  * It supports multiple selections and keyboard selection/navigation. 
20036  * @constructor
20037  * @param {Object} config
20038  */
20039
20040 Roo.bootstrap.Table.RowSelectionModel = function(config){
20041     Roo.apply(this, config);
20042     this.selections = new Roo.util.MixedCollection(false, function(o){
20043         return o.id;
20044     });
20045
20046     this.last = false;
20047     this.lastActive = false;
20048
20049     this.addEvents({
20050         /**
20051              * @event selectionchange
20052              * Fires when the selection changes
20053              * @param {SelectionModel} this
20054              */
20055             "selectionchange" : true,
20056         /**
20057              * @event afterselectionchange
20058              * Fires after the selection changes (eg. by key press or clicking)
20059              * @param {SelectionModel} this
20060              */
20061             "afterselectionchange" : true,
20062         /**
20063              * @event beforerowselect
20064              * Fires when a row is selected being selected, return false to cancel.
20065              * @param {SelectionModel} this
20066              * @param {Number} rowIndex The selected index
20067              * @param {Boolean} keepExisting False if other selections will be cleared
20068              */
20069             "beforerowselect" : true,
20070         /**
20071              * @event rowselect
20072              * Fires when a row is selected.
20073              * @param {SelectionModel} this
20074              * @param {Number} rowIndex The selected index
20075              * @param {Roo.data.Record} r The record
20076              */
20077             "rowselect" : true,
20078         /**
20079              * @event rowdeselect
20080              * Fires when a row is deselected.
20081              * @param {SelectionModel} this
20082              * @param {Number} rowIndex The selected index
20083              */
20084         "rowdeselect" : true
20085     });
20086     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
20087     this.locked = false;
20088 };
20089
20090 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
20091     /**
20092      * @cfg {Boolean} singleSelect
20093      * True to allow selection of only one row at a time (defaults to false)
20094      */
20095     singleSelect : false,
20096
20097     // private
20098     initEvents : function(){
20099
20100         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
20101             this.grid.on("mousedown", this.handleMouseDown, this);
20102         }else{ // allow click to work like normal
20103             this.grid.on("rowclick", this.handleDragableRowClick, this);
20104         }
20105
20106         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
20107             "up" : function(e){
20108                 if(!e.shiftKey){
20109                     this.selectPrevious(e.shiftKey);
20110                 }else if(this.last !== false && this.lastActive !== false){
20111                     var last = this.last;
20112                     this.selectRange(this.last,  this.lastActive-1);
20113                     this.grid.getView().focusRow(this.lastActive);
20114                     if(last !== false){
20115                         this.last = last;
20116                     }
20117                 }else{
20118                     this.selectFirstRow();
20119                 }
20120                 this.fireEvent("afterselectionchange", this);
20121             },
20122             "down" : function(e){
20123                 if(!e.shiftKey){
20124                     this.selectNext(e.shiftKey);
20125                 }else if(this.last !== false && this.lastActive !== false){
20126                     var last = this.last;
20127                     this.selectRange(this.last,  this.lastActive+1);
20128                     this.grid.getView().focusRow(this.lastActive);
20129                     if(last !== false){
20130                         this.last = last;
20131                     }
20132                 }else{
20133                     this.selectFirstRow();
20134                 }
20135                 this.fireEvent("afterselectionchange", this);
20136             },
20137             scope: this
20138         });
20139
20140         var view = this.grid.view;
20141         view.on("refresh", this.onRefresh, this);
20142         view.on("rowupdated", this.onRowUpdated, this);
20143         view.on("rowremoved", this.onRemove, this);
20144     },
20145
20146     // private
20147     onRefresh : function(){
20148         var ds = this.grid.dataSource, i, v = this.grid.view;
20149         var s = this.selections;
20150         s.each(function(r){
20151             if((i = ds.indexOfId(r.id)) != -1){
20152                 v.onRowSelect(i);
20153             }else{
20154                 s.remove(r);
20155             }
20156         });
20157     },
20158
20159     // private
20160     onRemove : function(v, index, r){
20161         this.selections.remove(r);
20162     },
20163
20164     // private
20165     onRowUpdated : function(v, index, r){
20166         if(this.isSelected(r)){
20167             v.onRowSelect(index);
20168         }
20169     },
20170
20171     /**
20172      * Select records.
20173      * @param {Array} records The records to select
20174      * @param {Boolean} keepExisting (optional) True to keep existing selections
20175      */
20176     selectRecords : function(records, keepExisting){
20177         if(!keepExisting){
20178             this.clearSelections();
20179         }
20180         var ds = this.grid.dataSource;
20181         for(var i = 0, len = records.length; i < len; i++){
20182             this.selectRow(ds.indexOf(records[i]), true);
20183         }
20184     },
20185
20186     /**
20187      * Gets the number of selected rows.
20188      * @return {Number}
20189      */
20190     getCount : function(){
20191         return this.selections.length;
20192     },
20193
20194     /**
20195      * Selects the first row in the grid.
20196      */
20197     selectFirstRow : function(){
20198         this.selectRow(0);
20199     },
20200
20201     /**
20202      * Select the last row.
20203      * @param {Boolean} keepExisting (optional) True to keep existing selections
20204      */
20205     selectLastRow : function(keepExisting){
20206         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
20207     },
20208
20209     /**
20210      * Selects the row immediately following the last selected row.
20211      * @param {Boolean} keepExisting (optional) True to keep existing selections
20212      */
20213     selectNext : function(keepExisting){
20214         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
20215             this.selectRow(this.last+1, keepExisting);
20216             this.grid.getView().focusRow(this.last);
20217         }
20218     },
20219
20220     /**
20221      * Selects the row that precedes the last selected row.
20222      * @param {Boolean} keepExisting (optional) True to keep existing selections
20223      */
20224     selectPrevious : function(keepExisting){
20225         if(this.last){
20226             this.selectRow(this.last-1, keepExisting);
20227             this.grid.getView().focusRow(this.last);
20228         }
20229     },
20230
20231     /**
20232      * Returns the selected records
20233      * @return {Array} Array of selected records
20234      */
20235     getSelections : function(){
20236         return [].concat(this.selections.items);
20237     },
20238
20239     /**
20240      * Returns the first selected record.
20241      * @return {Record}
20242      */
20243     getSelected : function(){
20244         return this.selections.itemAt(0);
20245     },
20246
20247
20248     /**
20249      * Clears all selections.
20250      */
20251     clearSelections : function(fast){
20252         if(this.locked) return;
20253         if(fast !== true){
20254             var ds = this.grid.dataSource;
20255             var s = this.selections;
20256             s.each(function(r){
20257                 this.deselectRow(ds.indexOfId(r.id));
20258             }, this);
20259             s.clear();
20260         }else{
20261             this.selections.clear();
20262         }
20263         this.last = false;
20264     },
20265
20266
20267     /**
20268      * Selects all rows.
20269      */
20270     selectAll : function(){
20271         if(this.locked) return;
20272         this.selections.clear();
20273         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
20274             this.selectRow(i, true);
20275         }
20276     },
20277
20278     /**
20279      * Returns True if there is a selection.
20280      * @return {Boolean}
20281      */
20282     hasSelection : function(){
20283         return this.selections.length > 0;
20284     },
20285
20286     /**
20287      * Returns True if the specified row is selected.
20288      * @param {Number/Record} record The record or index of the record to check
20289      * @return {Boolean}
20290      */
20291     isSelected : function(index){
20292         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
20293         return (r && this.selections.key(r.id) ? true : false);
20294     },
20295
20296     /**
20297      * Returns True if the specified record id is selected.
20298      * @param {String} id The id of record to check
20299      * @return {Boolean}
20300      */
20301     isIdSelected : function(id){
20302         return (this.selections.key(id) ? true : false);
20303     },
20304
20305     // private
20306     handleMouseDown : function(e, t){
20307         var view = this.grid.getView(), rowIndex;
20308         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
20309             return;
20310         };
20311         if(e.shiftKey && this.last !== false){
20312             var last = this.last;
20313             this.selectRange(last, rowIndex, e.ctrlKey);
20314             this.last = last; // reset the last
20315             view.focusRow(rowIndex);
20316         }else{
20317             var isSelected = this.isSelected(rowIndex);
20318             if(e.button !== 0 && isSelected){
20319                 view.focusRow(rowIndex);
20320             }else if(e.ctrlKey && isSelected){
20321                 this.deselectRow(rowIndex);
20322             }else if(!isSelected){
20323                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
20324                 view.focusRow(rowIndex);
20325             }
20326         }
20327         this.fireEvent("afterselectionchange", this);
20328     },
20329     // private
20330     handleDragableRowClick :  function(grid, rowIndex, e) 
20331     {
20332         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
20333             this.selectRow(rowIndex, false);
20334             grid.view.focusRow(rowIndex);
20335              this.fireEvent("afterselectionchange", this);
20336         }
20337     },
20338     
20339     /**
20340      * Selects multiple rows.
20341      * @param {Array} rows Array of the indexes of the row to select
20342      * @param {Boolean} keepExisting (optional) True to keep existing selections
20343      */
20344     selectRows : function(rows, keepExisting){
20345         if(!keepExisting){
20346             this.clearSelections();
20347         }
20348         for(var i = 0, len = rows.length; i < len; i++){
20349             this.selectRow(rows[i], true);
20350         }
20351     },
20352
20353     /**
20354      * Selects a range of rows. All rows in between startRow and endRow are also selected.
20355      * @param {Number} startRow The index of the first row in the range
20356      * @param {Number} endRow The index of the last row in the range
20357      * @param {Boolean} keepExisting (optional) True to retain existing selections
20358      */
20359     selectRange : function(startRow, endRow, keepExisting){
20360         if(this.locked) return;
20361         if(!keepExisting){
20362             this.clearSelections();
20363         }
20364         if(startRow <= endRow){
20365             for(var i = startRow; i <= endRow; i++){
20366                 this.selectRow(i, true);
20367             }
20368         }else{
20369             for(var i = startRow; i >= endRow; i--){
20370                 this.selectRow(i, true);
20371             }
20372         }
20373     },
20374
20375     /**
20376      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
20377      * @param {Number} startRow The index of the first row in the range
20378      * @param {Number} endRow The index of the last row in the range
20379      */
20380     deselectRange : function(startRow, endRow, preventViewNotify){
20381         if(this.locked) return;
20382         for(var i = startRow; i <= endRow; i++){
20383             this.deselectRow(i, preventViewNotify);
20384         }
20385     },
20386
20387     /**
20388      * Selects a row.
20389      * @param {Number} row The index of the row to select
20390      * @param {Boolean} keepExisting (optional) True to keep existing selections
20391      */
20392     selectRow : function(index, keepExisting, preventViewNotify){
20393         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
20394         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
20395             if(!keepExisting || this.singleSelect){
20396                 this.clearSelections();
20397             }
20398             var r = this.grid.dataSource.getAt(index);
20399             this.selections.add(r);
20400             this.last = this.lastActive = index;
20401             if(!preventViewNotify){
20402                 this.grid.getView().onRowSelect(index);
20403             }
20404             this.fireEvent("rowselect", this, index, r);
20405             this.fireEvent("selectionchange", this);
20406         }
20407     },
20408
20409     /**
20410      * Deselects a row.
20411      * @param {Number} row The index of the row to deselect
20412      */
20413     deselectRow : function(index, preventViewNotify){
20414         if(this.locked) return;
20415         if(this.last == index){
20416             this.last = false;
20417         }
20418         if(this.lastActive == index){
20419             this.lastActive = false;
20420         }
20421         var r = this.grid.dataSource.getAt(index);
20422         this.selections.remove(r);
20423         if(!preventViewNotify){
20424             this.grid.getView().onRowDeselect(index);
20425         }
20426         this.fireEvent("rowdeselect", this, index);
20427         this.fireEvent("selectionchange", this);
20428     },
20429
20430     // private
20431     restoreLast : function(){
20432         if(this._last){
20433             this.last = this._last;
20434         }
20435     },
20436
20437     // private
20438     acceptsNav : function(row, col, cm){
20439         return !cm.isHidden(col) && cm.isCellEditable(col, row);
20440     },
20441
20442     // private
20443     onEditorKey : function(field, e){
20444         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
20445         if(k == e.TAB){
20446             e.stopEvent();
20447             ed.completeEdit();
20448             if(e.shiftKey){
20449                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
20450             }else{
20451                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
20452             }
20453         }else if(k == e.ENTER && !e.ctrlKey){
20454             e.stopEvent();
20455             ed.completeEdit();
20456             if(e.shiftKey){
20457                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
20458             }else{
20459                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
20460             }
20461         }else if(k == e.ESC){
20462             ed.cancelEdit();
20463         }
20464         if(newCell){
20465             g.startEditing(newCell[0], newCell[1]);
20466         }
20467     }
20468 });/*
20469  * Based on:
20470  * Ext JS Library 1.1.1
20471  * Copyright(c) 2006-2007, Ext JS, LLC.
20472  *
20473  * Originally Released Under LGPL - original licence link has changed is not relivant.
20474  *
20475  * Fork - LGPL
20476  * <script type="text/javascript">
20477  */
20478  
20479 /**
20480  * @class Roo.bootstrap.PagingToolbar
20481  * @extends Roo.Row
20482  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
20483  * @constructor
20484  * Create a new PagingToolbar
20485  * @param {Object} config The config object
20486  */
20487 Roo.bootstrap.PagingToolbar = function(config)
20488 {
20489     // old args format still supported... - xtype is prefered..
20490         // created from xtype...
20491     var ds = config.dataSource;
20492     this.toolbarItems = [];
20493     if (config.items) {
20494         this.toolbarItems = config.items;
20495 //        config.items = [];
20496     }
20497     
20498     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
20499     this.ds = ds;
20500     this.cursor = 0;
20501     if (ds) { 
20502         this.bind(ds);
20503     }
20504     
20505     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
20506     
20507 };
20508
20509 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
20510     /**
20511      * @cfg {Roo.data.Store} dataSource
20512      * The underlying data store providing the paged data
20513      */
20514     /**
20515      * @cfg {String/HTMLElement/Element} container
20516      * container The id or element that will contain the toolbar
20517      */
20518     /**
20519      * @cfg {Boolean} displayInfo
20520      * True to display the displayMsg (defaults to false)
20521      */
20522     /**
20523      * @cfg {Number} pageSize
20524      * The number of records to display per page (defaults to 20)
20525      */
20526     pageSize: 20,
20527     /**
20528      * @cfg {String} displayMsg
20529      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
20530      */
20531     displayMsg : 'Displaying {0} - {1} of {2}',
20532     /**
20533      * @cfg {String} emptyMsg
20534      * The message to display when no records are found (defaults to "No data to display")
20535      */
20536     emptyMsg : 'No data to display',
20537     /**
20538      * Customizable piece of the default paging text (defaults to "Page")
20539      * @type String
20540      */
20541     beforePageText : "Page",
20542     /**
20543      * Customizable piece of the default paging text (defaults to "of %0")
20544      * @type String
20545      */
20546     afterPageText : "of {0}",
20547     /**
20548      * Customizable piece of the default paging text (defaults to "First Page")
20549      * @type String
20550      */
20551     firstText : "First Page",
20552     /**
20553      * Customizable piece of the default paging text (defaults to "Previous Page")
20554      * @type String
20555      */
20556     prevText : "Previous Page",
20557     /**
20558      * Customizable piece of the default paging text (defaults to "Next Page")
20559      * @type String
20560      */
20561     nextText : "Next Page",
20562     /**
20563      * Customizable piece of the default paging text (defaults to "Last Page")
20564      * @type String
20565      */
20566     lastText : "Last Page",
20567     /**
20568      * Customizable piece of the default paging text (defaults to "Refresh")
20569      * @type String
20570      */
20571     refreshText : "Refresh",
20572
20573     buttons : false,
20574     // private
20575     onRender : function(ct, position) 
20576     {
20577         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
20578         this.navgroup.parentId = this.id;
20579         this.navgroup.onRender(this.el, null);
20580         // add the buttons to the navgroup
20581         
20582         if(this.displayInfo){
20583             Roo.log(this.el.select('ul.navbar-nav',true).first());
20584             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
20585             this.displayEl = this.el.select('.x-paging-info', true).first();
20586 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
20587 //            this.displayEl = navel.el.select('span',true).first();
20588         }
20589         
20590         var _this = this;
20591         
20592         if(this.buttons){
20593             Roo.each(_this.buttons, function(e){
20594                Roo.factory(e).onRender(_this.el, null);
20595             });
20596         }
20597             
20598         Roo.each(_this.toolbarItems, function(e) {
20599             _this.navgroup.addItem(e);
20600         });
20601         
20602         
20603         this.first = this.navgroup.addItem({
20604             tooltip: this.firstText,
20605             cls: "prev",
20606             icon : 'fa fa-backward',
20607             disabled: true,
20608             preventDefault: true,
20609             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
20610         });
20611         
20612         this.prev =  this.navgroup.addItem({
20613             tooltip: this.prevText,
20614             cls: "prev",
20615             icon : 'fa fa-step-backward',
20616             disabled: true,
20617             preventDefault: true,
20618             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
20619         });
20620     //this.addSeparator();
20621         
20622         
20623         var field = this.navgroup.addItem( {
20624             tagtype : 'span',
20625             cls : 'x-paging-position',
20626             
20627             html : this.beforePageText  +
20628                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
20629                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
20630          } ); //?? escaped?
20631         
20632         this.field = field.el.select('input', true).first();
20633         this.field.on("keydown", this.onPagingKeydown, this);
20634         this.field.on("focus", function(){this.dom.select();});
20635     
20636     
20637         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
20638         //this.field.setHeight(18);
20639         //this.addSeparator();
20640         this.next = this.navgroup.addItem({
20641             tooltip: this.nextText,
20642             cls: "next",
20643             html : ' <i class="fa fa-step-forward">',
20644             disabled: true,
20645             preventDefault: true,
20646             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
20647         });
20648         this.last = this.navgroup.addItem({
20649             tooltip: this.lastText,
20650             icon : 'fa fa-forward',
20651             cls: "next",
20652             disabled: true,
20653             preventDefault: true,
20654             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
20655         });
20656     //this.addSeparator();
20657         this.loading = this.navgroup.addItem({
20658             tooltip: this.refreshText,
20659             icon: 'fa fa-refresh',
20660             preventDefault: true,
20661             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
20662         });
20663
20664     },
20665
20666     // private
20667     updateInfo : function(){
20668         if(this.displayEl){
20669             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
20670             var msg = count == 0 ?
20671                 this.emptyMsg :
20672                 String.format(
20673                     this.displayMsg,
20674                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
20675                 );
20676             this.displayEl.update(msg);
20677         }
20678     },
20679
20680     // private
20681     onLoad : function(ds, r, o){
20682        this.cursor = o.params ? o.params.start : 0;
20683        var d = this.getPageData(),
20684             ap = d.activePage,
20685             ps = d.pages;
20686         
20687        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
20688        this.field.dom.value = ap;
20689        this.first.setDisabled(ap == 1);
20690        this.prev.setDisabled(ap == 1);
20691        this.next.setDisabled(ap == ps);
20692        this.last.setDisabled(ap == ps);
20693        this.loading.enable();
20694        this.updateInfo();
20695     },
20696
20697     // private
20698     getPageData : function(){
20699         var total = this.ds.getTotalCount();
20700         return {
20701             total : total,
20702             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
20703             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
20704         };
20705     },
20706
20707     // private
20708     onLoadError : function(){
20709         this.loading.enable();
20710     },
20711
20712     // private
20713     onPagingKeydown : function(e){
20714         var k = e.getKey();
20715         var d = this.getPageData();
20716         if(k == e.RETURN){
20717             var v = this.field.dom.value, pageNum;
20718             if(!v || isNaN(pageNum = parseInt(v, 10))){
20719                 this.field.dom.value = d.activePage;
20720                 return;
20721             }
20722             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
20723             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20724             e.stopEvent();
20725         }
20726         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))
20727         {
20728           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
20729           this.field.dom.value = pageNum;
20730           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
20731           e.stopEvent();
20732         }
20733         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20734         {
20735           var v = this.field.dom.value, pageNum; 
20736           var increment = (e.shiftKey) ? 10 : 1;
20737           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20738             increment *= -1;
20739           if(!v || isNaN(pageNum = parseInt(v, 10))) {
20740             this.field.dom.value = d.activePage;
20741             return;
20742           }
20743           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
20744           {
20745             this.field.dom.value = parseInt(v, 10) + increment;
20746             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
20747             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20748           }
20749           e.stopEvent();
20750         }
20751     },
20752
20753     // private
20754     beforeLoad : function(){
20755         if(this.loading){
20756             this.loading.disable();
20757         }
20758     },
20759
20760     // private
20761     onClick : function(which){
20762         
20763         var ds = this.ds;
20764         if (!ds) {
20765             return;
20766         }
20767         
20768         switch(which){
20769             case "first":
20770                 ds.load({params:{start: 0, limit: this.pageSize}});
20771             break;
20772             case "prev":
20773                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
20774             break;
20775             case "next":
20776                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
20777             break;
20778             case "last":
20779                 var total = ds.getTotalCount();
20780                 var extra = total % this.pageSize;
20781                 var lastStart = extra ? (total - extra) : total-this.pageSize;
20782                 ds.load({params:{start: lastStart, limit: this.pageSize}});
20783             break;
20784             case "refresh":
20785                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
20786             break;
20787         }
20788     },
20789
20790     /**
20791      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
20792      * @param {Roo.data.Store} store The data store to unbind
20793      */
20794     unbind : function(ds){
20795         ds.un("beforeload", this.beforeLoad, this);
20796         ds.un("load", this.onLoad, this);
20797         ds.un("loadexception", this.onLoadError, this);
20798         ds.un("remove", this.updateInfo, this);
20799         ds.un("add", this.updateInfo, this);
20800         this.ds = undefined;
20801     },
20802
20803     /**
20804      * Binds the paging toolbar to the specified {@link Roo.data.Store}
20805      * @param {Roo.data.Store} store The data store to bind
20806      */
20807     bind : function(ds){
20808         ds.on("beforeload", this.beforeLoad, this);
20809         ds.on("load", this.onLoad, this);
20810         ds.on("loadexception", this.onLoadError, this);
20811         ds.on("remove", this.updateInfo, this);
20812         ds.on("add", this.updateInfo, this);
20813         this.ds = ds;
20814     }
20815 });/*
20816  * - LGPL
20817  *
20818  * element
20819  * 
20820  */
20821
20822 /**
20823  * @class Roo.bootstrap.MessageBar
20824  * @extends Roo.bootstrap.Component
20825  * Bootstrap MessageBar class
20826  * @cfg {String} html contents of the MessageBar
20827  * @cfg {String} weight (info | success | warning | danger) default info
20828  * @cfg {String} beforeClass insert the bar before the given class
20829  * @cfg {Boolean} closable (true | false) default false
20830  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
20831  * 
20832  * @constructor
20833  * Create a new Element
20834  * @param {Object} config The config object
20835  */
20836
20837 Roo.bootstrap.MessageBar = function(config){
20838     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
20839 };
20840
20841 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
20842     
20843     html: '',
20844     weight: 'info',
20845     closable: false,
20846     fixed: false,
20847     beforeClass: 'bootstrap-sticky-wrap',
20848     
20849     getAutoCreate : function(){
20850         
20851         var cfg = {
20852             tag: 'div',
20853             cls: 'alert alert-dismissable alert-' + this.weight,
20854             cn: [
20855                 {
20856                     tag: 'span',
20857                     cls: 'message',
20858                     html: this.html || ''
20859                 }
20860             ]
20861         }
20862         
20863         if(this.fixed){
20864             cfg.cls += ' alert-messages-fixed';
20865         }
20866         
20867         if(this.closable){
20868             cfg.cn.push({
20869                 tag: 'button',
20870                 cls: 'close',
20871                 html: 'x'
20872             });
20873         }
20874         
20875         return cfg;
20876     },
20877     
20878     onRender : function(ct, position)
20879     {
20880         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20881         
20882         if(!this.el){
20883             var cfg = Roo.apply({},  this.getAutoCreate());
20884             cfg.id = Roo.id();
20885             
20886             if (this.cls) {
20887                 cfg.cls += ' ' + this.cls;
20888             }
20889             if (this.style) {
20890                 cfg.style = this.style;
20891             }
20892             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
20893             
20894             this.el.setVisibilityMode(Roo.Element.DISPLAY);
20895         }
20896         
20897         this.el.select('>button.close').on('click', this.hide, this);
20898         
20899     },
20900     
20901     show : function()
20902     {
20903         if (!this.rendered) {
20904             this.render();
20905         }
20906         
20907         this.el.show();
20908         
20909         this.fireEvent('show', this);
20910         
20911     },
20912     
20913     hide : function()
20914     {
20915         if (!this.rendered) {
20916             this.render();
20917         }
20918         
20919         this.el.hide();
20920         
20921         this.fireEvent('hide', this);
20922     },
20923     
20924     update : function()
20925     {
20926 //        var e = this.el.dom.firstChild;
20927 //        
20928 //        if(this.closable){
20929 //            e = e.nextSibling;
20930 //        }
20931 //        
20932 //        e.data = this.html || '';
20933
20934         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
20935     }
20936    
20937 });
20938
20939  
20940
20941      /*
20942  * - LGPL
20943  *
20944  * Graph
20945  * 
20946  */
20947
20948
20949 /**
20950  * @class Roo.bootstrap.Graph
20951  * @extends Roo.bootstrap.Component
20952  * Bootstrap Graph class
20953 > Prameters
20954  -sm {number} sm 4
20955  -md {number} md 5
20956  @cfg {String} graphtype  bar | vbar | pie
20957  @cfg {number} g_x coodinator | centre x (pie)
20958  @cfg {number} g_y coodinator | centre y (pie)
20959  @cfg {number} g_r radius (pie)
20960  @cfg {number} g_height height of the chart (respected by all elements in the set)
20961  @cfg {number} g_width width of the chart (respected by all elements in the set)
20962  @cfg {Object} title The title of the chart
20963     
20964  -{Array}  values
20965  -opts (object) options for the chart 
20966      o {
20967      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
20968      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
20969      o vgutter (number)
20970      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.
20971      o stacked (boolean) whether or not to tread values as in a stacked bar chart
20972      o to
20973      o stretch (boolean)
20974      o }
20975  -opts (object) options for the pie
20976      o{
20977      o cut
20978      o startAngle (number)
20979      o endAngle (number)
20980      } 
20981  *
20982  * @constructor
20983  * Create a new Input
20984  * @param {Object} config The config object
20985  */
20986
20987 Roo.bootstrap.Graph = function(config){
20988     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
20989     
20990     this.addEvents({
20991         // img events
20992         /**
20993          * @event click
20994          * The img click event for the img.
20995          * @param {Roo.EventObject} e
20996          */
20997         "click" : true
20998     });
20999 };
21000
21001 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
21002     
21003     sm: 4,
21004     md: 5,
21005     graphtype: 'bar',
21006     g_height: 250,
21007     g_width: 400,
21008     g_x: 50,
21009     g_y: 50,
21010     g_r: 30,
21011     opts:{
21012         //g_colors: this.colors,
21013         g_type: 'soft',
21014         g_gutter: '20%'
21015
21016     },
21017     title : false,
21018
21019     getAutoCreate : function(){
21020         
21021         var cfg = {
21022             tag: 'div',
21023             html : null
21024         }
21025         
21026         
21027         return  cfg;
21028     },
21029
21030     onRender : function(ct,position){
21031         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
21032         this.raphael = Raphael(this.el.dom);
21033         
21034                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21035                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21036                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21037                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
21038                 /*
21039                 r.text(160, 10, "Single Series Chart").attr(txtattr);
21040                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
21041                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
21042                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
21043                 
21044                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
21045                 r.barchart(330, 10, 300, 220, data1);
21046                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
21047                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
21048                 */
21049                 
21050                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21051                 // r.barchart(30, 30, 560, 250,  xdata, {
21052                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
21053                 //     axis : "0 0 1 1",
21054                 //     axisxlabels :  xdata
21055                 //     //yvalues : cols,
21056                    
21057                 // });
21058 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21059 //        
21060 //        this.load(null,xdata,{
21061 //                axis : "0 0 1 1",
21062 //                axisxlabels :  xdata
21063 //                });
21064
21065     },
21066
21067     load : function(graphtype,xdata,opts){
21068         this.raphael.clear();
21069         if(!graphtype) {
21070             graphtype = this.graphtype;
21071         }
21072         if(!opts){
21073             opts = this.opts;
21074         }
21075         var r = this.raphael,
21076             fin = function () {
21077                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
21078             },
21079             fout = function () {
21080                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
21081             },
21082             pfin = function() {
21083                 this.sector.stop();
21084                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
21085
21086                 if (this.label) {
21087                     this.label[0].stop();
21088                     this.label[0].attr({ r: 7.5 });
21089                     this.label[1].attr({ "font-weight": 800 });
21090                 }
21091             },
21092             pfout = function() {
21093                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
21094
21095                 if (this.label) {
21096                     this.label[0].animate({ r: 5 }, 500, "bounce");
21097                     this.label[1].attr({ "font-weight": 400 });
21098                 }
21099             };
21100
21101         switch(graphtype){
21102             case 'bar':
21103                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21104                 break;
21105             case 'hbar':
21106                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21107                 break;
21108             case 'pie':
21109 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
21110 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
21111 //            
21112                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
21113                 
21114                 break;
21115
21116         }
21117         
21118         if(this.title){
21119             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
21120         }
21121         
21122     },
21123     
21124     setTitle: function(o)
21125     {
21126         this.title = o;
21127     },
21128     
21129     initEvents: function() {
21130         
21131         if(!this.href){
21132             this.el.on('click', this.onClick, this);
21133         }
21134     },
21135     
21136     onClick : function(e)
21137     {
21138         Roo.log('img onclick');
21139         this.fireEvent('click', this, e);
21140     }
21141    
21142 });
21143
21144  
21145 /*
21146  * - LGPL
21147  *
21148  * numberBox
21149  * 
21150  */
21151 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21152
21153 /**
21154  * @class Roo.bootstrap.dash.NumberBox
21155  * @extends Roo.bootstrap.Component
21156  * Bootstrap NumberBox class
21157  * @cfg {String} headline Box headline
21158  * @cfg {String} content Box content
21159  * @cfg {String} icon Box icon
21160  * @cfg {String} footer Footer text
21161  * @cfg {String} fhref Footer href
21162  * 
21163  * @constructor
21164  * Create a new NumberBox
21165  * @param {Object} config The config object
21166  */
21167
21168
21169 Roo.bootstrap.dash.NumberBox = function(config){
21170     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
21171     
21172 };
21173
21174 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
21175     
21176     headline : '',
21177     content : '',
21178     icon : '',
21179     footer : '',
21180     fhref : '',
21181     ficon : '',
21182     
21183     getAutoCreate : function(){
21184         
21185         var cfg = {
21186             tag : 'div',
21187             cls : 'small-box ',
21188             cn : [
21189                 {
21190                     tag : 'div',
21191                     cls : 'inner',
21192                     cn :[
21193                         {
21194                             tag : 'h3',
21195                             cls : 'roo-headline',
21196                             html : this.headline
21197                         },
21198                         {
21199                             tag : 'p',
21200                             cls : 'roo-content',
21201                             html : this.content
21202                         }
21203                     ]
21204                 }
21205             ]
21206         }
21207         
21208         if(this.icon){
21209             cfg.cn.push({
21210                 tag : 'div',
21211                 cls : 'icon',
21212                 cn :[
21213                     {
21214                         tag : 'i',
21215                         cls : 'ion ' + this.icon
21216                     }
21217                 ]
21218             });
21219         }
21220         
21221         if(this.footer){
21222             var footer = {
21223                 tag : 'a',
21224                 cls : 'small-box-footer',
21225                 href : this.fhref || '#',
21226                 html : this.footer
21227             };
21228             
21229             cfg.cn.push(footer);
21230             
21231         }
21232         
21233         return  cfg;
21234     },
21235
21236     onRender : function(ct,position){
21237         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
21238
21239
21240        
21241                 
21242     },
21243
21244     setHeadline: function (value)
21245     {
21246         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
21247     },
21248     
21249     setFooter: function (value, href)
21250     {
21251         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
21252         
21253         if(href){
21254             this.el.select('a.small-box-footer',true).first().attr('href', href);
21255         }
21256         
21257     },
21258
21259     setContent: function (value)
21260     {
21261         this.el.select('.roo-content',true).first().dom.innerHTML = value;
21262     },
21263
21264     initEvents: function() 
21265     {   
21266         
21267     }
21268     
21269 });
21270
21271  
21272 /*
21273  * - LGPL
21274  *
21275  * TabBox
21276  * 
21277  */
21278 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21279
21280 /**
21281  * @class Roo.bootstrap.dash.TabBox
21282  * @extends Roo.bootstrap.Component
21283  * Bootstrap TabBox class
21284  * @cfg {String} title Title of the TabBox
21285  * @cfg {String} icon Icon of the TabBox
21286  * @cfg {Boolean} showtabs (true|false) show the tabs default true
21287  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
21288  * 
21289  * @constructor
21290  * Create a new TabBox
21291  * @param {Object} config The config object
21292  */
21293
21294
21295 Roo.bootstrap.dash.TabBox = function(config){
21296     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
21297     this.addEvents({
21298         // raw events
21299         /**
21300          * @event addpane
21301          * When a pane is added
21302          * @param {Roo.bootstrap.dash.TabPane} pane
21303          */
21304         "addpane" : true,
21305         /**
21306          * @event activatepane
21307          * When a pane is activated
21308          * @param {Roo.bootstrap.dash.TabPane} pane
21309          */
21310         "activatepane" : true
21311         
21312          
21313     });
21314     
21315     this.panes = [];
21316 };
21317
21318 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
21319
21320     title : '',
21321     icon : false,
21322     showtabs : true,
21323     tabScrollable : false,
21324     
21325     getChildContainer : function()
21326     {
21327         return this.el.select('.tab-content', true).first();
21328     },
21329     
21330     getAutoCreate : function(){
21331         
21332         var header = {
21333             tag: 'li',
21334             cls: 'pull-left header',
21335             html: this.title,
21336             cn : []
21337         };
21338         
21339         if(this.icon){
21340             header.cn.push({
21341                 tag: 'i',
21342                 cls: 'fa ' + this.icon
21343             });
21344         }
21345         
21346         var h = {
21347             tag: 'ul',
21348             cls: 'nav nav-tabs pull-right',
21349             cn: [
21350                 header
21351             ]
21352         };
21353         
21354         if(this.tabScrollable){
21355             h = {
21356                 tag: 'div',
21357                 cls: 'tab-header',
21358                 cn: [
21359                     {
21360                         tag: 'ul',
21361                         cls: 'nav nav-tabs pull-right',
21362                         cn: [
21363                             header
21364                         ]
21365                     }
21366                 ]
21367             }
21368         }
21369         
21370         var cfg = {
21371             tag: 'div',
21372             cls: 'nav-tabs-custom',
21373             cn: [
21374                 h,
21375                 {
21376                     tag: 'div',
21377                     cls: 'tab-content no-padding',
21378                     cn: []
21379                 }
21380             ]
21381         }
21382
21383         return  cfg;
21384     },
21385     initEvents : function()
21386     {
21387         //Roo.log('add add pane handler');
21388         this.on('addpane', this.onAddPane, this);
21389     },
21390      /**
21391      * Updates the box title
21392      * @param {String} html to set the title to.
21393      */
21394     setTitle : function(value)
21395     {
21396         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
21397     },
21398     onAddPane : function(pane)
21399     {
21400         this.panes.push(pane);
21401         //Roo.log('addpane');
21402         //Roo.log(pane);
21403         // tabs are rendere left to right..
21404         if(!this.showtabs){
21405             return;
21406         }
21407         
21408         var ctr = this.el.select('.nav-tabs', true).first();
21409          
21410          
21411         var existing = ctr.select('.nav-tab',true);
21412         var qty = existing.getCount();;
21413         
21414         
21415         var tab = ctr.createChild({
21416             tag : 'li',
21417             cls : 'nav-tab' + (qty ? '' : ' active'),
21418             cn : [
21419                 {
21420                     tag : 'a',
21421                     href:'#',
21422                     html : pane.title
21423                 }
21424             ]
21425         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
21426         pane.tab = tab;
21427         
21428         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
21429         if (!qty) {
21430             pane.el.addClass('active');
21431         }
21432         
21433                 
21434     },
21435     onTabClick : function(ev,un,ob,pane)
21436     {
21437         //Roo.log('tab - prev default');
21438         ev.preventDefault();
21439         
21440         
21441         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
21442         pane.tab.addClass('active');
21443         //Roo.log(pane.title);
21444         this.getChildContainer().select('.tab-pane',true).removeClass('active');
21445         // technically we should have a deactivate event.. but maybe add later.
21446         // and it should not de-activate the selected tab...
21447         this.fireEvent('activatepane', pane);
21448         pane.el.addClass('active');
21449         pane.fireEvent('activate');
21450         
21451         
21452     },
21453     
21454     getActivePane : function()
21455     {
21456         var r = false;
21457         Roo.each(this.panes, function(p) {
21458             if(p.el.hasClass('active')){
21459                 r = p;
21460                 return false;
21461             }
21462             
21463             return;
21464         });
21465         
21466         return r;
21467     }
21468     
21469     
21470 });
21471
21472  
21473 /*
21474  * - LGPL
21475  *
21476  * Tab pane
21477  * 
21478  */
21479 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21480 /**
21481  * @class Roo.bootstrap.TabPane
21482  * @extends Roo.bootstrap.Component
21483  * Bootstrap TabPane class
21484  * @cfg {Boolean} active (false | true) Default false
21485  * @cfg {String} title title of panel
21486
21487  * 
21488  * @constructor
21489  * Create a new TabPane
21490  * @param {Object} config The config object
21491  */
21492
21493 Roo.bootstrap.dash.TabPane = function(config){
21494     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
21495     
21496     this.addEvents({
21497         // raw events
21498         /**
21499          * @event activate
21500          * When a pane is activated
21501          * @param {Roo.bootstrap.dash.TabPane} pane
21502          */
21503         "activate" : true
21504          
21505     });
21506 };
21507
21508 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
21509     
21510     active : false,
21511     title : '',
21512     
21513     // the tabBox that this is attached to.
21514     tab : false,
21515      
21516     getAutoCreate : function() 
21517     {
21518         var cfg = {
21519             tag: 'div',
21520             cls: 'tab-pane'
21521         }
21522         
21523         if(this.active){
21524             cfg.cls += ' active';
21525         }
21526         
21527         return cfg;
21528     },
21529     initEvents  : function()
21530     {
21531         //Roo.log('trigger add pane handler');
21532         this.parent().fireEvent('addpane', this)
21533     },
21534     
21535      /**
21536      * Updates the tab title 
21537      * @param {String} html to set the title to.
21538      */
21539     setTitle: function(str)
21540     {
21541         if (!this.tab) {
21542             return;
21543         }
21544         this.title = str;
21545         this.tab.select('a', true).first().dom.innerHTML = str;
21546         
21547     }
21548     
21549     
21550     
21551 });
21552
21553  
21554
21555
21556  /*
21557  * - LGPL
21558  *
21559  * menu
21560  * 
21561  */
21562 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21563
21564 /**
21565  * @class Roo.bootstrap.menu.Menu
21566  * @extends Roo.bootstrap.Component
21567  * Bootstrap Menu class - container for Menu
21568  * @cfg {String} html Text of the menu
21569  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
21570  * @cfg {String} icon Font awesome icon
21571  * @cfg {String} pos Menu align to (top | bottom) default bottom
21572  * 
21573  * 
21574  * @constructor
21575  * Create a new Menu
21576  * @param {Object} config The config object
21577  */
21578
21579
21580 Roo.bootstrap.menu.Menu = function(config){
21581     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
21582     
21583     this.addEvents({
21584         /**
21585          * @event beforeshow
21586          * Fires before this menu is displayed
21587          * @param {Roo.bootstrap.menu.Menu} this
21588          */
21589         beforeshow : true,
21590         /**
21591          * @event beforehide
21592          * Fires before this menu is hidden
21593          * @param {Roo.bootstrap.menu.Menu} this
21594          */
21595         beforehide : true,
21596         /**
21597          * @event show
21598          * Fires after this menu is displayed
21599          * @param {Roo.bootstrap.menu.Menu} this
21600          */
21601         show : true,
21602         /**
21603          * @event hide
21604          * Fires after this menu is hidden
21605          * @param {Roo.bootstrap.menu.Menu} this
21606          */
21607         hide : true,
21608         /**
21609          * @event click
21610          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
21611          * @param {Roo.bootstrap.menu.Menu} this
21612          * @param {Roo.EventObject} e
21613          */
21614         click : true
21615     });
21616     
21617 };
21618
21619 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
21620     
21621     submenu : false,
21622     html : '',
21623     weight : 'default',
21624     icon : false,
21625     pos : 'bottom',
21626     
21627     
21628     getChildContainer : function() {
21629         if(this.isSubMenu){
21630             return this.el;
21631         }
21632         
21633         return this.el.select('ul.dropdown-menu', true).first();  
21634     },
21635     
21636     getAutoCreate : function()
21637     {
21638         var text = [
21639             {
21640                 tag : 'span',
21641                 cls : 'roo-menu-text',
21642                 html : this.html
21643             }
21644         ];
21645         
21646         if(this.icon){
21647             text.unshift({
21648                 tag : 'i',
21649                 cls : 'fa ' + this.icon
21650             })
21651         }
21652         
21653         
21654         var cfg = {
21655             tag : 'div',
21656             cls : 'btn-group',
21657             cn : [
21658                 {
21659                     tag : 'button',
21660                     cls : 'dropdown-button btn btn-' + this.weight,
21661                     cn : text
21662                 },
21663                 {
21664                     tag : 'button',
21665                     cls : 'dropdown-toggle btn btn-' + this.weight,
21666                     cn : [
21667                         {
21668                             tag : 'span',
21669                             cls : 'caret'
21670                         }
21671                     ]
21672                 },
21673                 {
21674                     tag : 'ul',
21675                     cls : 'dropdown-menu'
21676                 }
21677             ]
21678             
21679         };
21680         
21681         if(this.pos == 'top'){
21682             cfg.cls += ' dropup';
21683         }
21684         
21685         if(this.isSubMenu){
21686             cfg = {
21687                 tag : 'ul',
21688                 cls : 'dropdown-menu'
21689             }
21690         }
21691         
21692         return cfg;
21693     },
21694     
21695     onRender : function(ct, position)
21696     {
21697         this.isSubMenu = ct.hasClass('dropdown-submenu');
21698         
21699         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
21700     },
21701     
21702     initEvents : function() 
21703     {
21704         if(this.isSubMenu){
21705             return;
21706         }
21707         
21708         this.hidden = true;
21709         
21710         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
21711         this.triggerEl.on('click', this.onTriggerPress, this);
21712         
21713         this.buttonEl = this.el.select('button.dropdown-button', true).first();
21714         this.buttonEl.on('click', this.onClick, this);
21715         
21716     },
21717     
21718     list : function()
21719     {
21720         if(this.isSubMenu){
21721             return this.el;
21722         }
21723         
21724         return this.el.select('ul.dropdown-menu', true).first();
21725     },
21726     
21727     onClick : function(e)
21728     {
21729         this.fireEvent("click", this, e);
21730     },
21731     
21732     onTriggerPress  : function(e)
21733     {   
21734         if (this.isVisible()) {
21735             this.hide();
21736         } else {
21737             this.show();
21738         }
21739     },
21740     
21741     isVisible : function(){
21742         return !this.hidden;
21743     },
21744     
21745     show : function()
21746     {
21747         this.fireEvent("beforeshow", this);
21748         
21749         this.hidden = false;
21750         this.el.addClass('open');
21751         
21752         Roo.get(document).on("mouseup", this.onMouseUp, this);
21753         
21754         this.fireEvent("show", this);
21755         
21756         
21757     },
21758     
21759     hide : function()
21760     {
21761         this.fireEvent("beforehide", this);
21762         
21763         this.hidden = true;
21764         this.el.removeClass('open');
21765         
21766         Roo.get(document).un("mouseup", this.onMouseUp);
21767         
21768         this.fireEvent("hide", this);
21769     },
21770     
21771     onMouseUp : function()
21772     {
21773         this.hide();
21774     }
21775     
21776 });
21777
21778  
21779  /*
21780  * - LGPL
21781  *
21782  * menu item
21783  * 
21784  */
21785 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21786
21787 /**
21788  * @class Roo.bootstrap.menu.Item
21789  * @extends Roo.bootstrap.Component
21790  * Bootstrap MenuItem class
21791  * @cfg {Boolean} submenu (true | false) default false
21792  * @cfg {String} html text of the item
21793  * @cfg {String} href the link
21794  * @cfg {Boolean} disable (true | false) default false
21795  * @cfg {Boolean} preventDefault (true | false) default true
21796  * @cfg {String} icon Font awesome icon
21797  * @cfg {String} pos Submenu align to (left | right) default right 
21798  * 
21799  * 
21800  * @constructor
21801  * Create a new Item
21802  * @param {Object} config The config object
21803  */
21804
21805
21806 Roo.bootstrap.menu.Item = function(config){
21807     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
21808     this.addEvents({
21809         /**
21810          * @event mouseover
21811          * Fires when the mouse is hovering over this menu
21812          * @param {Roo.bootstrap.menu.Item} this
21813          * @param {Roo.EventObject} e
21814          */
21815         mouseover : true,
21816         /**
21817          * @event mouseout
21818          * Fires when the mouse exits this menu
21819          * @param {Roo.bootstrap.menu.Item} this
21820          * @param {Roo.EventObject} e
21821          */
21822         mouseout : true,
21823         // raw events
21824         /**
21825          * @event click
21826          * The raw click event for the entire grid.
21827          * @param {Roo.EventObject} e
21828          */
21829         click : true
21830     });
21831 };
21832
21833 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
21834     
21835     submenu : false,
21836     href : '',
21837     html : '',
21838     preventDefault: true,
21839     disable : false,
21840     icon : false,
21841     pos : 'right',
21842     
21843     getAutoCreate : function()
21844     {
21845         var text = [
21846             {
21847                 tag : 'span',
21848                 cls : 'roo-menu-item-text',
21849                 html : this.html
21850             }
21851         ];
21852         
21853         if(this.icon){
21854             text.unshift({
21855                 tag : 'i',
21856                 cls : 'fa ' + this.icon
21857             })
21858         }
21859         
21860         var cfg = {
21861             tag : 'li',
21862             cn : [
21863                 {
21864                     tag : 'a',
21865                     href : this.href || '#',
21866                     cn : text
21867                 }
21868             ]
21869         };
21870         
21871         if(this.disable){
21872             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
21873         }
21874         
21875         if(this.submenu){
21876             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
21877             
21878             if(this.pos == 'left'){
21879                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
21880             }
21881         }
21882         
21883         return cfg;
21884     },
21885     
21886     initEvents : function() 
21887     {
21888         this.el.on('mouseover', this.onMouseOver, this);
21889         this.el.on('mouseout', this.onMouseOut, this);
21890         
21891         this.el.select('a', true).first().on('click', this.onClick, this);
21892         
21893     },
21894     
21895     onClick : function(e)
21896     {
21897         if(this.preventDefault){
21898             e.preventDefault();
21899         }
21900         
21901         this.fireEvent("click", this, e);
21902     },
21903     
21904     onMouseOver : function(e)
21905     {
21906         if(this.submenu && this.pos == 'left'){
21907             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
21908         }
21909         
21910         this.fireEvent("mouseover", this, e);
21911     },
21912     
21913     onMouseOut : function(e)
21914     {
21915         this.fireEvent("mouseout", this, e);
21916     }
21917 });
21918
21919  
21920
21921  /*
21922  * - LGPL
21923  *
21924  * menu separator
21925  * 
21926  */
21927 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21928
21929 /**
21930  * @class Roo.bootstrap.menu.Separator
21931  * @extends Roo.bootstrap.Component
21932  * Bootstrap Separator class
21933  * 
21934  * @constructor
21935  * Create a new Separator
21936  * @param {Object} config The config object
21937  */
21938
21939
21940 Roo.bootstrap.menu.Separator = function(config){
21941     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
21942 };
21943
21944 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
21945     
21946     getAutoCreate : function(){
21947         var cfg = {
21948             tag : 'li',
21949             cls: 'divider'
21950         };
21951         
21952         return cfg;
21953     }
21954    
21955 });
21956
21957  
21958
21959  /*
21960  * - LGPL
21961  *
21962  * Tooltip
21963  * 
21964  */
21965
21966 /**
21967  * @class Roo.bootstrap.Tooltip
21968  * Bootstrap Tooltip class
21969  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
21970  * to determine which dom element triggers the tooltip.
21971  * 
21972  * It needs to add support for additional attributes like tooltip-position
21973  * 
21974  * @constructor
21975  * Create a new Toolti
21976  * @param {Object} config The config object
21977  */
21978
21979 Roo.bootstrap.Tooltip = function(config){
21980     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
21981 };
21982
21983 Roo.apply(Roo.bootstrap.Tooltip, {
21984     /**
21985      * @function init initialize tooltip monitoring.
21986      * @static
21987      */
21988     currentEl : false,
21989     currentTip : false,
21990     currentRegion : false,
21991     
21992     //  init : delay?
21993     
21994     init : function()
21995     {
21996         Roo.get(document).on('mouseover', this.enter ,this);
21997         Roo.get(document).on('mouseout', this.leave, this);
21998          
21999         
22000         this.currentTip = new Roo.bootstrap.Tooltip();
22001     },
22002     
22003     enter : function(ev)
22004     {
22005         var dom = ev.getTarget();
22006         
22007         //Roo.log(['enter',dom]);
22008         var el = Roo.fly(dom);
22009         if (this.currentEl) {
22010             //Roo.log(dom);
22011             //Roo.log(this.currentEl);
22012             //Roo.log(this.currentEl.contains(dom));
22013             if (this.currentEl == el) {
22014                 return;
22015             }
22016             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
22017                 return;
22018             }
22019
22020         }
22021         
22022         
22023         
22024         if (this.currentTip.el) {
22025             this.currentTip.el.hide(); // force hiding...
22026         }    
22027         //Roo.log(ev);
22028         var bindEl = el;
22029         
22030         // you can not look for children, as if el is the body.. then everythign is the child..
22031         if (!el.attr('tooltip')) { //
22032             if (!el.select("[tooltip]").elements.length) {
22033                 return;
22034             }
22035             // is the mouse over this child...?
22036             bindEl = el.select("[tooltip]").first();
22037             var xy = ev.getXY();
22038             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
22039                 //Roo.log("not in region.");
22040                 return;
22041             }
22042             //Roo.log("child element over..");
22043             
22044         }
22045         this.currentEl = bindEl;
22046         this.currentTip.bind(bindEl);
22047         this.currentRegion = Roo.lib.Region.getRegion(dom);
22048         this.currentTip.enter();
22049         
22050     },
22051     leave : function(ev)
22052     {
22053         var dom = ev.getTarget();
22054         //Roo.log(['leave',dom]);
22055         if (!this.currentEl) {
22056             return;
22057         }
22058         
22059         
22060         if (dom != this.currentEl.dom) {
22061             return;
22062         }
22063         var xy = ev.getXY();
22064         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
22065             return;
22066         }
22067         // only activate leave if mouse cursor is outside... bounding box..
22068         
22069         
22070         
22071         
22072         if (this.currentTip) {
22073             this.currentTip.leave();
22074         }
22075         //Roo.log('clear currentEl');
22076         this.currentEl = false;
22077         
22078         
22079     },
22080     alignment : {
22081         'left' : ['r-l', [-2,0], 'right'],
22082         'right' : ['l-r', [2,0], 'left'],
22083         'bottom' : ['t-b', [0,2], 'top'],
22084         'top' : [ 'b-t', [0,-2], 'bottom']
22085     }
22086     
22087 });
22088
22089
22090 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
22091     
22092     
22093     bindEl : false,
22094     
22095     delay : null, // can be { show : 300 , hide: 500}
22096     
22097     timeout : null,
22098     
22099     hoverState : null, //???
22100     
22101     placement : 'bottom', 
22102     
22103     getAutoCreate : function(){
22104     
22105         var cfg = {
22106            cls : 'tooltip',
22107            role : 'tooltip',
22108            cn : [
22109                 {
22110                     cls : 'tooltip-arrow'
22111                 },
22112                 {
22113                     cls : 'tooltip-inner'
22114                 }
22115            ]
22116         };
22117         
22118         return cfg;
22119     },
22120     bind : function(el)
22121     {
22122         this.bindEl = el;
22123     },
22124       
22125     
22126     enter : function () {
22127        
22128         if (this.timeout != null) {
22129             clearTimeout(this.timeout);
22130         }
22131         
22132         this.hoverState = 'in';
22133          //Roo.log("enter - show");
22134         if (!this.delay || !this.delay.show) {
22135             this.show();
22136             return;
22137         }
22138         var _t = this;
22139         this.timeout = setTimeout(function () {
22140             if (_t.hoverState == 'in') {
22141                 _t.show();
22142             }
22143         }, this.delay.show);
22144     },
22145     leave : function()
22146     {
22147         clearTimeout(this.timeout);
22148     
22149         this.hoverState = 'out';
22150          if (!this.delay || !this.delay.hide) {
22151             this.hide();
22152             return;
22153         }
22154        
22155         var _t = this;
22156         this.timeout = setTimeout(function () {
22157             //Roo.log("leave - timeout");
22158             
22159             if (_t.hoverState == 'out') {
22160                 _t.hide();
22161                 Roo.bootstrap.Tooltip.currentEl = false;
22162             }
22163         }, delay);
22164     },
22165     
22166     show : function ()
22167     {
22168         if (!this.el) {
22169             this.render(document.body);
22170         }
22171         // set content.
22172         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
22173         
22174         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
22175         
22176         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
22177         
22178         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
22179         
22180         var placement = typeof this.placement == 'function' ?
22181             this.placement.call(this, this.el, on_el) :
22182             this.placement;
22183             
22184         var autoToken = /\s?auto?\s?/i;
22185         var autoPlace = autoToken.test(placement);
22186         if (autoPlace) {
22187             placement = placement.replace(autoToken, '') || 'top';
22188         }
22189         
22190         //this.el.detach()
22191         //this.el.setXY([0,0]);
22192         this.el.show();
22193         //this.el.dom.style.display='block';
22194         this.el.addClass(placement);
22195         
22196         //this.el.appendTo(on_el);
22197         
22198         var p = this.getPosition();
22199         var box = this.el.getBox();
22200         
22201         if (autoPlace) {
22202             // fixme..
22203         }
22204         var align = Roo.bootstrap.Tooltip.alignment[placement];
22205         this.el.alignTo(this.bindEl, align[0],align[1]);
22206         //var arrow = this.el.select('.arrow',true).first();
22207         //arrow.set(align[2], 
22208         
22209         this.el.addClass('in fade');
22210         this.hoverState = null;
22211         
22212         if (this.el.hasClass('fade')) {
22213             // fade it?
22214         }
22215         
22216     },
22217     hide : function()
22218     {
22219          
22220         if (!this.el) {
22221             return;
22222         }
22223         //this.el.setXY([0,0]);
22224         this.el.removeClass('in');
22225         //this.el.hide();
22226         
22227     }
22228     
22229 });
22230  
22231
22232  /*
22233  * - LGPL
22234  *
22235  * Location Picker
22236  * 
22237  */
22238
22239 /**
22240  * @class Roo.bootstrap.LocationPicker
22241  * @extends Roo.bootstrap.Component
22242  * Bootstrap LocationPicker class
22243  * @cfg {Number} latitude Position when init default 0
22244  * @cfg {Number} longitude Position when init default 0
22245  * @cfg {Number} zoom default 15
22246  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
22247  * @cfg {Boolean} mapTypeControl default false
22248  * @cfg {Boolean} disableDoubleClickZoom default false
22249  * @cfg {Boolean} scrollwheel default true
22250  * @cfg {Boolean} streetViewControl default false
22251  * @cfg {Number} radius default 0
22252  * @cfg {String} locationName
22253  * @cfg {Boolean} draggable default true
22254  * @cfg {Boolean} enableAutocomplete default false
22255  * @cfg {Boolean} enableReverseGeocode default true
22256  * @cfg {String} markerTitle
22257  * 
22258  * @constructor
22259  * Create a new LocationPicker
22260  * @param {Object} config The config object
22261  */
22262
22263
22264 Roo.bootstrap.LocationPicker = function(config){
22265     
22266     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
22267     
22268     this.addEvents({
22269         /**
22270          * @event initial
22271          * Fires when the picker initialized.
22272          * @param {Roo.bootstrap.LocationPicker} this
22273          * @param {Google Location} location
22274          */
22275         initial : true,
22276         /**
22277          * @event positionchanged
22278          * Fires when the picker position changed.
22279          * @param {Roo.bootstrap.LocationPicker} this
22280          * @param {Google Location} location
22281          */
22282         positionchanged : true,
22283         /**
22284          * @event resize
22285          * Fires when the map resize.
22286          * @param {Roo.bootstrap.LocationPicker} this
22287          */
22288         resize : true,
22289         /**
22290          * @event show
22291          * Fires when the map show.
22292          * @param {Roo.bootstrap.LocationPicker} this
22293          */
22294         show : true,
22295         /**
22296          * @event hide
22297          * Fires when the map hide.
22298          * @param {Roo.bootstrap.LocationPicker} this
22299          */
22300         hide : true,
22301         /**
22302          * @event mapClick
22303          * Fires when click the map.
22304          * @param {Roo.bootstrap.LocationPicker} this
22305          * @param {Map event} e
22306          */
22307         mapClick : true,
22308         /**
22309          * @event mapRightClick
22310          * Fires when right click the map.
22311          * @param {Roo.bootstrap.LocationPicker} this
22312          * @param {Map event} e
22313          */
22314         mapRightClick : true,
22315         /**
22316          * @event markerClick
22317          * Fires when click the marker.
22318          * @param {Roo.bootstrap.LocationPicker} this
22319          * @param {Map event} e
22320          */
22321         markerClick : true,
22322         /**
22323          * @event markerRightClick
22324          * Fires when right click the marker.
22325          * @param {Roo.bootstrap.LocationPicker} this
22326          * @param {Map event} e
22327          */
22328         markerRightClick : true,
22329         /**
22330          * @event OverlayViewDraw
22331          * Fires when OverlayView Draw
22332          * @param {Roo.bootstrap.LocationPicker} this
22333          */
22334         OverlayViewDraw : true,
22335         /**
22336          * @event OverlayViewOnAdd
22337          * Fires when OverlayView Draw
22338          * @param {Roo.bootstrap.LocationPicker} this
22339          */
22340         OverlayViewOnAdd : true,
22341         /**
22342          * @event OverlayViewOnRemove
22343          * Fires when OverlayView Draw
22344          * @param {Roo.bootstrap.LocationPicker} this
22345          */
22346         OverlayViewOnRemove : true,
22347         /**
22348          * @event OverlayViewShow
22349          * Fires when OverlayView Draw
22350          * @param {Roo.bootstrap.LocationPicker} this
22351          * @param {Pixel} cpx
22352          */
22353         OverlayViewShow : true,
22354         /**
22355          * @event OverlayViewHide
22356          * Fires when OverlayView Draw
22357          * @param {Roo.bootstrap.LocationPicker} this
22358          */
22359         OverlayViewHide : true
22360     });
22361         
22362 };
22363
22364 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
22365     
22366     gMapContext: false,
22367     
22368     latitude: 0,
22369     longitude: 0,
22370     zoom: 15,
22371     mapTypeId: false,
22372     mapTypeControl: false,
22373     disableDoubleClickZoom: false,
22374     scrollwheel: true,
22375     streetViewControl: false,
22376     radius: 0,
22377     locationName: '',
22378     draggable: true,
22379     enableAutocomplete: false,
22380     enableReverseGeocode: true,
22381     markerTitle: '',
22382     
22383     getAutoCreate: function()
22384     {
22385
22386         var cfg = {
22387             tag: 'div',
22388             cls: 'roo-location-picker'
22389         };
22390         
22391         return cfg
22392     },
22393     
22394     initEvents: function(ct, position)
22395     {       
22396         if(!this.el.getWidth() || this.isApplied()){
22397             return;
22398         }
22399         
22400         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22401         
22402         this.initial();
22403     },
22404     
22405     initial: function()
22406     {
22407         if(!this.mapTypeId){
22408             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
22409         }
22410         
22411         this.gMapContext = this.GMapContext();
22412         
22413         this.initOverlayView();
22414         
22415         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
22416         
22417         var _this = this;
22418                 
22419         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
22420             _this.setPosition(_this.gMapContext.marker.position);
22421         });
22422         
22423         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
22424             _this.fireEvent('mapClick', this, event);
22425             
22426         });
22427
22428         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
22429             _this.fireEvent('mapRightClick', this, event);
22430             
22431         });
22432         
22433         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
22434             _this.fireEvent('markerClick', this, event);
22435             
22436         });
22437
22438         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
22439             _this.fireEvent('markerRightClick', this, event);
22440             
22441         });
22442         
22443         this.setPosition(this.gMapContext.location);
22444         
22445         this.fireEvent('initial', this, this.gMapContext.location);
22446     },
22447     
22448     initOverlayView: function()
22449     {
22450         var _this = this;
22451         
22452         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
22453             
22454             draw: function()
22455             {
22456                 _this.fireEvent('OverlayViewDraw', _this);
22457             },
22458             
22459             onAdd: function()
22460             {
22461                 _this.fireEvent('OverlayViewOnAdd', _this);
22462             },
22463             
22464             onRemove: function()
22465             {
22466                 _this.fireEvent('OverlayViewOnRemove', _this);
22467             },
22468             
22469             show: function(cpx)
22470             {
22471                 _this.fireEvent('OverlayViewShow', _this, cpx);
22472             },
22473             
22474             hide: function()
22475             {
22476                 _this.fireEvent('OverlayViewHide', _this);
22477             }
22478             
22479         });
22480     },
22481     
22482     fromLatLngToContainerPixel: function(event)
22483     {
22484         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
22485     },
22486     
22487     isApplied: function() 
22488     {
22489         return this.getGmapContext() == false ? false : true;
22490     },
22491     
22492     getGmapContext: function() 
22493     {
22494         return this.gMapContext
22495     },
22496     
22497     GMapContext: function() 
22498     {
22499         var position = new google.maps.LatLng(this.latitude, this.longitude);
22500         
22501         var _map = new google.maps.Map(this.el.dom, {
22502             center: position,
22503             zoom: this.zoom,
22504             mapTypeId: this.mapTypeId,
22505             mapTypeControl: this.mapTypeControl,
22506             disableDoubleClickZoom: this.disableDoubleClickZoom,
22507             scrollwheel: this.scrollwheel,
22508             streetViewControl: this.streetViewControl,
22509             locationName: this.locationName,
22510             draggable: this.draggable,
22511             enableAutocomplete: this.enableAutocomplete,
22512             enableReverseGeocode: this.enableReverseGeocode
22513         });
22514         
22515         var _marker = new google.maps.Marker({
22516             position: position,
22517             map: _map,
22518             title: this.markerTitle,
22519             draggable: this.draggable
22520         });
22521         
22522         return {
22523             map: _map,
22524             marker: _marker,
22525             circle: null,
22526             location: position,
22527             radius: this.radius,
22528             locationName: this.locationName,
22529             addressComponents: {
22530                 formatted_address: null,
22531                 addressLine1: null,
22532                 addressLine2: null,
22533                 streetName: null,
22534                 streetNumber: null,
22535                 city: null,
22536                 district: null,
22537                 state: null,
22538                 stateOrProvince: null
22539             },
22540             settings: this,
22541             domContainer: this.el.dom,
22542             geodecoder: new google.maps.Geocoder()
22543         };
22544     },
22545     
22546     drawCircle: function(center, radius, options) 
22547     {
22548         if (this.gMapContext.circle != null) {
22549             this.gMapContext.circle.setMap(null);
22550         }
22551         if (radius > 0) {
22552             radius *= 1;
22553             options = Roo.apply({}, options, {
22554                 strokeColor: "#0000FF",
22555                 strokeOpacity: .35,
22556                 strokeWeight: 2,
22557                 fillColor: "#0000FF",
22558                 fillOpacity: .2
22559             });
22560             
22561             options.map = this.gMapContext.map;
22562             options.radius = radius;
22563             options.center = center;
22564             this.gMapContext.circle = new google.maps.Circle(options);
22565             return this.gMapContext.circle;
22566         }
22567         
22568         return null;
22569     },
22570     
22571     setPosition: function(location) 
22572     {
22573         this.gMapContext.location = location;
22574         this.gMapContext.marker.setPosition(location);
22575         this.gMapContext.map.panTo(location);
22576         this.drawCircle(location, this.gMapContext.radius, {});
22577         
22578         var _this = this;
22579         
22580         if (this.gMapContext.settings.enableReverseGeocode) {
22581             this.gMapContext.geodecoder.geocode({
22582                 latLng: this.gMapContext.location
22583             }, function(results, status) {
22584                 
22585                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
22586                     _this.gMapContext.locationName = results[0].formatted_address;
22587                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
22588                     
22589                     _this.fireEvent('positionchanged', this, location);
22590                 }
22591             });
22592             
22593             return;
22594         }
22595         
22596         this.fireEvent('positionchanged', this, location);
22597     },
22598     
22599     resize: function()
22600     {
22601         google.maps.event.trigger(this.gMapContext.map, "resize");
22602         
22603         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
22604         
22605         this.fireEvent('resize', this);
22606     },
22607     
22608     setPositionByLatLng: function(latitude, longitude)
22609     {
22610         this.setPosition(new google.maps.LatLng(latitude, longitude));
22611     },
22612     
22613     getCurrentPosition: function() 
22614     {
22615         return {
22616             latitude: this.gMapContext.location.lat(),
22617             longitude: this.gMapContext.location.lng()
22618         };
22619     },
22620     
22621     getAddressName: function() 
22622     {
22623         return this.gMapContext.locationName;
22624     },
22625     
22626     getAddressComponents: function() 
22627     {
22628         return this.gMapContext.addressComponents;
22629     },
22630     
22631     address_component_from_google_geocode: function(address_components) 
22632     {
22633         var result = {};
22634         
22635         for (var i = 0; i < address_components.length; i++) {
22636             var component = address_components[i];
22637             if (component.types.indexOf("postal_code") >= 0) {
22638                 result.postalCode = component.short_name;
22639             } else if (component.types.indexOf("street_number") >= 0) {
22640                 result.streetNumber = component.short_name;
22641             } else if (component.types.indexOf("route") >= 0) {
22642                 result.streetName = component.short_name;
22643             } else if (component.types.indexOf("neighborhood") >= 0) {
22644                 result.city = component.short_name;
22645             } else if (component.types.indexOf("locality") >= 0) {
22646                 result.city = component.short_name;
22647             } else if (component.types.indexOf("sublocality") >= 0) {
22648                 result.district = component.short_name;
22649             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
22650                 result.stateOrProvince = component.short_name;
22651             } else if (component.types.indexOf("country") >= 0) {
22652                 result.country = component.short_name;
22653             }
22654         }
22655         
22656         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
22657         result.addressLine2 = "";
22658         return result;
22659     },
22660     
22661     setZoomLevel: function(zoom)
22662     {
22663         this.gMapContext.map.setZoom(zoom);
22664     },
22665     
22666     show: function()
22667     {
22668         if(!this.el){
22669             return;
22670         }
22671         
22672         this.el.show();
22673         
22674         this.resize();
22675         
22676         this.fireEvent('show', this);
22677     },
22678     
22679     hide: function()
22680     {
22681         if(!this.el){
22682             return;
22683         }
22684         
22685         this.el.hide();
22686         
22687         this.fireEvent('hide', this);
22688     }
22689     
22690 });
22691
22692 Roo.apply(Roo.bootstrap.LocationPicker, {
22693     
22694     OverlayView : function(map, options)
22695     {
22696         options = options || {};
22697         
22698         this.setMap(map);
22699     }
22700     
22701     
22702 });/*
22703  * - LGPL
22704  *
22705  * Alert
22706  * 
22707  */
22708
22709 /**
22710  * @class Roo.bootstrap.Alert
22711  * @extends Roo.bootstrap.Component
22712  * Bootstrap Alert class
22713  * @cfg {String} title The title of alert
22714  * @cfg {String} html The content of alert
22715  * @cfg {String} weight (  success | info | warning | danger )
22716  * @cfg {String} faicon font-awesomeicon
22717  * 
22718  * @constructor
22719  * Create a new alert
22720  * @param {Object} config The config object
22721  */
22722
22723
22724 Roo.bootstrap.Alert = function(config){
22725     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
22726     
22727 };
22728
22729 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
22730     
22731     title: '',
22732     html: '',
22733     weight: false,
22734     faicon: false,
22735     
22736     getAutoCreate : function()
22737     {
22738         
22739         var cfg = {
22740             tag : 'div',
22741             cls : 'alert',
22742             cn : [
22743                 {
22744                     tag : 'i',
22745                     cls : 'roo-alert-icon'
22746                     
22747                 },
22748                 {
22749                     tag : 'b',
22750                     cls : 'roo-alert-title',
22751                     html : this.title
22752                 },
22753                 {
22754                     tag : 'span',
22755                     cls : 'roo-alert-text',
22756                     html : this.html
22757                 }
22758             ]
22759         };
22760         
22761         if(this.faicon){
22762             cfg.cn[0].cls += ' fa ' + this.faicon;
22763         }
22764         
22765         if(this.weight){
22766             cfg.cls += ' alert-' + this.weight;
22767         }
22768         
22769         return cfg;
22770     },
22771     
22772     initEvents: function() 
22773     {
22774         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22775     },
22776     
22777     setTitle : function(str)
22778     {
22779         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
22780     },
22781     
22782     setText : function(str)
22783     {
22784         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
22785     },
22786     
22787     setWeight : function(weight)
22788     {
22789         if(this.weight){
22790             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
22791         }
22792         
22793         this.weight = weight;
22794         
22795         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
22796     },
22797     
22798     setIcon : function(icon)
22799     {
22800         if(this.faicon){
22801             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
22802         }
22803         
22804         this.faicon = icon
22805         
22806         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
22807     },
22808     
22809     hide: function() 
22810     {
22811         this.el.hide();   
22812     },
22813     
22814     show: function() 
22815     {  
22816         this.el.show();   
22817     }
22818     
22819 });
22820
22821