roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129        
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         this.initEvents();
141         
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229         return ret;
230     },
231     
232     addxtypeChild : function (tree, cntr)
233     {
234         Roo.debug && Roo.log('addxtypeChild:' + cntr);
235         var cn = this;
236         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
237         
238         
239         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
240                     (typeof(tree['flexy:foreach']) != 'undefined');
241           
242         
243         
244          skip_children = false;
245         // render the element if it's not BODY.
246         if (tree.xtype != 'Body') {
247            
248             cn = Roo.factory(tree);
249            
250             cn.parentType = this.xtype; //??
251             cn.parentId = this.id;
252             
253             var build_from_html =  Roo.XComponent.build_from_html;
254             
255             
256             // does the container contain child eleemnts with 'xtype' attributes.
257             // that match this xtype..
258             // note - when we render we create these as well..
259             // so we should check to see if body has xtype set.
260             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
261                
262                 var self_cntr_el = Roo.get(this[cntr](false));
263                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
264                 if (echild) { 
265                     Roo.log(Roo.XComponent.build_from_html);
266                     Roo.log("got echild:");
267                     Roo.log(echild);
268                 }
269                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
270                 // and are not displayed -this causes this to use up the wrong element when matching.
271                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
272                 
273                 
274                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
275                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
276                   
277                   
278                   
279                     cn.el = echild;
280                   //  Roo.log("GOT");
281                     //echild.dom.removeAttribute('xtype');
282                 } else {
283                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
284                     Roo.debug && Roo.log(self_cntr_el);
285                     Roo.debug && Roo.log(echild);
286                     Roo.debug && Roo.log(cn);
287                 }
288             }
289            
290             
291            
292             // if object has flexy:if - then it may or may not be rendered.
293             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
294                 // skip a flexy if element.
295                 Roo.debug && Roo.log('skipping render');
296                 Roo.debug && Roo.log(tree);
297                 if (!cn.el) {
298                     Roo.debug && Roo.log('skipping all children');
299                     skip_children = true;
300                 }
301                 
302              } else {
303                  
304                 // actually if flexy:foreach is found, we really want to create 
305                 // multiple copies here...
306                 //Roo.log('render');
307                 //Roo.log(this[cntr]());
308                 cn.render(this[cntr](true));
309              }
310             // then add the element..
311         }
312         
313         
314         // handle the kids..
315         
316         var nitems = [];
317         /*
318         if (typeof (tree.menu) != 'undefined') {
319             tree.menu.parentType = cn.xtype;
320             tree.menu.triggerEl = cn.el;
321             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
322             
323         }
324         */
325         if (!tree.items || !tree.items.length) {
326             cn.items = nitems;
327             return cn;
328         }
329         var items = tree.items;
330         delete tree.items;
331         
332         //Roo.log(items.length);
333             // add the items..
334         if (!skip_children) {    
335             for(var i =0;i < items.length;i++) {
336                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
337             }
338         }
339         
340         cn.items = nitems;
341         
342         this.fireEvent('childrenrendered', this);
343         
344         return cn;
345     } 
346     
347     
348 });
349
350  /*
351  * - LGPL
352  *
353  * Body
354  * 
355  */
356
357 /**
358  * @class Roo.bootstrap.Body
359  * @extends Roo.bootstrap.Component
360  * Bootstrap Body class
361  * 
362  * @constructor
363  * Create a new body
364  * @param {Object} config The config object
365  */
366
367 Roo.bootstrap.Body = function(config){
368     Roo.bootstrap.Body.superclass.constructor.call(this, config);
369     this.el = Roo.get(document.body);
370     if (this.cls && this.cls.length) {
371         Roo.get(document.body).addClass(this.cls);
372     }
373 };
374
375 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
376       
377         autoCreate : {
378         cls: 'container'
379     },
380     onRender : function(ct, position)
381     {
382        /* Roo.log("Roo.bootstrap.Body - onRender");
383         if (this.cls && this.cls.length) {
384             Roo.get(document.body).addClass(this.cls);
385         }
386         // style??? xttr???
387         */
388     }
389     
390     
391  
392    
393 });
394
395  /*
396  * - LGPL
397  *
398  * button group
399  * 
400  */
401
402
403 /**
404  * @class Roo.bootstrap.ButtonGroup
405  * @extends Roo.bootstrap.Component
406  * Bootstrap ButtonGroup class
407  * @cfg {String} size lg | sm | xs (default empty normal)
408  * @cfg {String} align vertical | justified  (default none)
409  * @cfg {String} direction up | down (default down)
410  * @cfg {Boolean} toolbar false | true
411  * @cfg {Boolean} btn true | false
412  * 
413  * 
414  * @constructor
415  * Create a new Input
416  * @param {Object} config The config object
417  */
418
419 Roo.bootstrap.ButtonGroup = function(config){
420     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
421 };
422
423 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
424     
425     size: '',
426     align: '',
427     direction: '',
428     toolbar: false,
429     btn: true,
430
431     getAutoCreate : function(){
432         var cfg = {
433             cls: 'btn-group',
434             html : null
435         }
436         
437         cfg.html = this.html || cfg.html;
438         
439         if (this.toolbar) {
440             cfg = {
441                 cls: 'btn-toolbar',
442                 html: null
443             }
444             
445             return cfg;
446         }
447         
448         if (['vertical','justified'].indexOf(this.align)!==-1) {
449             cfg.cls = 'btn-group-' + this.align;
450             
451             if (this.align == 'justified') {
452                 console.log(this.items);
453             }
454         }
455         
456         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
457             cfg.cls += ' btn-group-' + this.size;
458         }
459         
460         if (this.direction == 'up') {
461             cfg.cls += ' dropup' ;
462         }
463         
464         return cfg;
465     }
466    
467 });
468
469  /*
470  * - LGPL
471  *
472  * button
473  * 
474  */
475
476 /**
477  * @class Roo.bootstrap.Button
478  * @extends Roo.bootstrap.Component
479  * Bootstrap Button class
480  * @cfg {String} html The button content
481  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
482  * @cfg {String} size ( lg | sm | xs)
483  * @cfg {String} tag ( a | input | submit)
484  * @cfg {String} href empty or href
485  * @cfg {Boolean} disabled default false;
486  * @cfg {Boolean} isClose default false;
487  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
488  * @cfg {String} badge text for badge
489  * @cfg {String} theme default 
490  * @cfg {Boolean} inverse 
491  * @cfg {Boolean} toggle 
492  * @cfg {String} ontext text for on toggle state
493  * @cfg {String} offtext text for off toggle state
494  * @cfg {Boolean} defaulton 
495  * @cfg {Boolean} preventDefault  default true
496  * @cfg {Boolean} removeClass remove the standard class..
497  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
498  * 
499  * @constructor
500  * Create a new button
501  * @param {Object} config The config object
502  */
503
504
505 Roo.bootstrap.Button = function(config){
506     Roo.bootstrap.Button.superclass.constructor.call(this, config);
507     this.addEvents({
508         // raw events
509         /**
510          * @event click
511          * When a butotn is pressed
512          * @param {Roo.bootstrap.Button} this
513          * @param {Roo.EventObject} e
514          */
515         "click" : true,
516          /**
517          * @event toggle
518          * After the button has been toggles
519          * @param {Roo.EventObject} e
520          * @param {boolean} pressed (also available as button.pressed)
521          */
522         "toggle" : true
523     });
524 };
525
526 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
527     html: false,
528     active: false,
529     weight: '',
530     size: '',
531     tag: 'button',
532     href: '',
533     disabled: false,
534     isClose: false,
535     glyphicon: '',
536     badge: '',
537     theme: 'default',
538     inverse: false,
539     
540     toggle: false,
541     ontext: 'ON',
542     offtext: 'OFF',
543     defaulton: true,
544     preventDefault: true,
545     removeClass: false,
546     name: false,
547     target: false,
548     
549     
550     pressed : null,
551      
552     
553     getAutoCreate : function(){
554         
555         var cfg = {
556             tag : 'button',
557             cls : 'roo-button',
558             html: ''
559         };
560         
561         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
562             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
563             this.tag = 'button';
564         } else {
565             cfg.tag = this.tag;
566         }
567         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
568         
569         if (this.toggle == true) {
570             cfg={
571                 tag: 'div',
572                 cls: 'slider-frame roo-button',
573                 cn: [
574                     {
575                         tag: 'span',
576                         'data-on-text':'ON',
577                         'data-off-text':'OFF',
578                         cls: 'slider-button',
579                         html: this.offtext
580                     }
581                 ]
582             };
583             
584             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
585                 cfg.cls += ' '+this.weight;
586             }
587             
588             return cfg;
589         }
590         
591         if (this.isClose) {
592             cfg.cls += ' close';
593             
594             cfg["aria-hidden"] = true;
595             
596             cfg.html = "&times;";
597             
598             return cfg;
599         }
600         
601          
602         if (this.theme==='default') {
603             cfg.cls = 'btn roo-button';
604             
605             //if (this.parentType != 'Navbar') {
606             this.weight = this.weight.length ?  this.weight : 'default';
607             //}
608             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
609                 
610                 cfg.cls += ' btn-' + this.weight;
611             }
612         } else if (this.theme==='glow') {
613             
614             cfg.tag = 'a';
615             cfg.cls = 'btn-glow roo-button';
616             
617             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
618                 
619                 cfg.cls += ' ' + this.weight;
620             }
621         }
622    
623         
624         if (this.inverse) {
625             this.cls += ' inverse';
626         }
627         
628         
629         if (this.active) {
630             cfg.cls += ' active';
631         }
632         
633         if (this.disabled) {
634             cfg.disabled = 'disabled';
635         }
636         
637         if (this.items) {
638             Roo.log('changing to ul' );
639             cfg.tag = 'ul';
640             this.glyphicon = 'caret';
641         }
642         
643         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
644          
645         //gsRoo.log(this.parentType);
646         if (this.parentType === 'Navbar' && !this.parent().bar) {
647             Roo.log('changing to li?');
648             
649             cfg.tag = 'li';
650             
651             cfg.cls = '';
652             cfg.cn =  [{
653                 tag : 'a',
654                 cls : 'roo-button',
655                 html : this.html,
656                 href : this.href || '#'
657             }];
658             if (this.menu) {
659                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
660                 cfg.cls += ' dropdown';
661             }   
662             
663             delete cfg.html;
664             
665         }
666         
667        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
668         
669         if (this.glyphicon) {
670             cfg.html = ' ' + cfg.html;
671             
672             cfg.cn = [
673                 {
674                     tag: 'span',
675                     cls: 'glyphicon glyphicon-' + this.glyphicon
676                 }
677             ];
678         }
679         
680         if (this.badge) {
681             cfg.html += ' ';
682             
683             cfg.tag = 'a';
684             
685 //            cfg.cls='btn roo-button';
686             
687             cfg.href=this.href;
688             
689             var value = cfg.html;
690             
691             if(this.glyphicon){
692                 value = {
693                             tag: 'span',
694                             cls: 'glyphicon glyphicon-' + this.glyphicon,
695                             html: this.html
696                         };
697                 
698             }
699             
700             cfg.cn = [
701                 value,
702                 {
703                     tag: 'span',
704                     cls: 'badge',
705                     html: this.badge
706                 }
707             ];
708             
709             cfg.html='';
710         }
711         
712         if (this.menu) {
713             cfg.cls += ' dropdown';
714             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
715         }
716         
717         if (cfg.tag !== 'a' && this.href !== '') {
718             throw "Tag must be a to set href.";
719         } else if (this.href.length > 0) {
720             cfg.href = this.href;
721         }
722         
723         if(this.removeClass){
724             cfg.cls = '';
725         }
726         
727         if(this.target){
728             cfg.target = this.target;
729         }
730         
731         return cfg;
732     },
733     initEvents: function() {
734        // Roo.log('init events?');
735 //        Roo.log(this.el.dom);
736         // add the menu...
737         
738         if (typeof (this.menu) != 'undefined') {
739             this.menu.parentType = this.xtype;
740             this.menu.triggerEl = this.el;
741             this.addxtype(Roo.apply({}, this.menu));
742         }
743
744
745        if (this.el.hasClass('roo-button')) {
746             this.el.on('click', this.onClick, this);
747        } else {
748             this.el.select('.roo-button').on('click', this.onClick, this);
749        }
750        
751        if(this.removeClass){
752            this.el.on('click', this.onClick, this);
753        }
754        
755        this.el.enableDisplayMode();
756         
757     },
758     onClick : function(e)
759     {
760         if (this.disabled) {
761             return;
762         }
763         
764         
765         Roo.log('button on click ');
766         if(this.preventDefault){
767             e.preventDefault();
768         }
769         if (this.pressed === true || this.pressed === false) {
770             this.pressed = !this.pressed;
771             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
772             this.fireEvent('toggle', this, e, this.pressed);
773         }
774         
775         
776         this.fireEvent('click', this, e);
777     },
778     
779     /**
780      * Enables this button
781      */
782     enable : function()
783     {
784         this.disabled = false;
785         this.el.removeClass('disabled');
786     },
787     
788     /**
789      * Disable this button
790      */
791     disable : function()
792     {
793         this.disabled = true;
794         this.el.addClass('disabled');
795     },
796      /**
797      * sets the active state on/off, 
798      * @param {Boolean} state (optional) Force a particular state
799      */
800     setActive : function(v) {
801         
802         this.el[v ? 'addClass' : 'removeClass']('active');
803     },
804      /**
805      * toggles the current active state 
806      */
807     toggleActive : function()
808     {
809        var active = this.el.hasClass('active');
810        this.setActive(!active);
811        
812         
813     },
814     setText : function(str)
815     {
816         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
817     },
818     getText : function()
819     {
820         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
821     },
822     hide: function() {
823        
824      
825         this.el.hide();   
826     },
827     show: function() {
828        
829         this.el.show();   
830     }
831     
832     
833 });
834
835  /*
836  * - LGPL
837  *
838  * column
839  * 
840  */
841
842 /**
843  * @class Roo.bootstrap.Column
844  * @extends Roo.bootstrap.Component
845  * Bootstrap Column class
846  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
847  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
848  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
849  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
850  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
851  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
852  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
853  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
854  *
855  * 
856  * @cfg {Boolean} hidden (true|false) hide the element
857  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
858  * @cfg {String} fa (ban|check|...) font awesome icon
859  * @cfg {Number} fasize (1|2|....) font awsome size
860
861  * @cfg {String} icon (info-sign|check|...) glyphicon name
862
863  * @cfg {String} html content of column.
864  * 
865  * @constructor
866  * Create a new Column
867  * @param {Object} config The config object
868  */
869
870 Roo.bootstrap.Column = function(config){
871     Roo.bootstrap.Column.superclass.constructor.call(this, config);
872 };
873
874 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
875     
876     xs: false,
877     sm: false,
878     md: false,
879     lg: false,
880     xsoff: false,
881     smoff: false,
882     mdoff: false,
883     lgoff: false,
884     html: '',
885     offset: 0,
886     alert: false,
887     fa: false,
888     icon : false,
889     hidden : false,
890     fasize : 1,
891     
892     getAutoCreate : function(){
893         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
894         
895         cfg = {
896             tag: 'div',
897             cls: 'column'
898         };
899         
900         var settings=this;
901         ['xs','sm','md','lg'].map(function(size){
902             //Roo.log( size + ':' + settings[size]);
903             
904             if (settings[size+'off'] !== false) {
905                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
906             }
907             
908             if (settings[size] === false) {
909                 return;
910             }
911             Roo.log(settings[size]);
912             if (!settings[size]) { // 0 = hidden
913                 cfg.cls += ' hidden-' + size;
914                 return;
915             }
916             cfg.cls += ' col-' + size + '-' + settings[size];
917             
918         });
919         
920         if (this.hidden) {
921             cfg.cls += ' hidden';
922         }
923         
924         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
925             cfg.cls +=' alert alert-' + this.alert;
926         }
927         
928         
929         if (this.html.length) {
930             cfg.html = this.html;
931         }
932         if (this.fa) {
933             var fasize = '';
934             if (this.fasize > 1) {
935                 fasize = ' fa-' + this.fasize + 'x';
936             }
937             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
938             
939             
940         }
941         if (this.icon) {
942             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
943         }
944         
945         return cfg;
946     }
947    
948 });
949
950  
951
952  /*
953  * - LGPL
954  *
955  * page container.
956  * 
957  */
958
959
960 /**
961  * @class Roo.bootstrap.Container
962  * @extends Roo.bootstrap.Component
963  * Bootstrap Container class
964  * @cfg {Boolean} jumbotron is it a jumbotron element
965  * @cfg {String} html content of element
966  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
967  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
968  * @cfg {String} header content of header (for panel)
969  * @cfg {String} footer content of footer (for panel)
970  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
971  * @cfg {String} tag (header|aside|section) type of HTML tag.
972  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
973  * @cfg {String} fa (ban|check|...) font awesome icon
974  * @cfg {String} icon (info-sign|check|...) glyphicon name
975  * @cfg {Boolean} hidden (true|false) hide the element
976
977  *     
978  * @constructor
979  * Create a new Container
980  * @param {Object} config The config object
981  */
982
983 Roo.bootstrap.Container = function(config){
984     Roo.bootstrap.Container.superclass.constructor.call(this, config);
985 };
986
987 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
988     
989     jumbotron : false,
990     well: '',
991     panel : '',
992     header: '',
993     footer : '',
994     sticky: '',
995     tag : false,
996     alert : false,
997     fa: false,
998     icon : false,
999   
1000      
1001     getChildContainer : function() {
1002         
1003         if(!this.el){
1004             return false;
1005         }
1006         
1007         if (this.panel.length) {
1008             return this.el.select('.panel-body',true).first();
1009         }
1010         
1011         return this.el;
1012     },
1013     
1014     
1015     getAutoCreate : function(){
1016         
1017         var cfg = {
1018             tag : this.tag || 'div',
1019             html : '',
1020             cls : ''
1021         };
1022         if (this.jumbotron) {
1023             cfg.cls = 'jumbotron';
1024         }
1025         
1026         
1027         
1028         // - this is applied by the parent..
1029         //if (this.cls) {
1030         //    cfg.cls = this.cls + '';
1031         //}
1032         
1033         if (this.sticky.length) {
1034             
1035             var bd = Roo.get(document.body);
1036             if (!bd.hasClass('bootstrap-sticky')) {
1037                 bd.addClass('bootstrap-sticky');
1038                 Roo.select('html',true).setStyle('height', '100%');
1039             }
1040              
1041             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1042         }
1043         
1044         
1045         if (this.well.length) {
1046             switch (this.well) {
1047                 case 'lg':
1048                 case 'sm':
1049                     cfg.cls +=' well well-' +this.well;
1050                     break;
1051                 default:
1052                     cfg.cls +=' well';
1053                     break;
1054             }
1055         }
1056         
1057         if (this.hidden) {
1058             cfg.cls += ' hidden';
1059         }
1060         
1061         
1062         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1063             cfg.cls +=' alert alert-' + this.alert;
1064         }
1065         
1066         var body = cfg;
1067         
1068         if (this.panel.length) {
1069             cfg.cls += ' panel panel-' + this.panel;
1070             cfg.cn = [];
1071             if (this.header.length) {
1072                 cfg.cn.push({
1073                     
1074                     cls : 'panel-heading',
1075                     cn : [{
1076                         tag: 'h3',
1077                         cls : 'panel-title',
1078                         html : this.header
1079                     }]
1080                     
1081                 });
1082             }
1083             body = false;
1084             cfg.cn.push({
1085                 cls : 'panel-body',
1086                 html : this.html
1087             });
1088             
1089             
1090             if (this.footer.length) {
1091                 cfg.cn.push({
1092                     cls : 'panel-footer',
1093                     html : this.footer
1094                     
1095                 });
1096             }
1097             
1098         }
1099         
1100         if (body) {
1101             body.html = this.html || cfg.html;
1102             // prefix with the icons..
1103             if (this.fa) {
1104                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1105             }
1106             if (this.icon) {
1107                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1108             }
1109             
1110             
1111         }
1112         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1113             cfg.cls =  'container';
1114         }
1115         
1116         return cfg;
1117     },
1118     
1119     titleEl : function()
1120     {
1121         if(!this.el || !this.panel.length || !this.header.length){
1122             return;
1123         }
1124         
1125         return this.el.select('.panel-title',true).first();
1126     },
1127     
1128     setTitle : function(v)
1129     {
1130         var titleEl = this.titleEl();
1131         
1132         if(!titleEl){
1133             return;
1134         }
1135         
1136         titleEl.dom.innerHTML = v;
1137     },
1138     
1139     getTitle : function()
1140     {
1141         
1142         var titleEl = this.titleEl();
1143         
1144         if(!titleEl){
1145             return '';
1146         }
1147         
1148         return titleEl.dom.innerHTML;
1149     },
1150     
1151     show : function() {
1152         this.el.removeClass('hidden');
1153     },
1154     hide: function() {
1155         if (!this.el.hasClass('hidden')) {
1156             this.el.addClass('hidden');
1157         }
1158         
1159     }
1160    
1161 });
1162
1163  /*
1164  * - LGPL
1165  *
1166  * image
1167  * 
1168  */
1169
1170
1171 /**
1172  * @class Roo.bootstrap.Img
1173  * @extends Roo.bootstrap.Component
1174  * Bootstrap Img class
1175  * @cfg {Boolean} imgResponsive false | true
1176  * @cfg {String} border rounded | circle | thumbnail
1177  * @cfg {String} src image source
1178  * @cfg {String} alt image alternative text
1179  * @cfg {String} href a tag href
1180  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1181  * 
1182  * @constructor
1183  * Create a new Input
1184  * @param {Object} config The config object
1185  */
1186
1187 Roo.bootstrap.Img = function(config){
1188     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1189     
1190     this.addEvents({
1191         // img events
1192         /**
1193          * @event click
1194          * The img click event for the img.
1195          * @param {Roo.EventObject} e
1196          */
1197         "click" : true
1198     });
1199 };
1200
1201 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1202     
1203     imgResponsive: true,
1204     border: '',
1205     src: '',
1206     href: false,
1207     target: false,
1208
1209     getAutoCreate : function(){
1210         
1211         var cfg = {
1212             tag: 'img',
1213             cls: (this.imgResponsive) ? 'img-responsive' : '',
1214             html : null
1215         }
1216         
1217         cfg.html = this.html || cfg.html;
1218         
1219         cfg.src = this.src || cfg.src;
1220         
1221         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1222             cfg.cls += ' img-' + this.border;
1223         }
1224         
1225         if(this.alt){
1226             cfg.alt = this.alt;
1227         }
1228         
1229         if(this.href){
1230             var a = {
1231                 tag: 'a',
1232                 href: this.href,
1233                 cn: [
1234                     cfg
1235                 ]
1236             }
1237             
1238             if(this.target){
1239                 a.target = this.target;
1240             }
1241             
1242         }
1243         
1244         
1245         return (this.href) ? a : cfg;
1246     },
1247     
1248     initEvents: function() {
1249         
1250         if(!this.href){
1251             this.el.on('click', this.onClick, this);
1252         }
1253     },
1254     
1255     onClick : function(e)
1256     {
1257         Roo.log('img onclick');
1258         this.fireEvent('click', this, e);
1259     }
1260    
1261 });
1262
1263  /*
1264  * - LGPL
1265  *
1266  * image
1267  * 
1268  */
1269
1270
1271 /**
1272  * @class Roo.bootstrap.Link
1273  * @extends Roo.bootstrap.Component
1274  * Bootstrap Link Class
1275  * @cfg {String} alt image alternative text
1276  * @cfg {String} href a tag href
1277  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1278  * @cfg {String} html the content of the link.
1279  * @cfg {String} anchor name for the anchor link
1280
1281  * @cfg {Boolean} preventDefault (true | false) default false
1282
1283  * 
1284  * @constructor
1285  * Create a new Input
1286  * @param {Object} config The config object
1287  */
1288
1289 Roo.bootstrap.Link = function(config){
1290     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1291     
1292     this.addEvents({
1293         // img events
1294         /**
1295          * @event click
1296          * The img click event for the img.
1297          * @param {Roo.EventObject} e
1298          */
1299         "click" : true
1300     });
1301 };
1302
1303 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1304     
1305     href: false,
1306     target: false,
1307     preventDefault: false,
1308     anchor : false,
1309     alt : false,
1310
1311     getAutoCreate : function()
1312     {
1313         
1314         var cfg = {
1315             tag: 'a'
1316         };
1317         // anchor's do not require html/href...
1318         if (this.anchor === false) {
1319             cfg.html = this.html || 'html-missing';
1320             cfg.href = this.href || '#';
1321         } else {
1322             cfg.name = this.anchor;
1323             if (this.html !== false) {
1324                 cfg.html = this.html;
1325             }
1326             if (this.href !== false) {
1327                 cfg.href = this.href;
1328             }
1329         }
1330         
1331         if(this.alt !== false){
1332             cfg.alt = this.alt;
1333         }
1334         
1335         
1336         if(this.target !== false) {
1337             cfg.target = this.target;
1338         }
1339         
1340         return cfg;
1341     },
1342     
1343     initEvents: function() {
1344         
1345         if(!this.href || this.preventDefault){
1346             this.el.on('click', this.onClick, this);
1347         }
1348     },
1349     
1350     onClick : function(e)
1351     {
1352         if(this.preventDefault){
1353             e.preventDefault();
1354         }
1355         //Roo.log('img onclick');
1356         this.fireEvent('click', this, e);
1357     }
1358    
1359 });
1360
1361  /*
1362  * - LGPL
1363  *
1364  * header
1365  * 
1366  */
1367
1368 /**
1369  * @class Roo.bootstrap.Header
1370  * @extends Roo.bootstrap.Component
1371  * Bootstrap Header class
1372  * @cfg {String} html content of header
1373  * @cfg {Number} level (1|2|3|4|5|6) default 1
1374  * 
1375  * @constructor
1376  * Create a new Header
1377  * @param {Object} config The config object
1378  */
1379
1380
1381 Roo.bootstrap.Header  = function(config){
1382     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1383 };
1384
1385 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1386     
1387     //href : false,
1388     html : false,
1389     level : 1,
1390     
1391     
1392     
1393     getAutoCreate : function(){
1394         
1395         
1396         
1397         var cfg = {
1398             tag: 'h' + (1 *this.level),
1399             html: this.html || ''
1400         } ;
1401         
1402         return cfg;
1403     }
1404    
1405 });
1406
1407  
1408
1409  /*
1410  * Based on:
1411  * Ext JS Library 1.1.1
1412  * Copyright(c) 2006-2007, Ext JS, LLC.
1413  *
1414  * Originally Released Under LGPL - original licence link has changed is not relivant.
1415  *
1416  * Fork - LGPL
1417  * <script type="text/javascript">
1418  */
1419  
1420 /**
1421  * @class Roo.bootstrap.MenuMgr
1422  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1423  * @singleton
1424  */
1425 Roo.bootstrap.MenuMgr = function(){
1426    var menus, active, groups = {}, attached = false, lastShow = new Date();
1427
1428    // private - called when first menu is created
1429    function init(){
1430        menus = {};
1431        active = new Roo.util.MixedCollection();
1432        Roo.get(document).addKeyListener(27, function(){
1433            if(active.length > 0){
1434                hideAll();
1435            }
1436        });
1437    }
1438
1439    // private
1440    function hideAll(){
1441        if(active && active.length > 0){
1442            var c = active.clone();
1443            c.each(function(m){
1444                m.hide();
1445            });
1446        }
1447    }
1448
1449    // private
1450    function onHide(m){
1451        active.remove(m);
1452        if(active.length < 1){
1453            Roo.get(document).un("mouseup", onMouseDown);
1454             
1455            attached = false;
1456        }
1457    }
1458
1459    // private
1460    function onShow(m){
1461        var last = active.last();
1462        lastShow = new Date();
1463        active.add(m);
1464        if(!attached){
1465           Roo.get(document).on("mouseup", onMouseDown);
1466            
1467            attached = true;
1468        }
1469        if(m.parentMenu){
1470           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1471           m.parentMenu.activeChild = m;
1472        }else if(last && last.isVisible()){
1473           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1474        }
1475    }
1476
1477    // private
1478    function onBeforeHide(m){
1479        if(m.activeChild){
1480            m.activeChild.hide();
1481        }
1482        if(m.autoHideTimer){
1483            clearTimeout(m.autoHideTimer);
1484            delete m.autoHideTimer;
1485        }
1486    }
1487
1488    // private
1489    function onBeforeShow(m){
1490        var pm = m.parentMenu;
1491        if(!pm && !m.allowOtherMenus){
1492            hideAll();
1493        }else if(pm && pm.activeChild && active != m){
1494            pm.activeChild.hide();
1495        }
1496    }
1497
1498    // private
1499    function onMouseDown(e){
1500         Roo.log("on MouseDown");
1501         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1502            hideAll();
1503         }
1504         
1505         
1506    }
1507
1508    // private
1509    function onBeforeCheck(mi, state){
1510        if(state){
1511            var g = groups[mi.group];
1512            for(var i = 0, l = g.length; i < l; i++){
1513                if(g[i] != mi){
1514                    g[i].setChecked(false);
1515                }
1516            }
1517        }
1518    }
1519
1520    return {
1521
1522        /**
1523         * Hides all menus that are currently visible
1524         */
1525        hideAll : function(){
1526             hideAll();  
1527        },
1528
1529        // private
1530        register : function(menu){
1531            if(!menus){
1532                init();
1533            }
1534            menus[menu.id] = menu;
1535            menu.on("beforehide", onBeforeHide);
1536            menu.on("hide", onHide);
1537            menu.on("beforeshow", onBeforeShow);
1538            menu.on("show", onShow);
1539            var g = menu.group;
1540            if(g && menu.events["checkchange"]){
1541                if(!groups[g]){
1542                    groups[g] = [];
1543                }
1544                groups[g].push(menu);
1545                menu.on("checkchange", onCheck);
1546            }
1547        },
1548
1549         /**
1550          * Returns a {@link Roo.menu.Menu} object
1551          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1552          * be used to generate and return a new Menu instance.
1553          */
1554        get : function(menu){
1555            if(typeof menu == "string"){ // menu id
1556                return menus[menu];
1557            }else if(menu.events){  // menu instance
1558                return menu;
1559            }
1560            /*else if(typeof menu.length == 'number'){ // array of menu items?
1561                return new Roo.bootstrap.Menu({items:menu});
1562            }else{ // otherwise, must be a config
1563                return new Roo.bootstrap.Menu(menu);
1564            }
1565            */
1566            return false;
1567        },
1568
1569        // private
1570        unregister : function(menu){
1571            delete menus[menu.id];
1572            menu.un("beforehide", onBeforeHide);
1573            menu.un("hide", onHide);
1574            menu.un("beforeshow", onBeforeShow);
1575            menu.un("show", onShow);
1576            var g = menu.group;
1577            if(g && menu.events["checkchange"]){
1578                groups[g].remove(menu);
1579                menu.un("checkchange", onCheck);
1580            }
1581        },
1582
1583        // private
1584        registerCheckable : function(menuItem){
1585            var g = menuItem.group;
1586            if(g){
1587                if(!groups[g]){
1588                    groups[g] = [];
1589                }
1590                groups[g].push(menuItem);
1591                menuItem.on("beforecheckchange", onBeforeCheck);
1592            }
1593        },
1594
1595        // private
1596        unregisterCheckable : function(menuItem){
1597            var g = menuItem.group;
1598            if(g){
1599                groups[g].remove(menuItem);
1600                menuItem.un("beforecheckchange", onBeforeCheck);
1601            }
1602        }
1603    };
1604 }();/*
1605  * - LGPL
1606  *
1607  * menu
1608  * 
1609  */
1610
1611 /**
1612  * @class Roo.bootstrap.Menu
1613  * @extends Roo.bootstrap.Component
1614  * Bootstrap Menu class - container for MenuItems
1615  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1616  * 
1617  * @constructor
1618  * Create a new Menu
1619  * @param {Object} config The config object
1620  */
1621
1622
1623 Roo.bootstrap.Menu = function(config){
1624     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1625     if (this.registerMenu) {
1626         Roo.bootstrap.MenuMgr.register(this);
1627     }
1628     this.addEvents({
1629         /**
1630          * @event beforeshow
1631          * Fires before this menu is displayed
1632          * @param {Roo.menu.Menu} this
1633          */
1634         beforeshow : true,
1635         /**
1636          * @event beforehide
1637          * Fires before this menu is hidden
1638          * @param {Roo.menu.Menu} this
1639          */
1640         beforehide : true,
1641         /**
1642          * @event show
1643          * Fires after this menu is displayed
1644          * @param {Roo.menu.Menu} this
1645          */
1646         show : true,
1647         /**
1648          * @event hide
1649          * Fires after this menu is hidden
1650          * @param {Roo.menu.Menu} this
1651          */
1652         hide : true,
1653         /**
1654          * @event click
1655          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1656          * @param {Roo.menu.Menu} this
1657          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1658          * @param {Roo.EventObject} e
1659          */
1660         click : true,
1661         /**
1662          * @event mouseover
1663          * Fires when the mouse is hovering over this menu
1664          * @param {Roo.menu.Menu} this
1665          * @param {Roo.EventObject} e
1666          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1667          */
1668         mouseover : true,
1669         /**
1670          * @event mouseout
1671          * Fires when the mouse exits this menu
1672          * @param {Roo.menu.Menu} this
1673          * @param {Roo.EventObject} e
1674          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1675          */
1676         mouseout : true,
1677         /**
1678          * @event itemclick
1679          * Fires when a menu item contained in this menu is clicked
1680          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1681          * @param {Roo.EventObject} e
1682          */
1683         itemclick: true
1684     });
1685     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1686 };
1687
1688 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1689     
1690    /// html : false,
1691     //align : '',
1692     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1693     type: false,
1694     /**
1695      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1696      */
1697     registerMenu : true,
1698     
1699     menuItems :false, // stores the menu items..
1700     
1701     hidden:true,
1702     
1703     parentMenu : false,
1704     
1705     getChildContainer : function() {
1706         return this.el;  
1707     },
1708     
1709     getAutoCreate : function(){
1710          
1711         //if (['right'].indexOf(this.align)!==-1) {
1712         //    cfg.cn[1].cls += ' pull-right'
1713         //}
1714         
1715         
1716         var cfg = {
1717             tag : 'ul',
1718             cls : 'dropdown-menu' ,
1719             style : 'z-index:1000'
1720             
1721         }
1722         
1723         if (this.type === 'submenu') {
1724             cfg.cls = 'submenu active';
1725         }
1726         if (this.type === 'treeview') {
1727             cfg.cls = 'treeview-menu';
1728         }
1729         
1730         return cfg;
1731     },
1732     initEvents : function() {
1733         
1734        // Roo.log("ADD event");
1735        // Roo.log(this.triggerEl.dom);
1736         this.triggerEl.on('click', this.onTriggerPress, this);
1737         this.triggerEl.addClass('dropdown-toggle');
1738         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1739
1740         this.el.on("mouseover", this.onMouseOver, this);
1741         this.el.on("mouseout", this.onMouseOut, this);
1742         
1743         
1744     },
1745     findTargetItem : function(e){
1746         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1747         if(!t){
1748             return false;
1749         }
1750         //Roo.log(t);         Roo.log(t.id);
1751         if(t && t.id){
1752             //Roo.log(this.menuitems);
1753             return this.menuitems.get(t.id);
1754             
1755             //return this.items.get(t.menuItemId);
1756         }
1757         
1758         return false;
1759     },
1760     onClick : function(e){
1761         Roo.log("menu.onClick");
1762         var t = this.findTargetItem(e);
1763         if(!t || t.isContainer){
1764             return;
1765         }
1766         Roo.log(e);
1767         /*
1768         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1769             if(t == this.activeItem && t.shouldDeactivate(e)){
1770                 this.activeItem.deactivate();
1771                 delete this.activeItem;
1772                 return;
1773             }
1774             if(t.canActivate){
1775                 this.setActiveItem(t, true);
1776             }
1777             return;
1778             
1779             
1780         }
1781         */
1782        
1783         Roo.log('pass click event');
1784         
1785         t.onClick(e);
1786         
1787         this.fireEvent("click", this, t, e);
1788         
1789         this.hide();
1790     },
1791      onMouseOver : function(e){
1792         var t  = this.findTargetItem(e);
1793         //Roo.log(t);
1794         //if(t){
1795         //    if(t.canActivate && !t.disabled){
1796         //        this.setActiveItem(t, true);
1797         //    }
1798         //}
1799         
1800         this.fireEvent("mouseover", this, e, t);
1801     },
1802     isVisible : function(){
1803         return !this.hidden;
1804     },
1805      onMouseOut : function(e){
1806         var t  = this.findTargetItem(e);
1807         
1808         //if(t ){
1809         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1810         //        this.activeItem.deactivate();
1811         //        delete this.activeItem;
1812         //    }
1813         //}
1814         this.fireEvent("mouseout", this, e, t);
1815     },
1816     
1817     
1818     /**
1819      * Displays this menu relative to another element
1820      * @param {String/HTMLElement/Roo.Element} element The element to align to
1821      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1822      * the element (defaults to this.defaultAlign)
1823      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1824      */
1825     show : function(el, pos, parentMenu){
1826         this.parentMenu = parentMenu;
1827         if(!this.el){
1828             this.render();
1829         }
1830         this.fireEvent("beforeshow", this);
1831         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1832     },
1833      /**
1834      * Displays this menu at a specific xy position
1835      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1836      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1837      */
1838     showAt : function(xy, parentMenu, /* private: */_e){
1839         this.parentMenu = parentMenu;
1840         if(!this.el){
1841             this.render();
1842         }
1843         if(_e !== false){
1844             this.fireEvent("beforeshow", this);
1845             
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          * @event specialfilter
10763          * Fires when specialfilter
10764             * @param {Roo.bootstrap.ComboBox} combo This combo box
10765             */
10766         'specialfilter' : true
10767         
10768     });
10769     
10770     this.item = [];
10771     this.tickItems = [];
10772     
10773     this.selectedIndex = -1;
10774     if(this.mode == 'local'){
10775         if(config.queryDelay === undefined){
10776             this.queryDelay = 10;
10777         }
10778         if(config.minChars === undefined){
10779             this.minChars = 0;
10780         }
10781     }
10782 };
10783
10784 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10785      
10786     /**
10787      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10788      * rendering into an Roo.Editor, defaults to false)
10789      */
10790     /**
10791      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10792      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10793      */
10794     /**
10795      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10796      */
10797     /**
10798      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10799      * the dropdown list (defaults to undefined, with no header element)
10800      */
10801
10802      /**
10803      * @cfg {String/Roo.Template} tpl The template to use to render the output
10804      */
10805      
10806      /**
10807      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10808      */
10809     listWidth: undefined,
10810     /**
10811      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10812      * mode = 'remote' or 'text' if mode = 'local')
10813      */
10814     displayField: undefined,
10815     
10816     /**
10817      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10818      * mode = 'remote' or 'value' if mode = 'local'). 
10819      * Note: use of a valueField requires the user make a selection
10820      * in order for a value to be mapped.
10821      */
10822     valueField: undefined,
10823     
10824     
10825     /**
10826      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10827      * field's data value (defaults to the underlying DOM element's name)
10828      */
10829     hiddenName: undefined,
10830     /**
10831      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10832      */
10833     listClass: '',
10834     /**
10835      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10836      */
10837     selectedClass: 'active',
10838     
10839     /**
10840      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10841      */
10842     shadow:'sides',
10843     /**
10844      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10845      * anchor positions (defaults to 'tl-bl')
10846      */
10847     listAlign: 'tl-bl?',
10848     /**
10849      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10850      */
10851     maxHeight: 300,
10852     /**
10853      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10854      * query specified by the allQuery config option (defaults to 'query')
10855      */
10856     triggerAction: 'query',
10857     /**
10858      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10859      * (defaults to 4, does not apply if editable = false)
10860      */
10861     minChars : 4,
10862     /**
10863      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10864      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10865      */
10866     typeAhead: false,
10867     /**
10868      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10869      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10870      */
10871     queryDelay: 500,
10872     /**
10873      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10874      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10875      */
10876     pageSize: 0,
10877     /**
10878      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10879      * when editable = true (defaults to false)
10880      */
10881     selectOnFocus:false,
10882     /**
10883      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10884      */
10885     queryParam: 'query',
10886     /**
10887      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10888      * when mode = 'remote' (defaults to 'Loading...')
10889      */
10890     loadingText: 'Loading...',
10891     /**
10892      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10893      */
10894     resizable: false,
10895     /**
10896      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10897      */
10898     handleHeight : 8,
10899     /**
10900      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10901      * traditional select (defaults to true)
10902      */
10903     editable: true,
10904     /**
10905      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10906      */
10907     allQuery: '',
10908     /**
10909      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10910      */
10911     mode: 'remote',
10912     /**
10913      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10914      * listWidth has a higher value)
10915      */
10916     minListWidth : 70,
10917     /**
10918      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10919      * allow the user to set arbitrary text into the field (defaults to false)
10920      */
10921     forceSelection:false,
10922     /**
10923      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10924      * if typeAhead = true (defaults to 250)
10925      */
10926     typeAheadDelay : 250,
10927     /**
10928      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10929      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10930      */
10931     valueNotFoundText : undefined,
10932     /**
10933      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10934      */
10935     blockFocus : false,
10936     
10937     /**
10938      * @cfg {Boolean} disableClear Disable showing of clear button.
10939      */
10940     disableClear : false,
10941     /**
10942      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10943      */
10944     alwaysQuery : false,
10945     
10946     /**
10947      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10948      */
10949     multiple : false,
10950     
10951     /**
10952      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
10953      */
10954     invalidClass : "has-warning",
10955     
10956     /**
10957      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
10958      */
10959     validClass : "has-success",
10960     
10961     /**
10962      * @cfg {Boolean} specialFilter (true|false) special filter default false
10963      */
10964     specialFilter : false,
10965     
10966     //private
10967     addicon : false,
10968     editicon: false,
10969     
10970     page: 0,
10971     hasQuery: false,
10972     append: false,
10973     loadNext: false,
10974     autoFocus : true,
10975     tickable : false,
10976     btnPosition : 'right',
10977     triggerList : true,
10978     showToggleBtn : true,
10979     // element that contains real text value.. (when hidden is used..)
10980     
10981     getAutoCreate : function()
10982     {
10983         var cfg = false;
10984         
10985         /*
10986          *  Normal ComboBox
10987          */
10988         if(!this.tickable){
10989             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10990             return cfg;
10991         }
10992         
10993         /*
10994          *  ComboBox with tickable selections
10995          */
10996              
10997         var align = this.labelAlign || this.parentLabelAlign();
10998         
10999         cfg = {
11000             cls : 'form-group roo-combobox-tickable' //input-group
11001         };
11002         
11003         var buttons = {
11004             tag : 'div',
11005             cls : 'tickable-buttons',
11006             cn : [
11007                 {
11008                     tag : 'button',
11009                     type : 'button',
11010                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11011                     html : 'Edit'
11012                 },
11013                 {
11014                     tag : 'button',
11015                     type : 'button',
11016                     name : 'ok',
11017                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11018                     html : 'Done'
11019                 },
11020                 {
11021                     tag : 'button',
11022                     type : 'button',
11023                     name : 'cancel',
11024                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11025                     html : 'Cancel'
11026                 }
11027             ]
11028         };
11029         
11030         if(this.editable){
11031             buttons.cn.unshift({
11032                 tag: 'input',
11033                 cls: 'select2-search-field-input'
11034             });
11035         }
11036         
11037         var _this = this;
11038         
11039         Roo.each(buttons.cn, function(c){
11040             if (_this.size) {
11041                 c.cls += ' btn-' + _this.size;
11042             }
11043
11044             if (_this.disabled) {
11045                 c.disabled = true;
11046             }
11047         });
11048         
11049         var box = {
11050             tag: 'div',
11051             cn: [
11052                 {
11053                     tag: 'input',
11054                     type : 'hidden',
11055                     cls: 'form-hidden-field'
11056                 },
11057                 {
11058                     tag: 'ul',
11059                     cls: 'select2-choices',
11060                     cn:[
11061                         {
11062                             tag: 'li',
11063                             cls: 'select2-search-field',
11064                             cn: [
11065
11066                                 buttons
11067                             ]
11068                         }
11069                     ]
11070                 }
11071             ]
11072         }
11073         
11074         var combobox = {
11075             cls: 'select2-container input-group select2-container-multi',
11076             cn: [
11077                 box
11078 //                {
11079 //                    tag: 'ul',
11080 //                    cls: 'typeahead typeahead-long dropdown-menu',
11081 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11082 //                }
11083             ]
11084         };
11085         
11086         if(this.hasFeedback && !this.allowBlank){
11087             
11088             var feedback = {
11089                 tag: 'span',
11090                 cls: 'glyphicon form-control-feedback'
11091             };
11092
11093             combobox.cn.push(feedback);
11094         }
11095         
11096         if (align ==='left' && this.fieldLabel.length) {
11097             
11098                 Roo.log("left and has label");
11099                 cfg.cn = [
11100                     
11101                     {
11102                         tag: 'label',
11103                         'for' :  id,
11104                         cls : 'control-label col-sm-' + this.labelWidth,
11105                         html : this.fieldLabel
11106                         
11107                     },
11108                     {
11109                         cls : "col-sm-" + (12 - this.labelWidth), 
11110                         cn: [
11111                             combobox
11112                         ]
11113                     }
11114                     
11115                 ];
11116         } else if ( this.fieldLabel.length) {
11117                 Roo.log(" label");
11118                  cfg.cn = [
11119                    
11120                     {
11121                         tag: 'label',
11122                         //cls : 'input-group-addon',
11123                         html : this.fieldLabel
11124                         
11125                     },
11126                     
11127                     combobox
11128                     
11129                 ];
11130
11131         } else {
11132             
11133                 Roo.log(" no label && no align");
11134                 cfg = combobox
11135                      
11136                 
11137         }
11138          
11139         var settings=this;
11140         ['xs','sm','md','lg'].map(function(size){
11141             if (settings[size]) {
11142                 cfg.cls += ' col-' + size + '-' + settings[size];
11143             }
11144         });
11145         
11146         return cfg;
11147         
11148     },
11149     
11150     // private
11151     initEvents: function()
11152     {
11153         
11154         if (!this.store) {
11155             throw "can not find store for combo";
11156         }
11157         this.store = Roo.factory(this.store, Roo.data);
11158         
11159         if(this.tickable){
11160             this.initTickableEvents();
11161             return;
11162         }
11163         
11164         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11165         
11166         if(this.hiddenName){
11167             
11168             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11169             
11170             this.hiddenField.dom.value =
11171                 this.hiddenValue !== undefined ? this.hiddenValue :
11172                 this.value !== undefined ? this.value : '';
11173
11174             // prevent input submission
11175             this.el.dom.removeAttribute('name');
11176             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11177              
11178              
11179         }
11180         //if(Roo.isGecko){
11181         //    this.el.dom.setAttribute('autocomplete', 'off');
11182         //}
11183         
11184         var cls = 'x-combo-list';
11185         
11186         //this.list = new Roo.Layer({
11187         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11188         //});
11189         
11190         var _this = this;
11191         
11192         (function(){
11193             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11194             _this.list.setWidth(lw);
11195         }).defer(100);
11196         
11197         this.list.on('mouseover', this.onViewOver, this);
11198         this.list.on('mousemove', this.onViewMove, this);
11199         
11200         this.list.on('scroll', this.onViewScroll, this);
11201         
11202         /*
11203         this.list.swallowEvent('mousewheel');
11204         this.assetHeight = 0;
11205
11206         if(this.title){
11207             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11208             this.assetHeight += this.header.getHeight();
11209         }
11210
11211         this.innerList = this.list.createChild({cls:cls+'-inner'});
11212         this.innerList.on('mouseover', this.onViewOver, this);
11213         this.innerList.on('mousemove', this.onViewMove, this);
11214         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11215         
11216         if(this.allowBlank && !this.pageSize && !this.disableClear){
11217             this.footer = this.list.createChild({cls:cls+'-ft'});
11218             this.pageTb = new Roo.Toolbar(this.footer);
11219            
11220         }
11221         if(this.pageSize){
11222             this.footer = this.list.createChild({cls:cls+'-ft'});
11223             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11224                     {pageSize: this.pageSize});
11225             
11226         }
11227         
11228         if (this.pageTb && this.allowBlank && !this.disableClear) {
11229             var _this = this;
11230             this.pageTb.add(new Roo.Toolbar.Fill(), {
11231                 cls: 'x-btn-icon x-btn-clear',
11232                 text: '&#160;',
11233                 handler: function()
11234                 {
11235                     _this.collapse();
11236                     _this.clearValue();
11237                     _this.onSelect(false, -1);
11238                 }
11239             });
11240         }
11241         if (this.footer) {
11242             this.assetHeight += this.footer.getHeight();
11243         }
11244         */
11245             
11246         if(!this.tpl){
11247             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11248         }
11249
11250         this.view = new Roo.View(this.list, this.tpl, {
11251             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11252         });
11253         //this.view.wrapEl.setDisplayed(false);
11254         this.view.on('click', this.onViewClick, this);
11255         
11256         
11257         
11258         this.store.on('beforeload', this.onBeforeLoad, this);
11259         this.store.on('load', this.onLoad, this);
11260         this.store.on('loadexception', this.onLoadException, this);
11261         /*
11262         if(this.resizable){
11263             this.resizer = new Roo.Resizable(this.list,  {
11264                pinned:true, handles:'se'
11265             });
11266             this.resizer.on('resize', function(r, w, h){
11267                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11268                 this.listWidth = w;
11269                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11270                 this.restrictHeight();
11271             }, this);
11272             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11273         }
11274         */
11275         if(!this.editable){
11276             this.editable = true;
11277             this.setEditable(false);
11278         }
11279         
11280         /*
11281         
11282         if (typeof(this.events.add.listeners) != 'undefined') {
11283             
11284             this.addicon = this.wrap.createChild(
11285                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11286        
11287             this.addicon.on('click', function(e) {
11288                 this.fireEvent('add', this);
11289             }, this);
11290         }
11291         if (typeof(this.events.edit.listeners) != 'undefined') {
11292             
11293             this.editicon = this.wrap.createChild(
11294                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11295             if (this.addicon) {
11296                 this.editicon.setStyle('margin-left', '40px');
11297             }
11298             this.editicon.on('click', function(e) {
11299                 
11300                 // we fire even  if inothing is selected..
11301                 this.fireEvent('edit', this, this.lastData );
11302                 
11303             }, this);
11304         }
11305         */
11306         
11307         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11308             "up" : function(e){
11309                 this.inKeyMode = true;
11310                 this.selectPrev();
11311             },
11312
11313             "down" : function(e){
11314                 if(!this.isExpanded()){
11315                     this.onTriggerClick();
11316                 }else{
11317                     this.inKeyMode = true;
11318                     this.selectNext();
11319                 }
11320             },
11321
11322             "enter" : function(e){
11323 //                this.onViewClick();
11324                 //return true;
11325                 this.collapse();
11326                 
11327                 if(this.fireEvent("specialkey", this, e)){
11328                     this.onViewClick(false);
11329                 }
11330                 
11331                 return true;
11332             },
11333
11334             "esc" : function(e){
11335                 this.collapse();
11336             },
11337
11338             "tab" : function(e){
11339                 this.collapse();
11340                 
11341                 if(this.fireEvent("specialkey", this, e)){
11342                     this.onViewClick(false);
11343                 }
11344                 
11345                 return true;
11346             },
11347
11348             scope : this,
11349
11350             doRelay : function(foo, bar, hname){
11351                 if(hname == 'down' || this.scope.isExpanded()){
11352                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11353                 }
11354                 return true;
11355             },
11356
11357             forceKeyDown: true
11358         });
11359         
11360         
11361         this.queryDelay = Math.max(this.queryDelay || 10,
11362                 this.mode == 'local' ? 10 : 250);
11363         
11364         
11365         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11366         
11367         if(this.typeAhead){
11368             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11369         }
11370         if(this.editable !== false){
11371             this.inputEl().on("keyup", this.onKeyUp, this);
11372         }
11373         if(this.forceSelection){
11374             this.inputEl().on('blur', this.doForce, this);
11375         }
11376         
11377         if(this.multiple){
11378             this.choices = this.el.select('ul.select2-choices', true).first();
11379             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11380         }
11381     },
11382     
11383     initTickableEvents: function()
11384     {   
11385         this.createList();
11386         
11387         if(this.hiddenName){
11388             
11389             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11390             
11391             this.hiddenField.dom.value =
11392                 this.hiddenValue !== undefined ? this.hiddenValue :
11393                 this.value !== undefined ? this.value : '';
11394
11395             // prevent input submission
11396             this.el.dom.removeAttribute('name');
11397             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11398              
11399              
11400         }
11401         
11402 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11403         
11404         this.choices = this.el.select('ul.select2-choices', true).first();
11405         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11406         if(this.triggerList){
11407             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11408         }
11409          
11410         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11411         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11412         
11413         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11414         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11415         
11416         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11417         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11418         
11419         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11420         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11421         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11422         
11423         this.okBtn.hide();
11424         this.cancelBtn.hide();
11425         
11426         var _this = this;
11427         
11428         (function(){
11429             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11430             _this.list.setWidth(lw);
11431         }).defer(100);
11432         
11433         this.list.on('mouseover', this.onViewOver, this);
11434         this.list.on('mousemove', this.onViewMove, this);
11435         
11436         this.list.on('scroll', this.onViewScroll, this);
11437         
11438         if(!this.tpl){
11439             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>';
11440         }
11441
11442         this.view = new Roo.View(this.list, this.tpl, {
11443             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11444         });
11445         
11446         //this.view.wrapEl.setDisplayed(false);
11447         this.view.on('click', this.onViewClick, this);
11448         
11449         
11450         
11451         this.store.on('beforeload', this.onBeforeLoad, this);
11452         this.store.on('load', this.onLoad, this);
11453         this.store.on('loadexception', this.onLoadException, this);
11454         
11455         if(this.editable){
11456             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11457                 "up" : function(e){
11458                     this.inKeyMode = true;
11459                     this.selectPrev();
11460                 },
11461
11462                 "down" : function(e){
11463                     this.inKeyMode = true;
11464                     this.selectNext();
11465                 },
11466
11467                 "enter" : function(e){
11468                     if(this.fireEvent("specialkey", this, e)){
11469                         this.onViewClick(false);
11470                     }
11471                     
11472                     return true;
11473                 },
11474
11475                 "esc" : function(e){
11476                     this.onTickableFooterButtonClick(e, false, false);
11477                 },
11478
11479                 "tab" : function(e){
11480                     this.fireEvent("specialkey", this, e);
11481                     
11482                     this.onTickableFooterButtonClick(e, false, false);
11483                     
11484                     return true;
11485                 },
11486
11487                 scope : this,
11488
11489                 doRelay : function(e, fn, key){
11490                     if(this.scope.isExpanded()){
11491                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11492                     }
11493                     return true;
11494                 },
11495
11496                 forceKeyDown: true
11497             });
11498         }
11499         
11500         this.queryDelay = Math.max(this.queryDelay || 10,
11501                 this.mode == 'local' ? 10 : 250);
11502         
11503         
11504         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11505         
11506         if(this.typeAhead){
11507             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11508         }
11509         
11510         if(this.editable !== false){
11511             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11512         }
11513         
11514     },
11515
11516     onDestroy : function(){
11517         if(this.view){
11518             this.view.setStore(null);
11519             this.view.el.removeAllListeners();
11520             this.view.el.remove();
11521             this.view.purgeListeners();
11522         }
11523         if(this.list){
11524             this.list.dom.innerHTML  = '';
11525         }
11526         
11527         if(this.store){
11528             this.store.un('beforeload', this.onBeforeLoad, this);
11529             this.store.un('load', this.onLoad, this);
11530             this.store.un('loadexception', this.onLoadException, this);
11531         }
11532         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11533     },
11534
11535     // private
11536     fireKey : function(e){
11537         if(e.isNavKeyPress() && !this.list.isVisible()){
11538             this.fireEvent("specialkey", this, e);
11539         }
11540     },
11541
11542     // private
11543     onResize: function(w, h){
11544 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11545 //        
11546 //        if(typeof w != 'number'){
11547 //            // we do not handle it!?!?
11548 //            return;
11549 //        }
11550 //        var tw = this.trigger.getWidth();
11551 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11552 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11553 //        var x = w - tw;
11554 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11555 //            
11556 //        //this.trigger.setStyle('left', x+'px');
11557 //        
11558 //        if(this.list && this.listWidth === undefined){
11559 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11560 //            this.list.setWidth(lw);
11561 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11562 //        }
11563         
11564     
11565         
11566     },
11567
11568     /**
11569      * Allow or prevent the user from directly editing the field text.  If false is passed,
11570      * the user will only be able to select from the items defined in the dropdown list.  This method
11571      * is the runtime equivalent of setting the 'editable' config option at config time.
11572      * @param {Boolean} value True to allow the user to directly edit the field text
11573      */
11574     setEditable : function(value){
11575         if(value == this.editable){
11576             return;
11577         }
11578         this.editable = value;
11579         if(!value){
11580             this.inputEl().dom.setAttribute('readOnly', true);
11581             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11582             this.inputEl().addClass('x-combo-noedit');
11583         }else{
11584             this.inputEl().dom.setAttribute('readOnly', false);
11585             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11586             this.inputEl().removeClass('x-combo-noedit');
11587         }
11588     },
11589
11590     // private
11591     
11592     onBeforeLoad : function(combo,opts){
11593         if(!this.hasFocus){
11594             return;
11595         }
11596          if (!opts.add) {
11597             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11598          }
11599         this.restrictHeight();
11600         this.selectedIndex = -1;
11601     },
11602
11603     // private
11604     onLoad : function(){
11605         
11606         this.hasQuery = false;
11607         
11608         if(!this.hasFocus){
11609             return;
11610         }
11611         
11612         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11613             this.loading.hide();
11614         }
11615              
11616         if(this.store.getCount() > 0){
11617             this.expand();
11618             this.restrictHeight();
11619             if(this.lastQuery == this.allQuery){
11620                 if(this.editable && !this.tickable){
11621                     this.inputEl().dom.select();
11622                 }
11623                 
11624                 if(
11625                     !this.selectByValue(this.value, true) &&
11626                     this.autoFocus && 
11627                     (
11628                         !this.store.lastOptions ||
11629                         typeof(this.store.lastOptions.add) == 'undefined' || 
11630                         this.store.lastOptions.add != true
11631                     )
11632                 ){
11633                     this.select(0, true);
11634                 }
11635             }else{
11636                 if(this.autoFocus){
11637                     this.selectNext();
11638                 }
11639                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11640                     this.taTask.delay(this.typeAheadDelay);
11641                 }
11642             }
11643         }else{
11644             this.onEmptyResults();
11645         }
11646         
11647         //this.el.focus();
11648     },
11649     // private
11650     onLoadException : function()
11651     {
11652         this.hasQuery = false;
11653         
11654         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11655             this.loading.hide();
11656         }
11657         
11658         if(this.tickable && this.editable){
11659             return;
11660         }
11661         
11662         this.collapse();
11663         
11664         Roo.log(this.store.reader.jsonData);
11665         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11666             // fixme
11667             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11668         }
11669         
11670         
11671     },
11672     // private
11673     onTypeAhead : function(){
11674         if(this.store.getCount() > 0){
11675             var r = this.store.getAt(0);
11676             var newValue = r.data[this.displayField];
11677             var len = newValue.length;
11678             var selStart = this.getRawValue().length;
11679             
11680             if(selStart != len){
11681                 this.setRawValue(newValue);
11682                 this.selectText(selStart, newValue.length);
11683             }
11684         }
11685     },
11686
11687     // private
11688     onSelect : function(record, index){
11689         
11690         if(this.fireEvent('beforeselect', this, record, index) !== false){
11691         
11692             this.setFromData(index > -1 ? record.data : false);
11693             
11694             this.collapse();
11695             this.fireEvent('select', this, record, index);
11696         }
11697     },
11698
11699     /**
11700      * Returns the currently selected field value or empty string if no value is set.
11701      * @return {String} value The selected value
11702      */
11703     getValue : function(){
11704         
11705         if(this.multiple){
11706             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11707         }
11708         
11709         if(this.valueField){
11710             return typeof this.value != 'undefined' ? this.value : '';
11711         }else{
11712             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11713         }
11714     },
11715
11716     /**
11717      * Clears any text/value currently set in the field
11718      */
11719     clearValue : function(){
11720         if(this.hiddenField){
11721             this.hiddenField.dom.value = '';
11722         }
11723         this.value = '';
11724         this.setRawValue('');
11725         this.lastSelectionText = '';
11726         this.lastData = false;
11727         
11728     },
11729
11730     /**
11731      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11732      * will be displayed in the field.  If the value does not match the data value of an existing item,
11733      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11734      * Otherwise the field will be blank (although the value will still be set).
11735      * @param {String} value The value to match
11736      */
11737     setValue : function(v){
11738         if(this.multiple){
11739             this.syncValue();
11740             return;
11741         }
11742         
11743         var text = v;
11744         if(this.valueField){
11745             var r = this.findRecord(this.valueField, v);
11746             if(r){
11747                 text = r.data[this.displayField];
11748             }else if(this.valueNotFoundText !== undefined){
11749                 text = this.valueNotFoundText;
11750             }
11751         }
11752         this.lastSelectionText = text;
11753         if(this.hiddenField){
11754             this.hiddenField.dom.value = v;
11755         }
11756         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11757         this.value = v;
11758     },
11759     /**
11760      * @property {Object} the last set data for the element
11761      */
11762     
11763     lastData : false,
11764     /**
11765      * Sets the value of the field based on a object which is related to the record format for the store.
11766      * @param {Object} value the value to set as. or false on reset?
11767      */
11768     setFromData : function(o){
11769         
11770         if(this.multiple){
11771             this.addItem(o);
11772             return;
11773         }
11774             
11775         var dv = ''; // display value
11776         var vv = ''; // value value..
11777         this.lastData = o;
11778         if (this.displayField) {
11779             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11780         } else {
11781             // this is an error condition!!!
11782             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11783         }
11784         
11785         if(this.valueField){
11786             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11787         }
11788         
11789         if(this.hiddenField){
11790             this.hiddenField.dom.value = vv;
11791             
11792             this.lastSelectionText = dv;
11793             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11794             this.value = vv;
11795             return;
11796         }
11797         // no hidden field.. - we store the value in 'value', but still display
11798         // display field!!!!
11799         this.lastSelectionText = dv;
11800         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11801         this.value = vv;
11802         
11803         
11804     },
11805     // private
11806     reset : function(){
11807         // overridden so that last data is reset..
11808         
11809         if(this.multiple){
11810             this.clearItem();
11811             return;
11812         }
11813         
11814         this.setValue(this.originalValue);
11815         this.clearInvalid();
11816         this.lastData = false;
11817         if (this.view) {
11818             this.view.clearSelections();
11819         }
11820     },
11821     // private
11822     findRecord : function(prop, value){
11823         var record;
11824         if(this.store.getCount() > 0){
11825             this.store.each(function(r){
11826                 if(r.data[prop] == value){
11827                     record = r;
11828                     return false;
11829                 }
11830                 return true;
11831             });
11832         }
11833         return record;
11834     },
11835     
11836     getName: function()
11837     {
11838         // returns hidden if it's set..
11839         if (!this.rendered) {return ''};
11840         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11841         
11842     },
11843     // private
11844     onViewMove : function(e, t){
11845         this.inKeyMode = false;
11846     },
11847
11848     // private
11849     onViewOver : function(e, t){
11850         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11851             return;
11852         }
11853         var item = this.view.findItemFromChild(t);
11854         
11855         if(item){
11856             var index = this.view.indexOf(item);
11857             this.select(index, false);
11858         }
11859     },
11860
11861     // private
11862     onViewClick : function(view, doFocus, el, e)
11863     {
11864         var index = this.view.getSelectedIndexes()[0];
11865         
11866         var r = this.store.getAt(index);
11867         
11868         if(this.tickable){
11869             
11870             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
11871                 return;
11872             }
11873             
11874             var rm = false;
11875             var _this = this;
11876             
11877             Roo.each(this.tickItems, function(v,k){
11878                 
11879                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11880                     _this.tickItems.splice(k, 1);
11881                     
11882                     if(typeof(e) == 'undefined' && view == false){
11883                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
11884                     }
11885                     
11886                     rm = true;
11887                     return;
11888                 }
11889             });
11890             
11891             if(rm){
11892                 return;
11893             }
11894             
11895             this.tickItems.push(r.data);
11896             
11897             if(typeof(e) == 'undefined' && view == false){
11898                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
11899             }
11900                     
11901             return;
11902         }
11903         
11904         if(r){
11905             this.onSelect(r, index);
11906         }
11907         if(doFocus !== false && !this.blockFocus){
11908             this.inputEl().focus();
11909         }
11910     },
11911
11912     // private
11913     restrictHeight : function(){
11914         //this.innerList.dom.style.height = '';
11915         //var inner = this.innerList.dom;
11916         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11917         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11918         //this.list.beginUpdate();
11919         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11920         this.list.alignTo(this.inputEl(), this.listAlign);
11921         this.list.alignTo(this.inputEl(), this.listAlign);
11922         //this.list.endUpdate();
11923     },
11924
11925     // private
11926     onEmptyResults : function(){
11927         
11928         if(this.tickable && this.editable){
11929             this.restrictHeight();
11930             return;
11931         }
11932         
11933         this.collapse();
11934     },
11935
11936     /**
11937      * Returns true if the dropdown list is expanded, else false.
11938      */
11939     isExpanded : function(){
11940         return this.list.isVisible();
11941     },
11942
11943     /**
11944      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11945      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11946      * @param {String} value The data value of the item to select
11947      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11948      * selected item if it is not currently in view (defaults to true)
11949      * @return {Boolean} True if the value matched an item in the list, else false
11950      */
11951     selectByValue : function(v, scrollIntoView){
11952         if(v !== undefined && v !== null){
11953             var r = this.findRecord(this.valueField || this.displayField, v);
11954             if(r){
11955                 this.select(this.store.indexOf(r), scrollIntoView);
11956                 return true;
11957             }
11958         }
11959         return false;
11960     },
11961
11962     /**
11963      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11964      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11965      * @param {Number} index The zero-based index of the list item to select
11966      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11967      * selected item if it is not currently in view (defaults to true)
11968      */
11969     select : function(index, scrollIntoView){
11970         this.selectedIndex = index;
11971         this.view.select(index);
11972         if(scrollIntoView !== false){
11973             var el = this.view.getNode(index);
11974             /*
11975              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
11976              */
11977             if(el){
11978                 this.list.scrollChildIntoView(el, false);
11979             }
11980         }
11981     },
11982
11983     // private
11984     selectNext : 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 < ct-1){
11990                 this.select(this.selectedIndex+1);
11991             }
11992         }
11993     },
11994
11995     // private
11996     selectPrev : function(){
11997         var ct = this.store.getCount();
11998         if(ct > 0){
11999             if(this.selectedIndex == -1){
12000                 this.select(0);
12001             }else if(this.selectedIndex != 0){
12002                 this.select(this.selectedIndex-1);
12003             }
12004         }
12005     },
12006
12007     // private
12008     onKeyUp : function(e){
12009         if(this.editable !== false && !e.isSpecialKey()){
12010             this.lastKey = e.getKey();
12011             this.dqTask.delay(this.queryDelay);
12012         }
12013     },
12014
12015     // private
12016     validateBlur : function(){
12017         return !this.list || !this.list.isVisible();   
12018     },
12019
12020     // private
12021     initQuery : function(){
12022         
12023         var v = this.getRawValue();
12024         
12025         if(this.tickable && this.editable){
12026             v = this.tickableInputEl().getValue();
12027         }
12028         
12029         this.doQuery(v);
12030     },
12031
12032     // private
12033     doForce : function(){
12034         if(this.inputEl().dom.value.length > 0){
12035             this.inputEl().dom.value =
12036                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12037              
12038         }
12039     },
12040
12041     /**
12042      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12043      * query allowing the query action to be canceled if needed.
12044      * @param {String} query The SQL query to execute
12045      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12046      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12047      * saved in the current store (defaults to false)
12048      */
12049     doQuery : function(q, forceAll){
12050         
12051         if(q === undefined || q === null){
12052             q = '';
12053         }
12054         var qe = {
12055             query: q,
12056             forceAll: forceAll,
12057             combo: this,
12058             cancel:false
12059         };
12060         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12061             return false;
12062         }
12063         q = qe.query;
12064         
12065         forceAll = qe.forceAll;
12066         if(forceAll === true || (q.length >= this.minChars)){
12067             
12068             this.hasQuery = true;
12069             
12070             if(this.lastQuery != q || this.alwaysQuery){
12071                 this.lastQuery = q;
12072                 if(this.mode == 'local'){
12073                     this.selectedIndex = -1;
12074                     if(forceAll){
12075                         this.store.clearFilter();
12076                     }else{
12077                         
12078                         if(this.specialFilter){
12079                             this.fireEvent('specialfilter', this);
12080                             this.onLoad();
12081                             return;
12082                         }
12083                         
12084                         this.store.filter(this.displayField, q);
12085                     }
12086                     this.onLoad();
12087                 }else{
12088                     
12089                     this.store.baseParams[this.queryParam] = q;
12090                     
12091                     var options = {params : this.getParams(q)};
12092                     
12093                     if(this.loadNext){
12094                         options.add = true;
12095                         options.params.start = this.page * this.pageSize;
12096                     }
12097                     
12098                     this.store.load(options);
12099                     
12100                     /*
12101                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12102                      *  we should expand the list on onLoad
12103                      *  so command out it
12104                      */
12105 //                    this.expand();
12106                 }
12107             }else{
12108                 this.selectedIndex = -1;
12109                 this.onLoad();   
12110             }
12111         }
12112         
12113         this.loadNext = false;
12114     },
12115     
12116     // private
12117     getParams : function(q){
12118         var p = {};
12119         //p[this.queryParam] = q;
12120         
12121         if(this.pageSize){
12122             p.start = 0;
12123             p.limit = this.pageSize;
12124         }
12125         return p;
12126     },
12127
12128     /**
12129      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12130      */
12131     collapse : function(){
12132         if(!this.isExpanded()){
12133             return;
12134         }
12135         
12136         this.list.hide();
12137         
12138         if(this.tickable){
12139             this.hasFocus = false;
12140             this.okBtn.hide();
12141             this.cancelBtn.hide();
12142             this.trigger.show();
12143             
12144             if(this.editable){
12145                 this.tickableInputEl().dom.value = '';
12146                 this.tickableInputEl().blur();
12147             }
12148             
12149         }
12150         
12151         Roo.get(document).un('mousedown', this.collapseIf, this);
12152         Roo.get(document).un('mousewheel', this.collapseIf, this);
12153         if (!this.editable) {
12154             Roo.get(document).un('keydown', this.listKeyPress, this);
12155         }
12156         this.fireEvent('collapse', this);
12157     },
12158
12159     // private
12160     collapseIf : function(e){
12161         var in_combo  = e.within(this.el);
12162         var in_list =  e.within(this.list);
12163         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12164         
12165         if (in_combo || in_list || is_list) {
12166             //e.stopPropagation();
12167             return;
12168         }
12169         
12170         if(this.tickable){
12171             this.onTickableFooterButtonClick(e, false, false);
12172         }
12173
12174         this.collapse();
12175         
12176     },
12177
12178     /**
12179      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12180      */
12181     expand : function(){
12182        
12183         if(this.isExpanded() || !this.hasFocus){
12184             return;
12185         }
12186         
12187         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12188         this.list.setWidth(lw);
12189         
12190         
12191          Roo.log('expand');
12192         
12193         this.list.show();
12194         
12195         this.restrictHeight();
12196         
12197         if(this.tickable){
12198             
12199             this.tickItems = Roo.apply([], this.item);
12200             
12201             this.okBtn.show();
12202             this.cancelBtn.show();
12203             this.trigger.hide();
12204             
12205             if(this.editable){
12206                 this.tickableInputEl().focus();
12207             }
12208             
12209         }
12210         
12211         Roo.get(document).on('mousedown', this.collapseIf, this);
12212         Roo.get(document).on('mousewheel', this.collapseIf, this);
12213         if (!this.editable) {
12214             Roo.get(document).on('keydown', this.listKeyPress, this);
12215         }
12216         
12217         this.fireEvent('expand', this);
12218     },
12219
12220     // private
12221     // Implements the default empty TriggerField.onTriggerClick function
12222     onTriggerClick : function(e)
12223     {
12224         Roo.log('trigger click');
12225         
12226         if(this.disabled || !this.triggerList){
12227             return;
12228         }
12229         
12230         this.page = 0;
12231         this.loadNext = false;
12232         
12233         if(this.isExpanded()){
12234             this.collapse();
12235             if (!this.blockFocus) {
12236                 this.inputEl().focus();
12237             }
12238             
12239         }else {
12240             this.hasFocus = true;
12241             if(this.triggerAction == 'all') {
12242                 this.doQuery(this.allQuery, true);
12243             } else {
12244                 this.doQuery(this.getRawValue());
12245             }
12246             if (!this.blockFocus) {
12247                 this.inputEl().focus();
12248             }
12249         }
12250     },
12251     
12252     onTickableTriggerClick : function(e)
12253     {
12254         if(this.disabled){
12255             return;
12256         }
12257         
12258         this.page = 0;
12259         this.loadNext = false;
12260         this.hasFocus = true;
12261         
12262         if(this.triggerAction == 'all') {
12263             this.doQuery(this.allQuery, true);
12264         } else {
12265             this.doQuery(this.getRawValue());
12266         }
12267     },
12268     
12269     onSearchFieldClick : function(e)
12270     {
12271         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12272             this.onTickableFooterButtonClick(e, false, false);
12273             return;
12274         }
12275         
12276         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12277             return;
12278         }
12279         
12280         this.page = 0;
12281         this.loadNext = false;
12282         this.hasFocus = true;
12283         
12284         if(this.triggerAction == 'all') {
12285             this.doQuery(this.allQuery, true);
12286         } else {
12287             this.doQuery(this.getRawValue());
12288         }
12289     },
12290     
12291     listKeyPress : function(e)
12292     {
12293         //Roo.log('listkeypress');
12294         // scroll to first matching element based on key pres..
12295         if (e.isSpecialKey()) {
12296             return false;
12297         }
12298         var k = String.fromCharCode(e.getKey()).toUpperCase();
12299         //Roo.log(k);
12300         var match  = false;
12301         var csel = this.view.getSelectedNodes();
12302         var cselitem = false;
12303         if (csel.length) {
12304             var ix = this.view.indexOf(csel[0]);
12305             cselitem  = this.store.getAt(ix);
12306             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12307                 cselitem = false;
12308             }
12309             
12310         }
12311         
12312         this.store.each(function(v) { 
12313             if (cselitem) {
12314                 // start at existing selection.
12315                 if (cselitem.id == v.id) {
12316                     cselitem = false;
12317                 }
12318                 return true;
12319             }
12320                 
12321             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12322                 match = this.store.indexOf(v);
12323                 return false;
12324             }
12325             return true;
12326         }, this);
12327         
12328         if (match === false) {
12329             return true; // no more action?
12330         }
12331         // scroll to?
12332         this.view.select(match);
12333         var sn = Roo.get(this.view.getSelectedNodes()[0])
12334         sn.scrollIntoView(sn.dom.parentNode, false);
12335     },
12336     
12337     onViewScroll : function(e, t){
12338         
12339         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){
12340             return;
12341         }
12342         
12343         this.hasQuery = true;
12344         
12345         this.loading = this.list.select('.loading', true).first();
12346         
12347         if(this.loading === null){
12348             this.list.createChild({
12349                 tag: 'div',
12350                 cls: 'loading select2-more-results select2-active',
12351                 html: 'Loading more results...'
12352             })
12353             
12354             this.loading = this.list.select('.loading', true).first();
12355             
12356             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12357             
12358             this.loading.hide();
12359         }
12360         
12361         this.loading.show();
12362         
12363         var _combo = this;
12364         
12365         this.page++;
12366         this.loadNext = true;
12367         
12368         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12369         
12370         return;
12371     },
12372     
12373     addItem : function(o)
12374     {   
12375         var dv = ''; // display value
12376         
12377         if (this.displayField) {
12378             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12379         } else {
12380             // this is an error condition!!!
12381             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12382         }
12383         
12384         if(!dv.length){
12385             return;
12386         }
12387         
12388         var choice = this.choices.createChild({
12389             tag: 'li',
12390             cls: 'select2-search-choice',
12391             cn: [
12392                 {
12393                     tag: 'div',
12394                     html: dv
12395                 },
12396                 {
12397                     tag: 'a',
12398                     href: '#',
12399                     cls: 'select2-search-choice-close',
12400                     tabindex: '-1'
12401                 }
12402             ]
12403             
12404         }, this.searchField);
12405         
12406         var close = choice.select('a.select2-search-choice-close', true).first()
12407         
12408         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12409         
12410         this.item.push(o);
12411         
12412         this.lastData = o;
12413         
12414         this.syncValue();
12415         
12416         this.inputEl().dom.value = '';
12417         
12418         this.validate();
12419     },
12420     
12421     onRemoveItem : function(e, _self, o)
12422     {
12423         e.preventDefault();
12424         
12425         this.lastItem = Roo.apply([], this.item);
12426         
12427         var index = this.item.indexOf(o.data) * 1;
12428         
12429         if( index < 0){
12430             Roo.log('not this item?!');
12431             return;
12432         }
12433         
12434         this.item.splice(index, 1);
12435         o.item.remove();
12436         
12437         this.syncValue();
12438         
12439         this.fireEvent('remove', this, e);
12440         
12441         this.validate();
12442         
12443     },
12444     
12445     syncValue : function()
12446     {
12447         if(!this.item.length){
12448             this.clearValue();
12449             return;
12450         }
12451             
12452         var value = [];
12453         var _this = this;
12454         Roo.each(this.item, function(i){
12455             if(_this.valueField){
12456                 value.push(i[_this.valueField]);
12457                 return;
12458             }
12459
12460             value.push(i);
12461         });
12462
12463         this.value = value.join(',');
12464
12465         if(this.hiddenField){
12466             this.hiddenField.dom.value = this.value;
12467         }
12468     },
12469     
12470     clearItem : function()
12471     {
12472         if(!this.multiple){
12473             return;
12474         }
12475         
12476         this.item = [];
12477         
12478         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12479            c.remove();
12480         });
12481         
12482         this.syncValue();
12483         
12484         this.validate();
12485     },
12486     
12487     inputEl: function ()
12488     {
12489         if(this.tickable){
12490             return this.searchField;
12491         }
12492         return this.el.select('input.form-control',true).first();
12493     },
12494     
12495     
12496     onTickableFooterButtonClick : function(e, btn, el)
12497     {
12498         e.preventDefault();
12499         
12500         this.lastItem = Roo.apply([], this.item);
12501         
12502         if(btn && btn.name == 'cancel'){
12503             this.tickItems = Roo.apply([], this.item);
12504             this.collapse();
12505             return;
12506         }
12507         
12508         this.clearItem();
12509         
12510         var _this = this;
12511         
12512         Roo.each(this.tickItems, function(o){
12513             _this.addItem(o);
12514         });
12515         
12516         this.collapse();
12517         
12518     },
12519     
12520     validate : function()
12521     {
12522         var v = this.getRawValue();
12523         
12524         if(this.multiple){
12525             v = this.getValue();
12526         }
12527         
12528         if(this.disabled || this.allowBlank || v.length){
12529             this.markValid();
12530             return true;
12531         }
12532         
12533         this.markInvalid();
12534         return false;
12535     },
12536     
12537     tickableInputEl : function()
12538     {
12539         if(!this.tickable || !this.editable){
12540             return this.inputEl();
12541         }
12542         
12543         return this.inputEl().select('.select2-search-field-input', true).first();
12544     }
12545     
12546     
12547
12548     /** 
12549     * @cfg {Boolean} grow 
12550     * @hide 
12551     */
12552     /** 
12553     * @cfg {Number} growMin 
12554     * @hide 
12555     */
12556     /** 
12557     * @cfg {Number} growMax 
12558     * @hide 
12559     */
12560     /**
12561      * @hide
12562      * @method autoSize
12563      */
12564 });
12565 /*
12566  * Based on:
12567  * Ext JS Library 1.1.1
12568  * Copyright(c) 2006-2007, Ext JS, LLC.
12569  *
12570  * Originally Released Under LGPL - original licence link has changed is not relivant.
12571  *
12572  * Fork - LGPL
12573  * <script type="text/javascript">
12574  */
12575
12576 /**
12577  * @class Roo.View
12578  * @extends Roo.util.Observable
12579  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12580  * This class also supports single and multi selection modes. <br>
12581  * Create a data model bound view:
12582  <pre><code>
12583  var store = new Roo.data.Store(...);
12584
12585  var view = new Roo.View({
12586     el : "my-element",
12587     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12588  
12589     singleSelect: true,
12590     selectedClass: "ydataview-selected",
12591     store: store
12592  });
12593
12594  // listen for node click?
12595  view.on("click", function(vw, index, node, e){
12596  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12597  });
12598
12599  // load XML data
12600  dataModel.load("foobar.xml");
12601  </code></pre>
12602  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12603  * <br><br>
12604  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12605  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12606  * 
12607  * Note: old style constructor is still suported (container, template, config)
12608  * 
12609  * @constructor
12610  * Create a new View
12611  * @param {Object} config The config object
12612  * 
12613  */
12614 Roo.View = function(config, depreciated_tpl, depreciated_config){
12615     
12616     this.parent = false;
12617     
12618     if (typeof(depreciated_tpl) == 'undefined') {
12619         // new way.. - universal constructor.
12620         Roo.apply(this, config);
12621         this.el  = Roo.get(this.el);
12622     } else {
12623         // old format..
12624         this.el  = Roo.get(config);
12625         this.tpl = depreciated_tpl;
12626         Roo.apply(this, depreciated_config);
12627     }
12628     this.wrapEl  = this.el.wrap().wrap();
12629     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12630     
12631     
12632     if(typeof(this.tpl) == "string"){
12633         this.tpl = new Roo.Template(this.tpl);
12634     } else {
12635         // support xtype ctors..
12636         this.tpl = new Roo.factory(this.tpl, Roo);
12637     }
12638     
12639     
12640     this.tpl.compile();
12641     
12642     /** @private */
12643     this.addEvents({
12644         /**
12645          * @event beforeclick
12646          * Fires before a click is processed. Returns false to cancel the default action.
12647          * @param {Roo.View} this
12648          * @param {Number} index The index of the target node
12649          * @param {HTMLElement} node The target node
12650          * @param {Roo.EventObject} e The raw event object
12651          */
12652             "beforeclick" : true,
12653         /**
12654          * @event click
12655          * Fires when a template node is clicked.
12656          * @param {Roo.View} this
12657          * @param {Number} index The index of the target node
12658          * @param {HTMLElement} node The target node
12659          * @param {Roo.EventObject} e The raw event object
12660          */
12661             "click" : true,
12662         /**
12663          * @event dblclick
12664          * Fires when a template node is double clicked.
12665          * @param {Roo.View} this
12666          * @param {Number} index The index of the target node
12667          * @param {HTMLElement} node The target node
12668          * @param {Roo.EventObject} e The raw event object
12669          */
12670             "dblclick" : true,
12671         /**
12672          * @event contextmenu
12673          * Fires when a template node is right clicked.
12674          * @param {Roo.View} this
12675          * @param {Number} index The index of the target node
12676          * @param {HTMLElement} node The target node
12677          * @param {Roo.EventObject} e The raw event object
12678          */
12679             "contextmenu" : true,
12680         /**
12681          * @event selectionchange
12682          * Fires when the selected nodes change.
12683          * @param {Roo.View} this
12684          * @param {Array} selections Array of the selected nodes
12685          */
12686             "selectionchange" : true,
12687     
12688         /**
12689          * @event beforeselect
12690          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12691          * @param {Roo.View} this
12692          * @param {HTMLElement} node The node to be selected
12693          * @param {Array} selections Array of currently selected nodes
12694          */
12695             "beforeselect" : true,
12696         /**
12697          * @event preparedata
12698          * Fires on every row to render, to allow you to change the data.
12699          * @param {Roo.View} this
12700          * @param {Object} data to be rendered (change this)
12701          */
12702           "preparedata" : true
12703           
12704           
12705         });
12706
12707
12708
12709     this.el.on({
12710         "click": this.onClick,
12711         "dblclick": this.onDblClick,
12712         "contextmenu": this.onContextMenu,
12713         scope:this
12714     });
12715
12716     this.selections = [];
12717     this.nodes = [];
12718     this.cmp = new Roo.CompositeElementLite([]);
12719     if(this.store){
12720         this.store = Roo.factory(this.store, Roo.data);
12721         this.setStore(this.store, true);
12722     }
12723     
12724     if ( this.footer && this.footer.xtype) {
12725            
12726          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12727         
12728         this.footer.dataSource = this.store
12729         this.footer.container = fctr;
12730         this.footer = Roo.factory(this.footer, Roo);
12731         fctr.insertFirst(this.el);
12732         
12733         // this is a bit insane - as the paging toolbar seems to detach the el..
12734 //        dom.parentNode.parentNode.parentNode
12735          // they get detached?
12736     }
12737     
12738     
12739     Roo.View.superclass.constructor.call(this);
12740     
12741     
12742 };
12743
12744 Roo.extend(Roo.View, Roo.util.Observable, {
12745     
12746      /**
12747      * @cfg {Roo.data.Store} store Data store to load data from.
12748      */
12749     store : false,
12750     
12751     /**
12752      * @cfg {String|Roo.Element} el The container element.
12753      */
12754     el : '',
12755     
12756     /**
12757      * @cfg {String|Roo.Template} tpl The template used by this View 
12758      */
12759     tpl : false,
12760     /**
12761      * @cfg {String} dataName the named area of the template to use as the data area
12762      *                          Works with domtemplates roo-name="name"
12763      */
12764     dataName: false,
12765     /**
12766      * @cfg {String} selectedClass The css class to add to selected nodes
12767      */
12768     selectedClass : "x-view-selected",
12769      /**
12770      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12771      */
12772     emptyText : "",
12773     
12774     /**
12775      * @cfg {String} text to display on mask (default Loading)
12776      */
12777     mask : false,
12778     /**
12779      * @cfg {Boolean} multiSelect Allow multiple selection
12780      */
12781     multiSelect : false,
12782     /**
12783      * @cfg {Boolean} singleSelect Allow single selection
12784      */
12785     singleSelect:  false,
12786     
12787     /**
12788      * @cfg {Boolean} toggleSelect - selecting 
12789      */
12790     toggleSelect : false,
12791     
12792     /**
12793      * @cfg {Boolean} tickable - selecting 
12794      */
12795     tickable : false,
12796     
12797     /**
12798      * Returns the element this view is bound to.
12799      * @return {Roo.Element}
12800      */
12801     getEl : function(){
12802         return this.wrapEl;
12803     },
12804     
12805     
12806
12807     /**
12808      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12809      */
12810     refresh : function(){
12811         //Roo.log('refresh');
12812         var t = this.tpl;
12813         
12814         // if we are using something like 'domtemplate', then
12815         // the what gets used is:
12816         // t.applySubtemplate(NAME, data, wrapping data..)
12817         // the outer template then get' applied with
12818         //     the store 'extra data'
12819         // and the body get's added to the
12820         //      roo-name="data" node?
12821         //      <span class='roo-tpl-{name}'></span> ?????
12822         
12823         
12824         
12825         this.clearSelections();
12826         this.el.update("");
12827         var html = [];
12828         var records = this.store.getRange();
12829         if(records.length < 1) {
12830             
12831             // is this valid??  = should it render a template??
12832             
12833             this.el.update(this.emptyText);
12834             return;
12835         }
12836         var el = this.el;
12837         if (this.dataName) {
12838             this.el.update(t.apply(this.store.meta)); //????
12839             el = this.el.child('.roo-tpl-' + this.dataName);
12840         }
12841         
12842         for(var i = 0, len = records.length; i < len; i++){
12843             var data = this.prepareData(records[i].data, i, records[i]);
12844             this.fireEvent("preparedata", this, data, i, records[i]);
12845             
12846             var d = Roo.apply({}, data);
12847             
12848             if(this.tickable){
12849                 Roo.apply(d, {'roo-id' : Roo.id()});
12850                 
12851                 var _this = this;
12852             
12853                 Roo.each(this.parent.item, function(item){
12854                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12855                         return;
12856                     }
12857                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12858                 });
12859             }
12860             
12861             html[html.length] = Roo.util.Format.trim(
12862                 this.dataName ?
12863                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12864                     t.apply(d)
12865             );
12866         }
12867         
12868         
12869         
12870         el.update(html.join(""));
12871         this.nodes = el.dom.childNodes;
12872         this.updateIndexes(0);
12873     },
12874     
12875
12876     /**
12877      * Function to override to reformat the data that is sent to
12878      * the template for each node.
12879      * DEPRICATED - use the preparedata event handler.
12880      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12881      * a JSON object for an UpdateManager bound view).
12882      */
12883     prepareData : function(data, index, record)
12884     {
12885         this.fireEvent("preparedata", this, data, index, record);
12886         return data;
12887     },
12888
12889     onUpdate : function(ds, record){
12890         // Roo.log('on update');   
12891         this.clearSelections();
12892         var index = this.store.indexOf(record);
12893         var n = this.nodes[index];
12894         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12895         n.parentNode.removeChild(n);
12896         this.updateIndexes(index, index);
12897     },
12898
12899     
12900     
12901 // --------- FIXME     
12902     onAdd : function(ds, records, index)
12903     {
12904         //Roo.log(['on Add', ds, records, index] );        
12905         this.clearSelections();
12906         if(this.nodes.length == 0){
12907             this.refresh();
12908             return;
12909         }
12910         var n = this.nodes[index];
12911         for(var i = 0, len = records.length; i < len; i++){
12912             var d = this.prepareData(records[i].data, i, records[i]);
12913             if(n){
12914                 this.tpl.insertBefore(n, d);
12915             }else{
12916                 
12917                 this.tpl.append(this.el, d);
12918             }
12919         }
12920         this.updateIndexes(index);
12921     },
12922
12923     onRemove : function(ds, record, index){
12924        // Roo.log('onRemove');
12925         this.clearSelections();
12926         var el = this.dataName  ?
12927             this.el.child('.roo-tpl-' + this.dataName) :
12928             this.el; 
12929         
12930         el.dom.removeChild(this.nodes[index]);
12931         this.updateIndexes(index);
12932     },
12933
12934     /**
12935      * Refresh an individual node.
12936      * @param {Number} index
12937      */
12938     refreshNode : function(index){
12939         this.onUpdate(this.store, this.store.getAt(index));
12940     },
12941
12942     updateIndexes : function(startIndex, endIndex){
12943         var ns = this.nodes;
12944         startIndex = startIndex || 0;
12945         endIndex = endIndex || ns.length - 1;
12946         for(var i = startIndex; i <= endIndex; i++){
12947             ns[i].nodeIndex = i;
12948         }
12949     },
12950
12951     /**
12952      * Changes the data store this view uses and refresh the view.
12953      * @param {Store} store
12954      */
12955     setStore : function(store, initial){
12956         if(!initial && this.store){
12957             this.store.un("datachanged", this.refresh);
12958             this.store.un("add", this.onAdd);
12959             this.store.un("remove", this.onRemove);
12960             this.store.un("update", this.onUpdate);
12961             this.store.un("clear", this.refresh);
12962             this.store.un("beforeload", this.onBeforeLoad);
12963             this.store.un("load", this.onLoad);
12964             this.store.un("loadexception", this.onLoad);
12965         }
12966         if(store){
12967           
12968             store.on("datachanged", this.refresh, this);
12969             store.on("add", this.onAdd, this);
12970             store.on("remove", this.onRemove, this);
12971             store.on("update", this.onUpdate, this);
12972             store.on("clear", this.refresh, this);
12973             store.on("beforeload", this.onBeforeLoad, this);
12974             store.on("load", this.onLoad, this);
12975             store.on("loadexception", this.onLoad, this);
12976         }
12977         
12978         if(store){
12979             this.refresh();
12980         }
12981     },
12982     /**
12983      * onbeforeLoad - masks the loading area.
12984      *
12985      */
12986     onBeforeLoad : function(store,opts)
12987     {
12988          //Roo.log('onBeforeLoad');   
12989         if (!opts.add) {
12990             this.el.update("");
12991         }
12992         this.el.mask(this.mask ? this.mask : "Loading" ); 
12993     },
12994     onLoad : function ()
12995     {
12996         this.el.unmask();
12997     },
12998     
12999
13000     /**
13001      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13002      * @param {HTMLElement} node
13003      * @return {HTMLElement} The template node
13004      */
13005     findItemFromChild : function(node){
13006         var el = this.dataName  ?
13007             this.el.child('.roo-tpl-' + this.dataName,true) :
13008             this.el.dom; 
13009         
13010         if(!node || node.parentNode == el){
13011                     return node;
13012             }
13013             var p = node.parentNode;
13014             while(p && p != el){
13015             if(p.parentNode == el){
13016                 return p;
13017             }
13018             p = p.parentNode;
13019         }
13020             return null;
13021     },
13022
13023     /** @ignore */
13024     onClick : function(e){
13025         var item = this.findItemFromChild(e.getTarget());
13026         if(item){
13027             var index = this.indexOf(item);
13028             if(this.onItemClick(item, index, e) !== false){
13029                 this.fireEvent("click", this, index, item, e);
13030             }
13031         }else{
13032             this.clearSelections();
13033         }
13034     },
13035
13036     /** @ignore */
13037     onContextMenu : function(e){
13038         var item = this.findItemFromChild(e.getTarget());
13039         if(item){
13040             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
13041         }
13042     },
13043
13044     /** @ignore */
13045     onDblClick : function(e){
13046         var item = this.findItemFromChild(e.getTarget());
13047         if(item){
13048             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
13049         }
13050     },
13051
13052     onItemClick : function(item, index, e)
13053     {
13054         if(this.fireEvent("beforeclick", this, index, item, e) === false){
13055             return false;
13056         }
13057         if (this.toggleSelect) {
13058             var m = this.isSelected(item) ? 'unselect' : 'select';
13059             //Roo.log(m);
13060             var _t = this;
13061             _t[m](item, true, false);
13062             return true;
13063         }
13064         if(this.multiSelect || this.singleSelect){
13065             if(this.multiSelect && e.shiftKey && this.lastSelection){
13066                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
13067             }else{
13068                 this.select(item, this.multiSelect && e.ctrlKey);
13069                 this.lastSelection = item;
13070             }
13071             
13072             if(!this.tickable){
13073                 e.preventDefault();
13074             }
13075             
13076         }
13077         return true;
13078     },
13079
13080     /**
13081      * Get the number of selected nodes.
13082      * @return {Number}
13083      */
13084     getSelectionCount : function(){
13085         return this.selections.length;
13086     },
13087
13088     /**
13089      * Get the currently selected nodes.
13090      * @return {Array} An array of HTMLElements
13091      */
13092     getSelectedNodes : function(){
13093         return this.selections;
13094     },
13095
13096     /**
13097      * Get the indexes of the selected nodes.
13098      * @return {Array}
13099      */
13100     getSelectedIndexes : function(){
13101         var indexes = [], s = this.selections;
13102         for(var i = 0, len = s.length; i < len; i++){
13103             indexes.push(s[i].nodeIndex);
13104         }
13105         return indexes;
13106     },
13107
13108     /**
13109      * Clear all selections
13110      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
13111      */
13112     clearSelections : function(suppressEvent){
13113         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
13114             this.cmp.elements = this.selections;
13115             this.cmp.removeClass(this.selectedClass);
13116             this.selections = [];
13117             if(!suppressEvent){
13118                 this.fireEvent("selectionchange", this, this.selections);
13119             }
13120         }
13121     },
13122
13123     /**
13124      * Returns true if the passed node is selected
13125      * @param {HTMLElement/Number} node The node or node index
13126      * @return {Boolean}
13127      */
13128     isSelected : function(node){
13129         var s = this.selections;
13130         if(s.length < 1){
13131             return false;
13132         }
13133         node = this.getNode(node);
13134         return s.indexOf(node) !== -1;
13135     },
13136
13137     /**
13138      * Selects nodes.
13139      * @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
13140      * @param {Boolean} keepExisting (optional) true to keep existing selections
13141      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13142      */
13143     select : function(nodeInfo, keepExisting, suppressEvent){
13144         if(nodeInfo instanceof Array){
13145             if(!keepExisting){
13146                 this.clearSelections(true);
13147             }
13148             for(var i = 0, len = nodeInfo.length; i < len; i++){
13149                 this.select(nodeInfo[i], true, true);
13150             }
13151             return;
13152         } 
13153         var node = this.getNode(nodeInfo);
13154         if(!node || this.isSelected(node)){
13155             return; // already selected.
13156         }
13157         if(!keepExisting){
13158             this.clearSelections(true);
13159         }
13160         
13161         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
13162             Roo.fly(node).addClass(this.selectedClass);
13163             this.selections.push(node);
13164             if(!suppressEvent){
13165                 this.fireEvent("selectionchange", this, this.selections);
13166             }
13167         }
13168         
13169         
13170     },
13171       /**
13172      * Unselects nodes.
13173      * @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
13174      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
13175      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13176      */
13177     unselect : function(nodeInfo, keepExisting, suppressEvent)
13178     {
13179         if(nodeInfo instanceof Array){
13180             Roo.each(this.selections, function(s) {
13181                 this.unselect(s, nodeInfo);
13182             }, this);
13183             return;
13184         }
13185         var node = this.getNode(nodeInfo);
13186         if(!node || !this.isSelected(node)){
13187             //Roo.log("not selected");
13188             return; // not selected.
13189         }
13190         // fireevent???
13191         var ns = [];
13192         Roo.each(this.selections, function(s) {
13193             if (s == node ) {
13194                 Roo.fly(node).removeClass(this.selectedClass);
13195
13196                 return;
13197             }
13198             ns.push(s);
13199         },this);
13200         
13201         this.selections= ns;
13202         this.fireEvent("selectionchange", this, this.selections);
13203     },
13204
13205     /**
13206      * Gets a template node.
13207      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13208      * @return {HTMLElement} The node or null if it wasn't found
13209      */
13210     getNode : function(nodeInfo){
13211         if(typeof nodeInfo == "string"){
13212             return document.getElementById(nodeInfo);
13213         }else if(typeof nodeInfo == "number"){
13214             return this.nodes[nodeInfo];
13215         }
13216         return nodeInfo;
13217     },
13218
13219     /**
13220      * Gets a range template nodes.
13221      * @param {Number} startIndex
13222      * @param {Number} endIndex
13223      * @return {Array} An array of nodes
13224      */
13225     getNodes : function(start, end){
13226         var ns = this.nodes;
13227         start = start || 0;
13228         end = typeof end == "undefined" ? ns.length - 1 : end;
13229         var nodes = [];
13230         if(start <= end){
13231             for(var i = start; i <= end; i++){
13232                 nodes.push(ns[i]);
13233             }
13234         } else{
13235             for(var i = start; i >= end; i--){
13236                 nodes.push(ns[i]);
13237             }
13238         }
13239         return nodes;
13240     },
13241
13242     /**
13243      * Finds the index of the passed node
13244      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13245      * @return {Number} The index of the node or -1
13246      */
13247     indexOf : function(node){
13248         node = this.getNode(node);
13249         if(typeof node.nodeIndex == "number"){
13250             return node.nodeIndex;
13251         }
13252         var ns = this.nodes;
13253         for(var i = 0, len = ns.length; i < len; i++){
13254             if(ns[i] == node){
13255                 return i;
13256             }
13257         }
13258         return -1;
13259     }
13260 });
13261 /*
13262  * - LGPL
13263  *
13264  * based on jquery fullcalendar
13265  * 
13266  */
13267
13268 Roo.bootstrap = Roo.bootstrap || {};
13269 /**
13270  * @class Roo.bootstrap.Calendar
13271  * @extends Roo.bootstrap.Component
13272  * Bootstrap Calendar class
13273  * @cfg {Boolean} loadMask (true|false) default false
13274  * @cfg {Object} header generate the user specific header of the calendar, default false
13275
13276  * @constructor
13277  * Create a new Container
13278  * @param {Object} config The config object
13279  */
13280
13281
13282
13283 Roo.bootstrap.Calendar = function(config){
13284     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
13285      this.addEvents({
13286         /**
13287              * @event select
13288              * Fires when a date is selected
13289              * @param {DatePicker} this
13290              * @param {Date} date The selected date
13291              */
13292         'select': true,
13293         /**
13294              * @event monthchange
13295              * Fires when the displayed month changes 
13296              * @param {DatePicker} this
13297              * @param {Date} date The selected month
13298              */
13299         'monthchange': true,
13300         /**
13301              * @event evententer
13302              * Fires when mouse over an event
13303              * @param {Calendar} this
13304              * @param {event} Event
13305              */
13306         'evententer': true,
13307         /**
13308              * @event eventleave
13309              * Fires when the mouse leaves an
13310              * @param {Calendar} this
13311              * @param {event}
13312              */
13313         'eventleave': true,
13314         /**
13315              * @event eventclick
13316              * Fires when the mouse click an
13317              * @param {Calendar} this
13318              * @param {event}
13319              */
13320         'eventclick': true
13321         
13322     });
13323
13324 };
13325
13326 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
13327     
13328      /**
13329      * @cfg {Number} startDay
13330      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
13331      */
13332     startDay : 0,
13333     
13334     loadMask : false,
13335     
13336     header : false,
13337       
13338     getAutoCreate : function(){
13339         
13340         
13341         var fc_button = function(name, corner, style, content ) {
13342             return Roo.apply({},{
13343                 tag : 'span',
13344                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
13345                          (corner.length ?
13346                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
13347                             ''
13348                         ),
13349                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
13350                 unselectable: 'on'
13351             });
13352         };
13353         
13354         var header = {};
13355         
13356         if(!this.header){
13357             header = {
13358                 tag : 'table',
13359                 cls : 'fc-header',
13360                 style : 'width:100%',
13361                 cn : [
13362                     {
13363                         tag: 'tr',
13364                         cn : [
13365                             {
13366                                 tag : 'td',
13367                                 cls : 'fc-header-left',
13368                                 cn : [
13369                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
13370                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
13371                                     { tag: 'span', cls: 'fc-header-space' },
13372                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
13373
13374
13375                                 ]
13376                             },
13377
13378                             {
13379                                 tag : 'td',
13380                                 cls : 'fc-header-center',
13381                                 cn : [
13382                                     {
13383                                         tag: 'span',
13384                                         cls: 'fc-header-title',
13385                                         cn : {
13386                                             tag: 'H2',
13387                                             html : 'month / year'
13388                                         }
13389                                     }
13390
13391                                 ]
13392                             },
13393                             {
13394                                 tag : 'td',
13395                                 cls : 'fc-header-right',
13396                                 cn : [
13397                               /*      fc_button('month', 'left', '', 'month' ),
13398                                     fc_button('week', '', '', 'week' ),
13399                                     fc_button('day', 'right', '', 'day' )
13400                                 */    
13401
13402                                 ]
13403                             }
13404
13405                         ]
13406                     }
13407                 ]
13408             };
13409         }
13410         
13411         header = this.header;
13412         
13413        
13414         var cal_heads = function() {
13415             var ret = [];
13416             // fixme - handle this.
13417             
13418             for (var i =0; i < Date.dayNames.length; i++) {
13419                 var d = Date.dayNames[i];
13420                 ret.push({
13421                     tag: 'th',
13422                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13423                     html : d.substring(0,3)
13424                 });
13425                 
13426             }
13427             ret[0].cls += ' fc-first';
13428             ret[6].cls += ' fc-last';
13429             return ret;
13430         };
13431         var cal_cell = function(n) {
13432             return  {
13433                 tag: 'td',
13434                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13435                 cn : [
13436                     {
13437                         cn : [
13438                             {
13439                                 cls: 'fc-day-number',
13440                                 html: 'D'
13441                             },
13442                             {
13443                                 cls: 'fc-day-content',
13444                              
13445                                 cn : [
13446                                      {
13447                                         style: 'position: relative;' // height: 17px;
13448                                     }
13449                                 ]
13450                             }
13451                             
13452                             
13453                         ]
13454                     }
13455                 ]
13456                 
13457             }
13458         };
13459         var cal_rows = function() {
13460             
13461             var ret = [];
13462             for (var r = 0; r < 6; r++) {
13463                 var row= {
13464                     tag : 'tr',
13465                     cls : 'fc-week',
13466                     cn : []
13467                 };
13468                 
13469                 for (var i =0; i < Date.dayNames.length; i++) {
13470                     var d = Date.dayNames[i];
13471                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13472
13473                 }
13474                 row.cn[0].cls+=' fc-first';
13475                 row.cn[0].cn[0].style = 'min-height:90px';
13476                 row.cn[6].cls+=' fc-last';
13477                 ret.push(row);
13478                 
13479             }
13480             ret[0].cls += ' fc-first';
13481             ret[4].cls += ' fc-prev-last';
13482             ret[5].cls += ' fc-last';
13483             return ret;
13484             
13485         };
13486         
13487         var cal_table = {
13488             tag: 'table',
13489             cls: 'fc-border-separate',
13490             style : 'width:100%',
13491             cellspacing  : 0,
13492             cn : [
13493                 { 
13494                     tag: 'thead',
13495                     cn : [
13496                         { 
13497                             tag: 'tr',
13498                             cls : 'fc-first fc-last',
13499                             cn : cal_heads()
13500                         }
13501                     ]
13502                 },
13503                 { 
13504                     tag: 'tbody',
13505                     cn : cal_rows()
13506                 }
13507                   
13508             ]
13509         };
13510          
13511          var cfg = {
13512             cls : 'fc fc-ltr',
13513             cn : [
13514                 header,
13515                 {
13516                     cls : 'fc-content',
13517                     style : "position: relative;",
13518                     cn : [
13519                         {
13520                             cls : 'fc-view fc-view-month fc-grid',
13521                             style : 'position: relative',
13522                             unselectable : 'on',
13523                             cn : [
13524                                 {
13525                                     cls : 'fc-event-container',
13526                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13527                                 },
13528                                 cal_table
13529                             ]
13530                         }
13531                     ]
13532     
13533                 }
13534            ] 
13535             
13536         };
13537         
13538          
13539         
13540         return cfg;
13541     },
13542     
13543     
13544     initEvents : function()
13545     {
13546         if(!this.store){
13547             throw "can not find store for calendar";
13548         }
13549         
13550         var mark = {
13551             tag: "div",
13552             cls:"x-dlg-mask",
13553             style: "text-align:center",
13554             cn: [
13555                 {
13556                     tag: "div",
13557                     style: "background-color:white;width:50%;margin:250 auto",
13558                     cn: [
13559                         {
13560                             tag: "img",
13561                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13562                         },
13563                         {
13564                             tag: "span",
13565                             html: "Loading"
13566                         }
13567                         
13568                     ]
13569                 }
13570             ]
13571         }
13572         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13573         
13574         var size = this.el.select('.fc-content', true).first().getSize();
13575         this.maskEl.setSize(size.width, size.height);
13576         this.maskEl.enableDisplayMode("block");
13577         if(!this.loadMask){
13578             this.maskEl.hide();
13579         }
13580         
13581         this.store = Roo.factory(this.store, Roo.data);
13582         this.store.on('load', this.onLoad, this);
13583         this.store.on('beforeload', this.onBeforeLoad, this);
13584         
13585         this.resize();
13586         
13587         this.cells = this.el.select('.fc-day',true);
13588         //Roo.log(this.cells);
13589         this.textNodes = this.el.query('.fc-day-number');
13590         this.cells.addClassOnOver('fc-state-hover');
13591         
13592         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13593         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13594         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13595         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13596         
13597         this.on('monthchange', this.onMonthChange, this);
13598         
13599         this.update(new Date().clearTime());
13600     },
13601     
13602     resize : function() {
13603         var sz  = this.el.getSize();
13604         
13605         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13606         this.el.select('.fc-day-content div',true).setHeight(34);
13607     },
13608     
13609     
13610     // private
13611     showPrevMonth : function(e){
13612         this.update(this.activeDate.add("mo", -1));
13613     },
13614     showToday : function(e){
13615         this.update(new Date().clearTime());
13616     },
13617     // private
13618     showNextMonth : function(e){
13619         this.update(this.activeDate.add("mo", 1));
13620     },
13621
13622     // private
13623     showPrevYear : function(){
13624         this.update(this.activeDate.add("y", -1));
13625     },
13626
13627     // private
13628     showNextYear : function(){
13629         this.update(this.activeDate.add("y", 1));
13630     },
13631
13632     
13633    // private
13634     update : function(date)
13635     {
13636         var vd = this.activeDate;
13637         this.activeDate = date;
13638 //        if(vd && this.el){
13639 //            var t = date.getTime();
13640 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13641 //                Roo.log('using add remove');
13642 //                
13643 //                this.fireEvent('monthchange', this, date);
13644 //                
13645 //                this.cells.removeClass("fc-state-highlight");
13646 //                this.cells.each(function(c){
13647 //                   if(c.dateValue == t){
13648 //                       c.addClass("fc-state-highlight");
13649 //                       setTimeout(function(){
13650 //                            try{c.dom.firstChild.focus();}catch(e){}
13651 //                       }, 50);
13652 //                       return false;
13653 //                   }
13654 //                   return true;
13655 //                });
13656 //                return;
13657 //            }
13658 //        }
13659         
13660         var days = date.getDaysInMonth();
13661         
13662         var firstOfMonth = date.getFirstDateOfMonth();
13663         var startingPos = firstOfMonth.getDay()-this.startDay;
13664         
13665         if(startingPos < this.startDay){
13666             startingPos += 7;
13667         }
13668         
13669         var pm = date.add(Date.MONTH, -1);
13670         var prevStart = pm.getDaysInMonth()-startingPos;
13671 //        
13672         this.cells = this.el.select('.fc-day',true);
13673         this.textNodes = this.el.query('.fc-day-number');
13674         this.cells.addClassOnOver('fc-state-hover');
13675         
13676         var cells = this.cells.elements;
13677         var textEls = this.textNodes;
13678         
13679         Roo.each(cells, function(cell){
13680             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13681         });
13682         
13683         days += startingPos;
13684
13685         // convert everything to numbers so it's fast
13686         var day = 86400000;
13687         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13688         //Roo.log(d);
13689         //Roo.log(pm);
13690         //Roo.log(prevStart);
13691         
13692         var today = new Date().clearTime().getTime();
13693         var sel = date.clearTime().getTime();
13694         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13695         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13696         var ddMatch = this.disabledDatesRE;
13697         var ddText = this.disabledDatesText;
13698         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13699         var ddaysText = this.disabledDaysText;
13700         var format = this.format;
13701         
13702         var setCellClass = function(cal, cell){
13703             cell.row = 0;
13704             cell.events = [];
13705             cell.more = [];
13706             //Roo.log('set Cell Class');
13707             cell.title = "";
13708             var t = d.getTime();
13709             
13710             //Roo.log(d);
13711             
13712             cell.dateValue = t;
13713             if(t == today){
13714                 cell.className += " fc-today";
13715                 cell.className += " fc-state-highlight";
13716                 cell.title = cal.todayText;
13717             }
13718             if(t == sel){
13719                 // disable highlight in other month..
13720                 //cell.className += " fc-state-highlight";
13721                 
13722             }
13723             // disabling
13724             if(t < min) {
13725                 cell.className = " fc-state-disabled";
13726                 cell.title = cal.minText;
13727                 return;
13728             }
13729             if(t > max) {
13730                 cell.className = " fc-state-disabled";
13731                 cell.title = cal.maxText;
13732                 return;
13733             }
13734             if(ddays){
13735                 if(ddays.indexOf(d.getDay()) != -1){
13736                     cell.title = ddaysText;
13737                     cell.className = " fc-state-disabled";
13738                 }
13739             }
13740             if(ddMatch && format){
13741                 var fvalue = d.dateFormat(format);
13742                 if(ddMatch.test(fvalue)){
13743                     cell.title = ddText.replace("%0", fvalue);
13744                     cell.className = " fc-state-disabled";
13745                 }
13746             }
13747             
13748             if (!cell.initialClassName) {
13749                 cell.initialClassName = cell.dom.className;
13750             }
13751             
13752             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13753         };
13754
13755         var i = 0;
13756         
13757         for(; i < startingPos; i++) {
13758             textEls[i].innerHTML = (++prevStart);
13759             d.setDate(d.getDate()+1);
13760             
13761             cells[i].className = "fc-past fc-other-month";
13762             setCellClass(this, cells[i]);
13763         }
13764         
13765         var intDay = 0;
13766         
13767         for(; i < days; i++){
13768             intDay = i - startingPos + 1;
13769             textEls[i].innerHTML = (intDay);
13770             d.setDate(d.getDate()+1);
13771             
13772             cells[i].className = ''; // "x-date-active";
13773             setCellClass(this, cells[i]);
13774         }
13775         var extraDays = 0;
13776         
13777         for(; i < 42; i++) {
13778             textEls[i].innerHTML = (++extraDays);
13779             d.setDate(d.getDate()+1);
13780             
13781             cells[i].className = "fc-future fc-other-month";
13782             setCellClass(this, cells[i]);
13783         }
13784         
13785         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13786         
13787         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13788         
13789         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13790         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13791         
13792         if(totalRows != 6){
13793             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13794             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13795         }
13796         
13797         this.fireEvent('monthchange', this, date);
13798         
13799         
13800         /*
13801         if(!this.internalRender){
13802             var main = this.el.dom.firstChild;
13803             var w = main.offsetWidth;
13804             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13805             Roo.fly(main).setWidth(w);
13806             this.internalRender = true;
13807             // opera does not respect the auto grow header center column
13808             // then, after it gets a width opera refuses to recalculate
13809             // without a second pass
13810             if(Roo.isOpera && !this.secondPass){
13811                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13812                 this.secondPass = true;
13813                 this.update.defer(10, this, [date]);
13814             }
13815         }
13816         */
13817         
13818     },
13819     
13820     findCell : function(dt) {
13821         dt = dt.clearTime().getTime();
13822         var ret = false;
13823         this.cells.each(function(c){
13824             //Roo.log("check " +c.dateValue + '?=' + dt);
13825             if(c.dateValue == dt){
13826                 ret = c;
13827                 return false;
13828             }
13829             return true;
13830         });
13831         
13832         return ret;
13833     },
13834     
13835     findCells : function(ev) {
13836         var s = ev.start.clone().clearTime().getTime();
13837        // Roo.log(s);
13838         var e= ev.end.clone().clearTime().getTime();
13839        // Roo.log(e);
13840         var ret = [];
13841         this.cells.each(function(c){
13842              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13843             
13844             if(c.dateValue > e){
13845                 return ;
13846             }
13847             if(c.dateValue < s){
13848                 return ;
13849             }
13850             ret.push(c);
13851         });
13852         
13853         return ret;    
13854     },
13855     
13856 //    findBestRow: function(cells)
13857 //    {
13858 //        var ret = 0;
13859 //        
13860 //        for (var i =0 ; i < cells.length;i++) {
13861 //            ret  = Math.max(cells[i].rows || 0,ret);
13862 //        }
13863 //        return ret;
13864 //        
13865 //    },
13866     
13867     
13868     addItem : function(ev)
13869     {
13870         // look for vertical location slot in
13871         var cells = this.findCells(ev);
13872         
13873 //        ev.row = this.findBestRow(cells);
13874         
13875         // work out the location.
13876         
13877         var crow = false;
13878         var rows = [];
13879         for(var i =0; i < cells.length; i++) {
13880             
13881             cells[i].row = cells[0].row;
13882             
13883             if(i == 0){
13884                 cells[i].row = cells[i].row + 1;
13885             }
13886             
13887             if (!crow) {
13888                 crow = {
13889                     start : cells[i],
13890                     end :  cells[i]
13891                 };
13892                 continue;
13893             }
13894             if (crow.start.getY() == cells[i].getY()) {
13895                 // on same row.
13896                 crow.end = cells[i];
13897                 continue;
13898             }
13899             // different row.
13900             rows.push(crow);
13901             crow = {
13902                 start: cells[i],
13903                 end : cells[i]
13904             };
13905             
13906         }
13907         
13908         rows.push(crow);
13909         ev.els = [];
13910         ev.rows = rows;
13911         ev.cells = cells;
13912         
13913         cells[0].events.push(ev);
13914         
13915         this.calevents.push(ev);
13916     },
13917     
13918     clearEvents: function() {
13919         
13920         if(!this.calevents){
13921             return;
13922         }
13923         
13924         Roo.each(this.cells.elements, function(c){
13925             c.row = 0;
13926             c.events = [];
13927             c.more = [];
13928         });
13929         
13930         Roo.each(this.calevents, function(e) {
13931             Roo.each(e.els, function(el) {
13932                 el.un('mouseenter' ,this.onEventEnter, this);
13933                 el.un('mouseleave' ,this.onEventLeave, this);
13934                 el.remove();
13935             },this);
13936         },this);
13937         
13938         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13939             e.remove();
13940         });
13941         
13942     },
13943     
13944     renderEvents: function()
13945     {   
13946         var _this = this;
13947         
13948         this.cells.each(function(c) {
13949             
13950             if(c.row < 5){
13951                 return;
13952             }
13953             
13954             var ev = c.events;
13955             
13956             var r = 4;
13957             if(c.row != c.events.length){
13958                 r = 4 - (4 - (c.row - c.events.length));
13959             }
13960             
13961             c.events = ev.slice(0, r);
13962             c.more = ev.slice(r);
13963             
13964             if(c.more.length && c.more.length == 1){
13965                 c.events.push(c.more.pop());
13966             }
13967             
13968             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13969             
13970         });
13971             
13972         this.cells.each(function(c) {
13973             
13974             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13975             
13976             
13977             for (var e = 0; e < c.events.length; e++){
13978                 var ev = c.events[e];
13979                 var rows = ev.rows;
13980                 
13981                 for(var i = 0; i < rows.length; i++) {
13982                 
13983                     // how many rows should it span..
13984
13985                     var  cfg = {
13986                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13987                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13988
13989                         unselectable : "on",
13990                         cn : [
13991                             {
13992                                 cls: 'fc-event-inner',
13993                                 cn : [
13994     //                                {
13995     //                                  tag:'span',
13996     //                                  cls: 'fc-event-time',
13997     //                                  html : cells.length > 1 ? '' : ev.time
13998     //                                },
13999                                     {
14000                                       tag:'span',
14001                                       cls: 'fc-event-title',
14002                                       html : String.format('{0}', ev.title)
14003                                     }
14004
14005
14006                                 ]
14007                             },
14008                             {
14009                                 cls: 'ui-resizable-handle ui-resizable-e',
14010                                 html : '&nbsp;&nbsp;&nbsp'
14011                             }
14012
14013                         ]
14014                     };
14015
14016                     if (i == 0) {
14017                         cfg.cls += ' fc-event-start';
14018                     }
14019                     if ((i+1) == rows.length) {
14020                         cfg.cls += ' fc-event-end';
14021                     }
14022
14023                     var ctr = _this.el.select('.fc-event-container',true).first();
14024                     var cg = ctr.createChild(cfg);
14025
14026                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
14027                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
14028
14029                     var r = (c.more.length) ? 1 : 0;
14030                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
14031                     cg.setWidth(ebox.right - sbox.x -2);
14032
14033                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
14034                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
14035                     cg.on('click', _this.onEventClick, _this, ev);
14036
14037                     ev.els.push(cg);
14038                     
14039                 }
14040                 
14041             }
14042             
14043             
14044             if(c.more.length){
14045                 var  cfg = {
14046                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
14047                     style : 'position: absolute',
14048                     unselectable : "on",
14049                     cn : [
14050                         {
14051                             cls: 'fc-event-inner',
14052                             cn : [
14053                                 {
14054                                   tag:'span',
14055                                   cls: 'fc-event-title',
14056                                   html : 'More'
14057                                 }
14058
14059
14060                             ]
14061                         },
14062                         {
14063                             cls: 'ui-resizable-handle ui-resizable-e',
14064                             html : '&nbsp;&nbsp;&nbsp'
14065                         }
14066
14067                     ]
14068                 };
14069
14070                 var ctr = _this.el.select('.fc-event-container',true).first();
14071                 var cg = ctr.createChild(cfg);
14072
14073                 var sbox = c.select('.fc-day-content',true).first().getBox();
14074                 var ebox = c.select('.fc-day-content',true).first().getBox();
14075                 //Roo.log(cg);
14076                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
14077                 cg.setWidth(ebox.right - sbox.x -2);
14078
14079                 cg.on('click', _this.onMoreEventClick, _this, c.more);
14080                 
14081             }
14082             
14083         });
14084         
14085         
14086         
14087     },
14088     
14089     onEventEnter: function (e, el,event,d) {
14090         this.fireEvent('evententer', this, el, event);
14091     },
14092     
14093     onEventLeave: function (e, el,event,d) {
14094         this.fireEvent('eventleave', this, el, event);
14095     },
14096     
14097     onEventClick: function (e, el,event,d) {
14098         this.fireEvent('eventclick', this, el, event);
14099     },
14100     
14101     onMonthChange: function () {
14102         this.store.load();
14103     },
14104     
14105     onMoreEventClick: function(e, el, more)
14106     {
14107         var _this = this;
14108         
14109         this.calpopover.placement = 'right';
14110         this.calpopover.setTitle('More');
14111         
14112         this.calpopover.setContent('');
14113         
14114         var ctr = this.calpopover.el.select('.popover-content', true).first();
14115         
14116         Roo.each(more, function(m){
14117             var cfg = {
14118                 cls : 'fc-event-hori fc-event-draggable',
14119                 html : m.title
14120             }
14121             var cg = ctr.createChild(cfg);
14122             
14123             cg.on('click', _this.onEventClick, _this, m);
14124         });
14125         
14126         this.calpopover.show(el);
14127         
14128         
14129     },
14130     
14131     onLoad: function () 
14132     {   
14133         this.calevents = [];
14134         var cal = this;
14135         
14136         if(this.store.getCount() > 0){
14137             this.store.data.each(function(d){
14138                cal.addItem({
14139                     id : d.data.id,
14140                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
14141                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
14142                     time : d.data.start_time,
14143                     title : d.data.title,
14144                     description : d.data.description,
14145                     venue : d.data.venue
14146                 });
14147             });
14148         }
14149         
14150         this.renderEvents();
14151         
14152         if(this.calevents.length && this.loadMask){
14153             this.maskEl.hide();
14154         }
14155     },
14156     
14157     onBeforeLoad: function()
14158     {
14159         this.clearEvents();
14160         if(this.loadMask){
14161             this.maskEl.show();
14162         }
14163     }
14164 });
14165
14166  
14167  /*
14168  * - LGPL
14169  *
14170  * element
14171  * 
14172  */
14173
14174 /**
14175  * @class Roo.bootstrap.Popover
14176  * @extends Roo.bootstrap.Component
14177  * Bootstrap Popover class
14178  * @cfg {String} html contents of the popover   (or false to use children..)
14179  * @cfg {String} title of popover (or false to hide)
14180  * @cfg {String} placement how it is placed
14181  * @cfg {String} trigger click || hover (or false to trigger manually)
14182  * @cfg {String} over what (parent or false to trigger manually.)
14183  * @cfg {Number} delay - delay before showing
14184  
14185  * @constructor
14186  * Create a new Popover
14187  * @param {Object} config The config object
14188  */
14189
14190 Roo.bootstrap.Popover = function(config){
14191     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
14192 };
14193
14194 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
14195     
14196     title: 'Fill in a title',
14197     html: false,
14198     
14199     placement : 'right',
14200     trigger : 'hover', // hover
14201     
14202     delay : 0,
14203     
14204     over: 'parent',
14205     
14206     can_build_overlaid : false,
14207     
14208     getChildContainer : function()
14209     {
14210         return this.el.select('.popover-content',true).first();
14211     },
14212     
14213     getAutoCreate : function(){
14214          Roo.log('make popover?');
14215         var cfg = {
14216            cls : 'popover roo-dynamic',
14217            style: 'display:block',
14218            cn : [
14219                 {
14220                     cls : 'arrow'
14221                 },
14222                 {
14223                     cls : 'popover-inner',
14224                     cn : [
14225                         {
14226                             tag: 'h3',
14227                             cls: 'popover-title',
14228                             html : this.title
14229                         },
14230                         {
14231                             cls : 'popover-content',
14232                             html : this.html
14233                         }
14234                     ]
14235                     
14236                 }
14237            ]
14238         };
14239         
14240         return cfg;
14241     },
14242     setTitle: function(str)
14243     {
14244         this.el.select('.popover-title',true).first().dom.innerHTML = str;
14245     },
14246     setContent: function(str)
14247     {
14248         this.el.select('.popover-content',true).first().dom.innerHTML = str;
14249     },
14250     // as it get's added to the bottom of the page.
14251     onRender : function(ct, position)
14252     {
14253         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
14254         if(!this.el){
14255             var cfg = Roo.apply({},  this.getAutoCreate());
14256             cfg.id = Roo.id();
14257             
14258             if (this.cls) {
14259                 cfg.cls += ' ' + this.cls;
14260             }
14261             if (this.style) {
14262                 cfg.style = this.style;
14263             }
14264             Roo.log("adding to ")
14265             this.el = Roo.get(document.body).createChild(cfg, position);
14266             Roo.log(this.el);
14267         }
14268         this.initEvents();
14269     },
14270     
14271     initEvents : function()
14272     {
14273         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
14274         this.el.enableDisplayMode('block');
14275         this.el.hide();
14276         if (this.over === false) {
14277             return; 
14278         }
14279         if (this.triggers === false) {
14280             return;
14281         }
14282         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14283         var triggers = this.trigger ? this.trigger.split(' ') : [];
14284         Roo.each(triggers, function(trigger) {
14285         
14286             if (trigger == 'click') {
14287                 on_el.on('click', this.toggle, this);
14288             } else if (trigger != 'manual') {
14289                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
14290                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
14291       
14292                 on_el.on(eventIn  ,this.enter, this);
14293                 on_el.on(eventOut, this.leave, this);
14294             }
14295         }, this);
14296         
14297     },
14298     
14299     
14300     // private
14301     timeout : null,
14302     hoverState : null,
14303     
14304     toggle : function () {
14305         this.hoverState == 'in' ? this.leave() : this.enter();
14306     },
14307     
14308     enter : function () {
14309        
14310     
14311         clearTimeout(this.timeout);
14312     
14313         this.hoverState = 'in';
14314     
14315         if (!this.delay || !this.delay.show) {
14316             this.show();
14317             return;
14318         }
14319         var _t = this;
14320         this.timeout = setTimeout(function () {
14321             if (_t.hoverState == 'in') {
14322                 _t.show();
14323             }
14324         }, this.delay.show)
14325     },
14326     leave : function() {
14327         clearTimeout(this.timeout);
14328     
14329         this.hoverState = 'out';
14330     
14331         if (!this.delay || !this.delay.hide) {
14332             this.hide();
14333             return;
14334         }
14335         var _t = this;
14336         this.timeout = setTimeout(function () {
14337             if (_t.hoverState == 'out') {
14338                 _t.hide();
14339             }
14340         }, this.delay.hide)
14341     },
14342     
14343     show : function (on_el)
14344     {
14345         if (!on_el) {
14346             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14347         }
14348         // set content.
14349         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
14350         if (this.html !== false) {
14351             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
14352         }
14353         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
14354         if (!this.title.length) {
14355             this.el.select('.popover-title',true).hide();
14356         }
14357         
14358         var placement = typeof this.placement == 'function' ?
14359             this.placement.call(this, this.el, on_el) :
14360             this.placement;
14361             
14362         var autoToken = /\s?auto?\s?/i;
14363         var autoPlace = autoToken.test(placement);
14364         if (autoPlace) {
14365             placement = placement.replace(autoToken, '') || 'top';
14366         }
14367         
14368         //this.el.detach()
14369         //this.el.setXY([0,0]);
14370         this.el.show();
14371         this.el.dom.style.display='block';
14372         this.el.addClass(placement);
14373         
14374         //this.el.appendTo(on_el);
14375         
14376         var p = this.getPosition();
14377         var box = this.el.getBox();
14378         
14379         if (autoPlace) {
14380             // fixme..
14381         }
14382         var align = Roo.bootstrap.Popover.alignment[placement];
14383         this.el.alignTo(on_el, align[0],align[1]);
14384         //var arrow = this.el.select('.arrow',true).first();
14385         //arrow.set(align[2], 
14386         
14387         this.el.addClass('in');
14388         this.hoverState = null;
14389         
14390         if (this.el.hasClass('fade')) {
14391             // fade it?
14392         }
14393         
14394     },
14395     hide : function()
14396     {
14397         this.el.setXY([0,0]);
14398         this.el.removeClass('in');
14399         this.el.hide();
14400         
14401     }
14402     
14403 });
14404
14405 Roo.bootstrap.Popover.alignment = {
14406     'left' : ['r-l', [-10,0], 'right'],
14407     'right' : ['l-r', [10,0], 'left'],
14408     'bottom' : ['t-b', [0,10], 'top'],
14409     'top' : [ 'b-t', [0,-10], 'bottom']
14410 };
14411
14412  /*
14413  * - LGPL
14414  *
14415  * Progress
14416  * 
14417  */
14418
14419 /**
14420  * @class Roo.bootstrap.Progress
14421  * @extends Roo.bootstrap.Component
14422  * Bootstrap Progress class
14423  * @cfg {Boolean} striped striped of the progress bar
14424  * @cfg {Boolean} active animated of the progress bar
14425  * 
14426  * 
14427  * @constructor
14428  * Create a new Progress
14429  * @param {Object} config The config object
14430  */
14431
14432 Roo.bootstrap.Progress = function(config){
14433     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14434 };
14435
14436 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14437     
14438     striped : false,
14439     active: false,
14440     
14441     getAutoCreate : function(){
14442         var cfg = {
14443             tag: 'div',
14444             cls: 'progress'
14445         };
14446         
14447         
14448         if(this.striped){
14449             cfg.cls += ' progress-striped';
14450         }
14451       
14452         if(this.active){
14453             cfg.cls += ' active';
14454         }
14455         
14456         
14457         return cfg;
14458     }
14459    
14460 });
14461
14462  
14463
14464  /*
14465  * - LGPL
14466  *
14467  * ProgressBar
14468  * 
14469  */
14470
14471 /**
14472  * @class Roo.bootstrap.ProgressBar
14473  * @extends Roo.bootstrap.Component
14474  * Bootstrap ProgressBar class
14475  * @cfg {Number} aria_valuenow aria-value now
14476  * @cfg {Number} aria_valuemin aria-value min
14477  * @cfg {Number} aria_valuemax aria-value max
14478  * @cfg {String} label label for the progress bar
14479  * @cfg {String} panel (success | info | warning | danger )
14480  * @cfg {String} role role of the progress bar
14481  * @cfg {String} sr_only text
14482  * 
14483  * 
14484  * @constructor
14485  * Create a new ProgressBar
14486  * @param {Object} config The config object
14487  */
14488
14489 Roo.bootstrap.ProgressBar = function(config){
14490     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14491 };
14492
14493 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14494     
14495     aria_valuenow : 0,
14496     aria_valuemin : 0,
14497     aria_valuemax : 100,
14498     label : false,
14499     panel : false,
14500     role : false,
14501     sr_only: false,
14502     
14503     getAutoCreate : function()
14504     {
14505         
14506         var cfg = {
14507             tag: 'div',
14508             cls: 'progress-bar',
14509             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14510         };
14511         
14512         if(this.sr_only){
14513             cfg.cn = {
14514                 tag: 'span',
14515                 cls: 'sr-only',
14516                 html: this.sr_only
14517             }
14518         }
14519         
14520         if(this.role){
14521             cfg.role = this.role;
14522         }
14523         
14524         if(this.aria_valuenow){
14525             cfg['aria-valuenow'] = this.aria_valuenow;
14526         }
14527         
14528         if(this.aria_valuemin){
14529             cfg['aria-valuemin'] = this.aria_valuemin;
14530         }
14531         
14532         if(this.aria_valuemax){
14533             cfg['aria-valuemax'] = this.aria_valuemax;
14534         }
14535         
14536         if(this.label && !this.sr_only){
14537             cfg.html = this.label;
14538         }
14539         
14540         if(this.panel){
14541             cfg.cls += ' progress-bar-' + this.panel;
14542         }
14543         
14544         return cfg;
14545     },
14546     
14547     update : function(aria_valuenow)
14548     {
14549         this.aria_valuenow = aria_valuenow;
14550         
14551         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14552     }
14553    
14554 });
14555
14556  
14557
14558  /*
14559  * - LGPL
14560  *
14561  * column
14562  * 
14563  */
14564
14565 /**
14566  * @class Roo.bootstrap.TabGroup
14567  * @extends Roo.bootstrap.Column
14568  * Bootstrap Column class
14569  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14570  * @cfg {Boolean} carousel true to make the group behave like a carousel
14571  * @cfg {Number} bullets show the panel pointer.. default 0
14572  * @cfg {Boolena} autoslide (true|false) auto slide .. default false
14573  * @cfg {Number} timer auto slide timer .. default 0 millisecond
14574  * 
14575  * @constructor
14576  * Create a new TabGroup
14577  * @param {Object} config The config object
14578  */
14579
14580 Roo.bootstrap.TabGroup = function(config){
14581     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14582     if (!this.navId) {
14583         this.navId = Roo.id();
14584     }
14585     this.tabs = [];
14586     Roo.bootstrap.TabGroup.register(this);
14587     
14588 };
14589
14590 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14591     
14592     carousel : false,
14593     transition : false,
14594     bullets : 0,
14595     timer : 0,
14596     autoslide : false,
14597     slideFn : false,
14598     
14599     getAutoCreate : function()
14600     {
14601         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14602         
14603         cfg.cls += ' tab-content';
14604         
14605         if (this.carousel) {
14606             cfg.cls += ' carousel slide';
14607             
14608             cfg.cn = [{
14609                cls : 'carousel-inner'
14610             }];
14611         
14612             if(this.bullets > 0){
14613                 
14614                 var bullets = {
14615                     cls : 'carousel-bullets',
14616                     cn : []
14617                 };
14618                 
14619                 for (var i = 0; i < this.bullets; i++){
14620                     bullets.cn.push({
14621                         cls : 'bullet bullet-' + i
14622                     });
14623                 }
14624                 
14625                 bullets.cn.push({
14626                     cls : 'clear'
14627                 });
14628                 
14629                 cfg.cn[0].cn = bullets;
14630             }
14631         }
14632         
14633         return cfg;
14634     },
14635     
14636     initEvents:  function()
14637     {
14638         Roo.log('-------- init events on tab group ---------');
14639         
14640         var _this = this;
14641         
14642         if(this.bullets > 0){
14643             
14644             for (var i = 0; i < this.bullets; i++){
14645                 var bullet = this.el.select('.bullet-' + i, true).first();
14646                 
14647                 if(!bullet){
14648                     continue;
14649                 }
14650                 
14651                 bullet.on('click', (function(e, el, o, ii, t){
14652                     
14653                     e.preventDefault();
14654                     
14655                     _this.showPanel(ii);
14656                     
14657                 }).createDelegate(this, [i, bullet], true));
14658                 
14659             }
14660         }
14661         
14662         if(this.autoslide){
14663             this.slideFn = window.setInterval(function() {
14664                 _this.showPanelNext();
14665             }, this.timer);
14666         }
14667     },
14668     
14669     getChildContainer : function()
14670     {
14671         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14672     },
14673     
14674     /**
14675     * register a Navigation item
14676     * @param {Roo.bootstrap.NavItem} the navitem to add
14677     */
14678     register : function(item)
14679     {
14680         this.tabs.push( item);
14681         item.navId = this.navId; // not really needed..
14682     
14683     },
14684     
14685     getActivePanel : function()
14686     {
14687         var r = false;
14688         Roo.each(this.tabs, function(t) {
14689             if (t.active) {
14690                 r = t;
14691                 return false;
14692             }
14693             return null;
14694         });
14695         return r;
14696         
14697     },
14698     getPanelByName : function(n)
14699     {
14700         var r = false;
14701         Roo.each(this.tabs, function(t) {
14702             if (t.tabId == n) {
14703                 r = t;
14704                 return false;
14705             }
14706             return null;
14707         });
14708         return r;
14709     },
14710     indexOfPanel : function(p)
14711     {
14712         var r = false;
14713         Roo.each(this.tabs, function(t,i) {
14714             if (t.tabId == p.tabId) {
14715                 r = i;
14716                 return false;
14717             }
14718             return null;
14719         });
14720         return r;
14721     },
14722     /**
14723      * show a specific panel
14724      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14725      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14726      */
14727     showPanel : function (pan)
14728     {
14729         if(this.transition){
14730             Roo.log("waiting for the transitionend");
14731             return;
14732         }
14733         
14734         if (typeof(pan) == 'number') {
14735             pan = this.tabs[pan];
14736         }
14737         if (typeof(pan) == 'string') {
14738             pan = this.getPanelByName(pan);
14739         }
14740         if (pan.tabId == this.getActivePanel().tabId) {
14741             return true;
14742         }
14743         var cur = this.getActivePanel();
14744         
14745         if (false === cur.fireEvent('beforedeactivate')) {
14746             return false;
14747         }
14748         
14749         if(this.bullets > 0){
14750             this.setActiveBullet(this.indexOfPanel(pan));
14751         }
14752         
14753         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
14754             
14755             this.transition = true;
14756             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14757             var lr = dir == 'next' ? 'left' : 'right';
14758             pan.el.addClass(dir); // or prev
14759             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14760             cur.el.addClass(lr); // or right
14761             pan.el.addClass(lr);
14762             
14763             var _this = this;
14764             cur.el.on('transitionend', function() {
14765                 Roo.log("trans end?");
14766                 
14767                 pan.el.removeClass([lr,dir]);
14768                 pan.setActive(true);
14769                 
14770                 cur.el.removeClass([lr]);
14771                 cur.setActive(false);
14772                 
14773                 _this.transition = false;
14774                 
14775             }, this, { single:  true } );
14776             
14777             return true;
14778         }
14779         
14780         cur.setActive(false);
14781         pan.setActive(true);
14782         
14783         return true;
14784         
14785     },
14786     showPanelNext : function()
14787     {
14788         var i = this.indexOfPanel(this.getActivePanel());
14789         
14790         if (i >= this.tabs.length - 1 && !this.autoslide) {
14791             return;
14792         }
14793         
14794         if (i >= this.tabs.length - 1 && this.autoslide) {
14795             i = -1;
14796         }
14797         
14798         this.showPanel(this.tabs[i+1]);
14799     },
14800     
14801     showPanelPrev : function()
14802     {
14803         var i = this.indexOfPanel(this.getActivePanel());
14804         
14805         if (i  < 1 && !this.autoslide) {
14806             return;
14807         }
14808         
14809         if (i < 1 && this.autoslide) {
14810             i = this.tabs.length;
14811         }
14812         
14813         this.showPanel(this.tabs[i-1]);
14814     },
14815     
14816     setActiveBullet : function(i)
14817     {
14818         Roo.each(this.el.select('.bullet', true).elements, function(el){
14819             el.removeClass('selected');
14820         });
14821
14822         var bullet = this.el.select('.bullet-' + i, true).first();
14823         
14824         if(!bullet){
14825             return;
14826         }
14827         
14828         bullet.addClass('selected');
14829     }
14830     
14831     
14832   
14833 });
14834
14835  
14836
14837  
14838  
14839 Roo.apply(Roo.bootstrap.TabGroup, {
14840     
14841     groups: {},
14842      /**
14843     * register a Navigation Group
14844     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14845     */
14846     register : function(navgrp)
14847     {
14848         this.groups[navgrp.navId] = navgrp;
14849         
14850     },
14851     /**
14852     * fetch a Navigation Group based on the navigation ID
14853     * if one does not exist , it will get created.
14854     * @param {string} the navgroup to add
14855     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14856     */
14857     get: function(navId) {
14858         if (typeof(this.groups[navId]) == 'undefined') {
14859             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14860         }
14861         return this.groups[navId] ;
14862     }
14863     
14864     
14865     
14866 });
14867
14868  /*
14869  * - LGPL
14870  *
14871  * TabPanel
14872  * 
14873  */
14874
14875 /**
14876  * @class Roo.bootstrap.TabPanel
14877  * @extends Roo.bootstrap.Component
14878  * Bootstrap TabPanel class
14879  * @cfg {Boolean} active panel active
14880  * @cfg {String} html panel content
14881  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14882  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14883  * 
14884  * 
14885  * @constructor
14886  * Create a new TabPanel
14887  * @param {Object} config The config object
14888  */
14889
14890 Roo.bootstrap.TabPanel = function(config){
14891     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14892     this.addEvents({
14893         /**
14894              * @event changed
14895              * Fires when the active status changes
14896              * @param {Roo.bootstrap.TabPanel} this
14897              * @param {Boolean} state the new state
14898             
14899          */
14900         'changed': true,
14901         /**
14902              * @event beforedeactivate
14903              * Fires before a tab is de-activated - can be used to do validation on a form.
14904              * @param {Roo.bootstrap.TabPanel} this
14905              * @return {Boolean} false if there is an error
14906             
14907          */
14908         'beforedeactivate': true
14909      });
14910     
14911     this.tabId = this.tabId || Roo.id();
14912   
14913 };
14914
14915 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14916     
14917     active: false,
14918     html: false,
14919     tabId: false,
14920     navId : false,
14921     
14922     getAutoCreate : function(){
14923         var cfg = {
14924             tag: 'div',
14925             // item is needed for carousel - not sure if it has any effect otherwise
14926             cls: 'tab-pane item',
14927             html: this.html || ''
14928         };
14929         
14930         if(this.active){
14931             cfg.cls += ' active';
14932         }
14933         
14934         if(this.tabId){
14935             cfg.tabId = this.tabId;
14936         }
14937         
14938         
14939         return cfg;
14940     },
14941     
14942     initEvents:  function()
14943     {
14944         Roo.log('-------- init events on tab panel ---------');
14945         
14946         var p = this.parent();
14947         this.navId = this.navId || p.navId;
14948         
14949         if (typeof(this.navId) != 'undefined') {
14950             // not really needed.. but just in case.. parent should be a NavGroup.
14951             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14952             Roo.log(['register', tg, this]);
14953             tg.register(this);
14954             
14955             var i = tg.tabs.length - 1;
14956             
14957             if(this.active && tg.bullets > 0 && i < tg.bullets){
14958                 tg.setActiveBullet(i);
14959             }
14960         }
14961         
14962     },
14963     
14964     
14965     onRender : function(ct, position)
14966     {
14967        // Roo.log("Call onRender: " + this.xtype);
14968         
14969         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14970         
14971         
14972         
14973         
14974         
14975     },
14976     
14977     setActive: function(state)
14978     {
14979         Roo.log("panel - set active " + this.tabId + "=" + state);
14980         
14981         this.active = state;
14982         if (!state) {
14983             this.el.removeClass('active');
14984             
14985         } else  if (!this.el.hasClass('active')) {
14986             this.el.addClass('active');
14987         }
14988         
14989         this.fireEvent('changed', this, state);
14990     }
14991     
14992     
14993 });
14994  
14995
14996  
14997
14998  /*
14999  * - LGPL
15000  *
15001  * DateField
15002  * 
15003  */
15004
15005 /**
15006  * @class Roo.bootstrap.DateField
15007  * @extends Roo.bootstrap.Input
15008  * Bootstrap DateField class
15009  * @cfg {Number} weekStart default 0
15010  * @cfg {String} viewMode default empty, (months|years)
15011  * @cfg {String} minViewMode default empty, (months|years)
15012  * @cfg {Number} startDate default -Infinity
15013  * @cfg {Number} endDate default Infinity
15014  * @cfg {Boolean} todayHighlight default false
15015  * @cfg {Boolean} todayBtn default false
15016  * @cfg {Boolean} calendarWeeks default false
15017  * @cfg {Object} daysOfWeekDisabled default empty
15018  * @cfg {Boolean} singleMode default false (true | false)
15019  * 
15020  * @cfg {Boolean} keyboardNavigation default true
15021  * @cfg {String} language default en
15022  * 
15023  * @constructor
15024  * Create a new DateField
15025  * @param {Object} config The config object
15026  */
15027
15028 Roo.bootstrap.DateField = function(config){
15029     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
15030      this.addEvents({
15031             /**
15032              * @event show
15033              * Fires when this field show.
15034              * @param {Roo.bootstrap.DateField} this
15035              * @param {Mixed} date The date value
15036              */
15037             show : true,
15038             /**
15039              * @event show
15040              * Fires when this field hide.
15041              * @param {Roo.bootstrap.DateField} this
15042              * @param {Mixed} date The date value
15043              */
15044             hide : true,
15045             /**
15046              * @event select
15047              * Fires when select a date.
15048              * @param {Roo.bootstrap.DateField} this
15049              * @param {Mixed} date The date value
15050              */
15051             select : true
15052         });
15053 };
15054
15055 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
15056     
15057     /**
15058      * @cfg {String} format
15059      * The default date format string which can be overriden for localization support.  The format must be
15060      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
15061      */
15062     format : "m/d/y",
15063     /**
15064      * @cfg {String} altFormats
15065      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
15066      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
15067      */
15068     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
15069     
15070     weekStart : 0,
15071     
15072     viewMode : '',
15073     
15074     minViewMode : '',
15075     
15076     todayHighlight : false,
15077     
15078     todayBtn: false,
15079     
15080     language: 'en',
15081     
15082     keyboardNavigation: true,
15083     
15084     calendarWeeks: false,
15085     
15086     startDate: -Infinity,
15087     
15088     endDate: Infinity,
15089     
15090     daysOfWeekDisabled: [],
15091     
15092     _events: [],
15093     
15094     singleMode : false,
15095     
15096     UTCDate: function()
15097     {
15098         return new Date(Date.UTC.apply(Date, arguments));
15099     },
15100     
15101     UTCToday: function()
15102     {
15103         var today = new Date();
15104         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
15105     },
15106     
15107     getDate: function() {
15108             var d = this.getUTCDate();
15109             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
15110     },
15111     
15112     getUTCDate: function() {
15113             return this.date;
15114     },
15115     
15116     setDate: function(d) {
15117             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
15118     },
15119     
15120     setUTCDate: function(d) {
15121             this.date = d;
15122             this.setValue(this.formatDate(this.date));
15123     },
15124         
15125     onRender: function(ct, position)
15126     {
15127         
15128         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
15129         
15130         this.language = this.language || 'en';
15131         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
15132         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
15133         
15134         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
15135         this.format = this.format || 'm/d/y';
15136         this.isInline = false;
15137         this.isInput = true;
15138         this.component = this.el.select('.add-on', true).first() || false;
15139         this.component = (this.component && this.component.length === 0) ? false : this.component;
15140         this.hasInput = this.component && this.inputEL().length;
15141         
15142         if (typeof(this.minViewMode === 'string')) {
15143             switch (this.minViewMode) {
15144                 case 'months':
15145                     this.minViewMode = 1;
15146                     break;
15147                 case 'years':
15148                     this.minViewMode = 2;
15149                     break;
15150                 default:
15151                     this.minViewMode = 0;
15152                     break;
15153             }
15154         }
15155         
15156         if (typeof(this.viewMode === 'string')) {
15157             switch (this.viewMode) {
15158                 case 'months':
15159                     this.viewMode = 1;
15160                     break;
15161                 case 'years':
15162                     this.viewMode = 2;
15163                     break;
15164                 default:
15165                     this.viewMode = 0;
15166                     break;
15167             }
15168         }
15169                 
15170         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
15171         
15172 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
15173         
15174         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15175         
15176         this.picker().on('mousedown', this.onMousedown, this);
15177         this.picker().on('click', this.onClick, this);
15178         
15179         this.picker().addClass('datepicker-dropdown');
15180         
15181         this.startViewMode = this.viewMode;
15182         
15183         if(this.singleMode){
15184             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
15185                 v.setVisibilityMode(Roo.Element.DISPLAY)
15186                 v.hide();
15187             });
15188             
15189             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
15190                 v.setStyle('width', '189px');
15191             });
15192         }
15193         
15194         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
15195             if(!this.calendarWeeks){
15196                 v.remove();
15197                 return;
15198             }
15199             
15200             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15201             v.attr('colspan', function(i, val){
15202                 return parseInt(val) + 1;
15203             });
15204         })
15205                         
15206         
15207         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
15208         
15209         this.setStartDate(this.startDate);
15210         this.setEndDate(this.endDate);
15211         
15212         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
15213         
15214         this.fillDow();
15215         this.fillMonths();
15216         this.update();
15217         this.showMode();
15218         
15219         if(this.isInline) {
15220             this.show();
15221         }
15222     },
15223     
15224     picker : function()
15225     {
15226         return this.pickerEl;
15227 //        return this.el.select('.datepicker', true).first();
15228     },
15229     
15230     fillDow: function()
15231     {
15232         var dowCnt = this.weekStart;
15233         
15234         var dow = {
15235             tag: 'tr',
15236             cn: [
15237                 
15238             ]
15239         };
15240         
15241         if(this.calendarWeeks){
15242             dow.cn.push({
15243                 tag: 'th',
15244                 cls: 'cw',
15245                 html: '&nbsp;'
15246             })
15247         }
15248         
15249         while (dowCnt < this.weekStart + 7) {
15250             dow.cn.push({
15251                 tag: 'th',
15252                 cls: 'dow',
15253                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
15254             });
15255         }
15256         
15257         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
15258     },
15259     
15260     fillMonths: function()
15261     {    
15262         var i = 0;
15263         var months = this.picker().select('>.datepicker-months td', true).first();
15264         
15265         months.dom.innerHTML = '';
15266         
15267         while (i < 12) {
15268             var month = {
15269                 tag: 'span',
15270                 cls: 'month',
15271                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
15272             }
15273             
15274             months.createChild(month);
15275         }
15276         
15277     },
15278     
15279     update: function()
15280     {
15281         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;
15282         
15283         if (this.date < this.startDate) {
15284             this.viewDate = new Date(this.startDate);
15285         } else if (this.date > this.endDate) {
15286             this.viewDate = new Date(this.endDate);
15287         } else {
15288             this.viewDate = new Date(this.date);
15289         }
15290         
15291         this.fill();
15292     },
15293     
15294     fill: function() 
15295     {
15296         var d = new Date(this.viewDate),
15297                 year = d.getUTCFullYear(),
15298                 month = d.getUTCMonth(),
15299                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
15300                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
15301                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
15302                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
15303                 currentDate = this.date && this.date.valueOf(),
15304                 today = this.UTCToday();
15305         
15306         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
15307         
15308 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15309         
15310 //        this.picker.select('>tfoot th.today').
15311 //                                              .text(dates[this.language].today)
15312 //                                              .toggle(this.todayBtn !== false);
15313     
15314         this.updateNavArrows();
15315         this.fillMonths();
15316                                                 
15317         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
15318         
15319         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
15320          
15321         prevMonth.setUTCDate(day);
15322         
15323         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
15324         
15325         var nextMonth = new Date(prevMonth);
15326         
15327         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
15328         
15329         nextMonth = nextMonth.valueOf();
15330         
15331         var fillMonths = false;
15332         
15333         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
15334         
15335         while(prevMonth.valueOf() < nextMonth) {
15336             var clsName = '';
15337             
15338             if (prevMonth.getUTCDay() === this.weekStart) {
15339                 if(fillMonths){
15340                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
15341                 }
15342                     
15343                 fillMonths = {
15344                     tag: 'tr',
15345                     cn: []
15346                 };
15347                 
15348                 if(this.calendarWeeks){
15349                     // ISO 8601: First week contains first thursday.
15350                     // ISO also states week starts on Monday, but we can be more abstract here.
15351                     var
15352                     // Start of current week: based on weekstart/current date
15353                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
15354                     // Thursday of this week
15355                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
15356                     // First Thursday of year, year from thursday
15357                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
15358                     // Calendar week: ms between thursdays, div ms per day, div 7 days
15359                     calWeek =  (th - yth) / 864e5 / 7 + 1;
15360                     
15361                     fillMonths.cn.push({
15362                         tag: 'td',
15363                         cls: 'cw',
15364                         html: calWeek
15365                     });
15366                 }
15367             }
15368             
15369             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
15370                 clsName += ' old';
15371             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
15372                 clsName += ' new';
15373             }
15374             if (this.todayHighlight &&
15375                 prevMonth.getUTCFullYear() == today.getFullYear() &&
15376                 prevMonth.getUTCMonth() == today.getMonth() &&
15377                 prevMonth.getUTCDate() == today.getDate()) {
15378                 clsName += ' today';
15379             }
15380             
15381             if (currentDate && prevMonth.valueOf() === currentDate) {
15382                 clsName += ' active';
15383             }
15384             
15385             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
15386                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
15387                     clsName += ' disabled';
15388             }
15389             
15390             fillMonths.cn.push({
15391                 tag: 'td',
15392                 cls: 'day ' + clsName,
15393                 html: prevMonth.getDate()
15394             })
15395             
15396             prevMonth.setDate(prevMonth.getDate()+1);
15397         }
15398           
15399         var currentYear = this.date && this.date.getUTCFullYear();
15400         var currentMonth = this.date && this.date.getUTCMonth();
15401         
15402         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
15403         
15404         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
15405             v.removeClass('active');
15406             
15407             if(currentYear === year && k === currentMonth){
15408                 v.addClass('active');
15409             }
15410             
15411             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
15412                 v.addClass('disabled');
15413             }
15414             
15415         });
15416         
15417         
15418         year = parseInt(year/10, 10) * 10;
15419         
15420         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
15421         
15422         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
15423         
15424         year -= 1;
15425         for (var i = -1; i < 11; i++) {
15426             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
15427                 tag: 'span',
15428                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
15429                 html: year
15430             })
15431             
15432             year += 1;
15433         }
15434     },
15435     
15436     showMode: function(dir) 
15437     {
15438         if (dir) {
15439             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15440         }
15441         
15442         Roo.each(this.picker().select('>div',true).elements, function(v){
15443             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15444             v.hide();
15445         });
15446         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15447     },
15448     
15449     place: function()
15450     {
15451         if(this.isInline) return;
15452         
15453         this.picker().removeClass(['bottom', 'top']);
15454         
15455         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15456             /*
15457              * place to the top of element!
15458              *
15459              */
15460             
15461             this.picker().addClass('top');
15462             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15463             
15464             return;
15465         }
15466         
15467         this.picker().addClass('bottom');
15468         
15469         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15470     },
15471     
15472     parseDate : function(value)
15473     {
15474         if(!value || value instanceof Date){
15475             return value;
15476         }
15477         var v = Date.parseDate(value, this.format);
15478         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15479             v = Date.parseDate(value, 'Y-m-d');
15480         }
15481         if(!v && this.altFormats){
15482             if(!this.altFormatsArray){
15483                 this.altFormatsArray = this.altFormats.split("|");
15484             }
15485             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15486                 v = Date.parseDate(value, this.altFormatsArray[i]);
15487             }
15488         }
15489         return v;
15490     },
15491     
15492     formatDate : function(date, fmt)
15493     {   
15494         return (!date || !(date instanceof Date)) ?
15495         date : date.dateFormat(fmt || this.format);
15496     },
15497     
15498     onFocus : function()
15499     {
15500         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15501         this.show();
15502     },
15503     
15504     onBlur : function()
15505     {
15506         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15507         
15508         var d = this.inputEl().getValue();
15509         
15510         this.setValue(d);
15511                 
15512         this.hide();
15513     },
15514     
15515     show : function()
15516     {
15517         this.picker().show();
15518         this.update();
15519         this.place();
15520         
15521         this.fireEvent('show', this, this.date);
15522     },
15523     
15524     hide : function()
15525     {
15526         if(this.isInline) return;
15527         this.picker().hide();
15528         this.viewMode = this.startViewMode;
15529         this.showMode();
15530         
15531         this.fireEvent('hide', this, this.date);
15532         
15533     },
15534     
15535     onMousedown: function(e)
15536     {
15537         e.stopPropagation();
15538         e.preventDefault();
15539     },
15540     
15541     keyup: function(e)
15542     {
15543         Roo.bootstrap.DateField.superclass.keyup.call(this);
15544         this.update();
15545     },
15546
15547     setValue: function(v)
15548     {
15549         
15550         // v can be a string or a date..
15551         
15552         
15553         var d = new Date(this.parseDate(v) ).clearTime();
15554         
15555         if(isNaN(d.getTime())){
15556             this.date = this.viewDate = '';
15557             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15558             return;
15559         }
15560         
15561         v = this.formatDate(d);
15562         
15563         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15564         
15565         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15566      
15567         this.update();
15568
15569         this.fireEvent('select', this, this.date);
15570         
15571     },
15572     
15573     getValue: function()
15574     {
15575         return this.formatDate(this.date);
15576     },
15577     
15578     fireKey: function(e)
15579     {
15580         if (!this.picker().isVisible()){
15581             if (e.keyCode == 27) // allow escape to hide and re-show picker
15582                 this.show();
15583             return;
15584         }
15585         
15586         var dateChanged = false,
15587         dir, day, month,
15588         newDate, newViewDate;
15589         
15590         switch(e.keyCode){
15591             case 27: // escape
15592                 this.hide();
15593                 e.preventDefault();
15594                 break;
15595             case 37: // left
15596             case 39: // right
15597                 if (!this.keyboardNavigation) break;
15598                 dir = e.keyCode == 37 ? -1 : 1;
15599                 
15600                 if (e.ctrlKey){
15601                     newDate = this.moveYear(this.date, dir);
15602                     newViewDate = this.moveYear(this.viewDate, dir);
15603                 } else if (e.shiftKey){
15604                     newDate = this.moveMonth(this.date, dir);
15605                     newViewDate = this.moveMonth(this.viewDate, dir);
15606                 } else {
15607                     newDate = new Date(this.date);
15608                     newDate.setUTCDate(this.date.getUTCDate() + dir);
15609                     newViewDate = new Date(this.viewDate);
15610                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15611                 }
15612                 if (this.dateWithinRange(newDate)){
15613                     this.date = newDate;
15614                     this.viewDate = newViewDate;
15615                     this.setValue(this.formatDate(this.date));
15616 //                    this.update();
15617                     e.preventDefault();
15618                     dateChanged = true;
15619                 }
15620                 break;
15621             case 38: // up
15622             case 40: // down
15623                 if (!this.keyboardNavigation) break;
15624                 dir = e.keyCode == 38 ? -1 : 1;
15625                 if (e.ctrlKey){
15626                     newDate = this.moveYear(this.date, dir);
15627                     newViewDate = this.moveYear(this.viewDate, dir);
15628                 } else if (e.shiftKey){
15629                     newDate = this.moveMonth(this.date, dir);
15630                     newViewDate = this.moveMonth(this.viewDate, dir);
15631                 } else {
15632                     newDate = new Date(this.date);
15633                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15634                     newViewDate = new Date(this.viewDate);
15635                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15636                 }
15637                 if (this.dateWithinRange(newDate)){
15638                     this.date = newDate;
15639                     this.viewDate = newViewDate;
15640                     this.setValue(this.formatDate(this.date));
15641 //                    this.update();
15642                     e.preventDefault();
15643                     dateChanged = true;
15644                 }
15645                 break;
15646             case 13: // enter
15647                 this.setValue(this.formatDate(this.date));
15648                 this.hide();
15649                 e.preventDefault();
15650                 break;
15651             case 9: // tab
15652                 this.setValue(this.formatDate(this.date));
15653                 this.hide();
15654                 break;
15655             case 16: // shift
15656             case 17: // ctrl
15657             case 18: // alt
15658                 break;
15659             default :
15660                 this.hide();
15661                 
15662         }
15663     },
15664     
15665     
15666     onClick: function(e) 
15667     {
15668         e.stopPropagation();
15669         e.preventDefault();
15670         
15671         var target = e.getTarget();
15672         
15673         if(target.nodeName.toLowerCase() === 'i'){
15674             target = Roo.get(target).dom.parentNode;
15675         }
15676         
15677         var nodeName = target.nodeName;
15678         var className = target.className;
15679         var html = target.innerHTML;
15680         //Roo.log(nodeName);
15681         
15682         switch(nodeName.toLowerCase()) {
15683             case 'th':
15684                 switch(className) {
15685                     case 'switch':
15686                         this.showMode(1);
15687                         break;
15688                     case 'prev':
15689                     case 'next':
15690                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15691                         switch(this.viewMode){
15692                                 case 0:
15693                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15694                                         break;
15695                                 case 1:
15696                                 case 2:
15697                                         this.viewDate = this.moveYear(this.viewDate, dir);
15698                                         break;
15699                         }
15700                         this.fill();
15701                         break;
15702                     case 'today':
15703                         var date = new Date();
15704                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15705 //                        this.fill()
15706                         this.setValue(this.formatDate(this.date));
15707                         
15708                         this.hide();
15709                         break;
15710                 }
15711                 break;
15712             case 'span':
15713                 if (className.indexOf('disabled') < 0) {
15714                     this.viewDate.setUTCDate(1);
15715                     if (className.indexOf('month') > -1) {
15716                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15717                     } else {
15718                         var year = parseInt(html, 10) || 0;
15719                         this.viewDate.setUTCFullYear(year);
15720                         
15721                     }
15722                     
15723                     if(this.singleMode){
15724                         this.setValue(this.formatDate(this.viewDate));
15725                         this.hide();
15726                         return;
15727                     }
15728                     
15729                     this.showMode(-1);
15730                     this.fill();
15731                 }
15732                 break;
15733                 
15734             case 'td':
15735                 //Roo.log(className);
15736                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15737                     var day = parseInt(html, 10) || 1;
15738                     var year = this.viewDate.getUTCFullYear(),
15739                         month = this.viewDate.getUTCMonth();
15740
15741                     if (className.indexOf('old') > -1) {
15742                         if(month === 0 ){
15743                             month = 11;
15744                             year -= 1;
15745                         }else{
15746                             month -= 1;
15747                         }
15748                     } else if (className.indexOf('new') > -1) {
15749                         if (month == 11) {
15750                             month = 0;
15751                             year += 1;
15752                         } else {
15753                             month += 1;
15754                         }
15755                     }
15756                     //Roo.log([year,month,day]);
15757                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15758                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15759 //                    this.fill();
15760                     //Roo.log(this.formatDate(this.date));
15761                     this.setValue(this.formatDate(this.date));
15762                     this.hide();
15763                 }
15764                 break;
15765         }
15766     },
15767     
15768     setStartDate: function(startDate)
15769     {
15770         this.startDate = startDate || -Infinity;
15771         if (this.startDate !== -Infinity) {
15772             this.startDate = this.parseDate(this.startDate);
15773         }
15774         this.update();
15775         this.updateNavArrows();
15776     },
15777
15778     setEndDate: function(endDate)
15779     {
15780         this.endDate = endDate || Infinity;
15781         if (this.endDate !== Infinity) {
15782             this.endDate = this.parseDate(this.endDate);
15783         }
15784         this.update();
15785         this.updateNavArrows();
15786     },
15787     
15788     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15789     {
15790         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15791         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15792             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15793         }
15794         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15795             return parseInt(d, 10);
15796         });
15797         this.update();
15798         this.updateNavArrows();
15799     },
15800     
15801     updateNavArrows: function() 
15802     {
15803         if(this.singleMode){
15804             return;
15805         }
15806         
15807         var d = new Date(this.viewDate),
15808         year = d.getUTCFullYear(),
15809         month = d.getUTCMonth();
15810         
15811         Roo.each(this.picker().select('.prev', true).elements, function(v){
15812             v.show();
15813             switch (this.viewMode) {
15814                 case 0:
15815
15816                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15817                         v.hide();
15818                     }
15819                     break;
15820                 case 1:
15821                 case 2:
15822                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15823                         v.hide();
15824                     }
15825                     break;
15826             }
15827         });
15828         
15829         Roo.each(this.picker().select('.next', true).elements, function(v){
15830             v.show();
15831             switch (this.viewMode) {
15832                 case 0:
15833
15834                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15835                         v.hide();
15836                     }
15837                     break;
15838                 case 1:
15839                 case 2:
15840                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15841                         v.hide();
15842                     }
15843                     break;
15844             }
15845         })
15846     },
15847     
15848     moveMonth: function(date, dir)
15849     {
15850         if (!dir) return date;
15851         var new_date = new Date(date.valueOf()),
15852         day = new_date.getUTCDate(),
15853         month = new_date.getUTCMonth(),
15854         mag = Math.abs(dir),
15855         new_month, test;
15856         dir = dir > 0 ? 1 : -1;
15857         if (mag == 1){
15858             test = dir == -1
15859             // If going back one month, make sure month is not current month
15860             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15861             ? function(){
15862                 return new_date.getUTCMonth() == month;
15863             }
15864             // If going forward one month, make sure month is as expected
15865             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15866             : function(){
15867                 return new_date.getUTCMonth() != new_month;
15868             };
15869             new_month = month + dir;
15870             new_date.setUTCMonth(new_month);
15871             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15872             if (new_month < 0 || new_month > 11)
15873                 new_month = (new_month + 12) % 12;
15874         } else {
15875             // For magnitudes >1, move one month at a time...
15876             for (var i=0; i<mag; i++)
15877                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15878                 new_date = this.moveMonth(new_date, dir);
15879             // ...then reset the day, keeping it in the new month
15880             new_month = new_date.getUTCMonth();
15881             new_date.setUTCDate(day);
15882             test = function(){
15883                 return new_month != new_date.getUTCMonth();
15884             };
15885         }
15886         // Common date-resetting loop -- if date is beyond end of month, make it
15887         // end of month
15888         while (test()){
15889             new_date.setUTCDate(--day);
15890             new_date.setUTCMonth(new_month);
15891         }
15892         return new_date;
15893     },
15894
15895     moveYear: function(date, dir)
15896     {
15897         return this.moveMonth(date, dir*12);
15898     },
15899
15900     dateWithinRange: function(date)
15901     {
15902         return date >= this.startDate && date <= this.endDate;
15903     },
15904
15905     
15906     remove: function() 
15907     {
15908         this.picker().remove();
15909     }
15910    
15911 });
15912
15913 Roo.apply(Roo.bootstrap.DateField,  {
15914     
15915     head : {
15916         tag: 'thead',
15917         cn: [
15918         {
15919             tag: 'tr',
15920             cn: [
15921             {
15922                 tag: 'th',
15923                 cls: 'prev',
15924                 html: '<i class="fa fa-arrow-left"/>'
15925             },
15926             {
15927                 tag: 'th',
15928                 cls: 'switch',
15929                 colspan: '5'
15930             },
15931             {
15932                 tag: 'th',
15933                 cls: 'next',
15934                 html: '<i class="fa fa-arrow-right"/>'
15935             }
15936
15937             ]
15938         }
15939         ]
15940     },
15941     
15942     content : {
15943         tag: 'tbody',
15944         cn: [
15945         {
15946             tag: 'tr',
15947             cn: [
15948             {
15949                 tag: 'td',
15950                 colspan: '7'
15951             }
15952             ]
15953         }
15954         ]
15955     },
15956     
15957     footer : {
15958         tag: 'tfoot',
15959         cn: [
15960         {
15961             tag: 'tr',
15962             cn: [
15963             {
15964                 tag: 'th',
15965                 colspan: '7',
15966                 cls: 'today'
15967             }
15968                     
15969             ]
15970         }
15971         ]
15972     },
15973     
15974     dates:{
15975         en: {
15976             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15977             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15978             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15979             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15980             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15981             today: "Today"
15982         }
15983     },
15984     
15985     modes: [
15986     {
15987         clsName: 'days',
15988         navFnc: 'Month',
15989         navStep: 1
15990     },
15991     {
15992         clsName: 'months',
15993         navFnc: 'FullYear',
15994         navStep: 1
15995     },
15996     {
15997         clsName: 'years',
15998         navFnc: 'FullYear',
15999         navStep: 10
16000     }]
16001 });
16002
16003 Roo.apply(Roo.bootstrap.DateField,  {
16004   
16005     template : {
16006         tag: 'div',
16007         cls: 'datepicker dropdown-menu roo-dynamic',
16008         cn: [
16009         {
16010             tag: 'div',
16011             cls: 'datepicker-days',
16012             cn: [
16013             {
16014                 tag: 'table',
16015                 cls: 'table-condensed',
16016                 cn:[
16017                 Roo.bootstrap.DateField.head,
16018                 {
16019                     tag: 'tbody'
16020                 },
16021                 Roo.bootstrap.DateField.footer
16022                 ]
16023             }
16024             ]
16025         },
16026         {
16027             tag: 'div',
16028             cls: 'datepicker-months',
16029             cn: [
16030             {
16031                 tag: 'table',
16032                 cls: 'table-condensed',
16033                 cn:[
16034                 Roo.bootstrap.DateField.head,
16035                 Roo.bootstrap.DateField.content,
16036                 Roo.bootstrap.DateField.footer
16037                 ]
16038             }
16039             ]
16040         },
16041         {
16042             tag: 'div',
16043             cls: 'datepicker-years',
16044             cn: [
16045             {
16046                 tag: 'table',
16047                 cls: 'table-condensed',
16048                 cn:[
16049                 Roo.bootstrap.DateField.head,
16050                 Roo.bootstrap.DateField.content,
16051                 Roo.bootstrap.DateField.footer
16052                 ]
16053             }
16054             ]
16055         }
16056         ]
16057     }
16058 });
16059
16060  
16061
16062  /*
16063  * - LGPL
16064  *
16065  * TimeField
16066  * 
16067  */
16068
16069 /**
16070  * @class Roo.bootstrap.TimeField
16071  * @extends Roo.bootstrap.Input
16072  * Bootstrap DateField class
16073  * 
16074  * 
16075  * @constructor
16076  * Create a new TimeField
16077  * @param {Object} config The config object
16078  */
16079
16080 Roo.bootstrap.TimeField = function(config){
16081     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
16082     this.addEvents({
16083             /**
16084              * @event show
16085              * Fires when this field show.
16086              * @param {Roo.bootstrap.DateField} thisthis
16087              * @param {Mixed} date The date value
16088              */
16089             show : true,
16090             /**
16091              * @event show
16092              * Fires when this field hide.
16093              * @param {Roo.bootstrap.DateField} this
16094              * @param {Mixed} date The date value
16095              */
16096             hide : true,
16097             /**
16098              * @event select
16099              * Fires when select a date.
16100              * @param {Roo.bootstrap.DateField} this
16101              * @param {Mixed} date The date value
16102              */
16103             select : true
16104         });
16105 };
16106
16107 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
16108     
16109     /**
16110      * @cfg {String} format
16111      * The default time format string which can be overriden for localization support.  The format must be
16112      * valid according to {@link Date#parseDate} (defaults to 'H:i').
16113      */
16114     format : "H:i",
16115        
16116     onRender: function(ct, position)
16117     {
16118         
16119         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
16120                 
16121         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
16122         
16123         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16124         
16125         this.pop = this.picker().select('>.datepicker-time',true).first();
16126         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16127         
16128         this.picker().on('mousedown', this.onMousedown, this);
16129         this.picker().on('click', this.onClick, this);
16130         
16131         this.picker().addClass('datepicker-dropdown');
16132     
16133         this.fillTime();
16134         this.update();
16135             
16136         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
16137         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
16138         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
16139         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
16140         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
16141         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
16142
16143     },
16144     
16145     fireKey: function(e){
16146         if (!this.picker().isVisible()){
16147             if (e.keyCode == 27) { // allow escape to hide and re-show picker
16148                 this.show();
16149             }
16150             return;
16151         }
16152
16153         e.preventDefault();
16154         
16155         switch(e.keyCode){
16156             case 27: // escape
16157                 this.hide();
16158                 break;
16159             case 37: // left
16160             case 39: // right
16161                 this.onTogglePeriod();
16162                 break;
16163             case 38: // up
16164                 this.onIncrementMinutes();
16165                 break;
16166             case 40: // down
16167                 this.onDecrementMinutes();
16168                 break;
16169             case 13: // enter
16170             case 9: // tab
16171                 this.setTime();
16172                 break;
16173         }
16174     },
16175     
16176     onClick: function(e) {
16177         e.stopPropagation();
16178         e.preventDefault();
16179     },
16180     
16181     picker : function()
16182     {
16183         return this.el.select('.datepicker', true).first();
16184     },
16185     
16186     fillTime: function()
16187     {    
16188         var time = this.pop.select('tbody', true).first();
16189         
16190         time.dom.innerHTML = '';
16191         
16192         time.createChild({
16193             tag: 'tr',
16194             cn: [
16195                 {
16196                     tag: 'td',
16197                     cn: [
16198                         {
16199                             tag: 'a',
16200                             href: '#',
16201                             cls: 'btn',
16202                             cn: [
16203                                 {
16204                                     tag: 'span',
16205                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
16206                                 }
16207                             ]
16208                         } 
16209                     ]
16210                 },
16211                 {
16212                     tag: 'td',
16213                     cls: 'separator'
16214                 },
16215                 {
16216                     tag: 'td',
16217                     cn: [
16218                         {
16219                             tag: 'a',
16220                             href: '#',
16221                             cls: 'btn',
16222                             cn: [
16223                                 {
16224                                     tag: 'span',
16225                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
16226                                 }
16227                             ]
16228                         }
16229                     ]
16230                 },
16231                 {
16232                     tag: 'td',
16233                     cls: 'separator'
16234                 }
16235             ]
16236         });
16237         
16238         time.createChild({
16239             tag: 'tr',
16240             cn: [
16241                 {
16242                     tag: 'td',
16243                     cn: [
16244                         {
16245                             tag: 'span',
16246                             cls: 'timepicker-hour',
16247                             html: '00'
16248                         }  
16249                     ]
16250                 },
16251                 {
16252                     tag: 'td',
16253                     cls: 'separator',
16254                     html: ':'
16255                 },
16256                 {
16257                     tag: 'td',
16258                     cn: [
16259                         {
16260                             tag: 'span',
16261                             cls: 'timepicker-minute',
16262                             html: '00'
16263                         }  
16264                     ]
16265                 },
16266                 {
16267                     tag: 'td',
16268                     cls: 'separator'
16269                 },
16270                 {
16271                     tag: 'td',
16272                     cn: [
16273                         {
16274                             tag: 'button',
16275                             type: 'button',
16276                             cls: 'btn btn-primary period',
16277                             html: 'AM'
16278                             
16279                         }
16280                     ]
16281                 }
16282             ]
16283         });
16284         
16285         time.createChild({
16286             tag: 'tr',
16287             cn: [
16288                 {
16289                     tag: 'td',
16290                     cn: [
16291                         {
16292                             tag: 'a',
16293                             href: '#',
16294                             cls: 'btn',
16295                             cn: [
16296                                 {
16297                                     tag: 'span',
16298                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
16299                                 }
16300                             ]
16301                         }
16302                     ]
16303                 },
16304                 {
16305                     tag: 'td',
16306                     cls: 'separator'
16307                 },
16308                 {
16309                     tag: 'td',
16310                     cn: [
16311                         {
16312                             tag: 'a',
16313                             href: '#',
16314                             cls: 'btn',
16315                             cn: [
16316                                 {
16317                                     tag: 'span',
16318                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
16319                                 }
16320                             ]
16321                         }
16322                     ]
16323                 },
16324                 {
16325                     tag: 'td',
16326                     cls: 'separator'
16327                 }
16328             ]
16329         });
16330         
16331     },
16332     
16333     update: function()
16334     {
16335         
16336         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
16337         
16338         this.fill();
16339     },
16340     
16341     fill: function() 
16342     {
16343         var hours = this.time.getHours();
16344         var minutes = this.time.getMinutes();
16345         var period = 'AM';
16346         
16347         if(hours > 11){
16348             period = 'PM';
16349         }
16350         
16351         if(hours == 0){
16352             hours = 12;
16353         }
16354         
16355         
16356         if(hours > 12){
16357             hours = hours - 12;
16358         }
16359         
16360         if(hours < 10){
16361             hours = '0' + hours;
16362         }
16363         
16364         if(minutes < 10){
16365             minutes = '0' + minutes;
16366         }
16367         
16368         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
16369         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
16370         this.pop.select('button', true).first().dom.innerHTML = period;
16371         
16372     },
16373     
16374     place: function()
16375     {   
16376         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
16377         
16378         var cls = ['bottom'];
16379         
16380         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
16381             cls.pop();
16382             cls.push('top');
16383         }
16384         
16385         cls.push('right');
16386         
16387         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
16388             cls.pop();
16389             cls.push('left');
16390         }
16391         
16392         this.picker().addClass(cls.join('-'));
16393         
16394         var _this = this;
16395         
16396         Roo.each(cls, function(c){
16397             if(c == 'bottom'){
16398                 _this.picker().setTop(_this.inputEl().getHeight());
16399                 return;
16400             }
16401             if(c == 'top'){
16402                 _this.picker().setTop(0 - _this.picker().getHeight());
16403                 return;
16404             }
16405             
16406             if(c == 'left'){
16407                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
16408                 return;
16409             }
16410             if(c == 'right'){
16411                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
16412                 return;
16413             }
16414         });
16415         
16416     },
16417   
16418     onFocus : function()
16419     {
16420         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
16421         this.show();
16422     },
16423     
16424     onBlur : function()
16425     {
16426         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
16427         this.hide();
16428     },
16429     
16430     show : function()
16431     {
16432         this.picker().show();
16433         this.pop.show();
16434         this.update();
16435         this.place();
16436         
16437         this.fireEvent('show', this, this.date);
16438     },
16439     
16440     hide : function()
16441     {
16442         this.picker().hide();
16443         this.pop.hide();
16444         
16445         this.fireEvent('hide', this, this.date);
16446     },
16447     
16448     setTime : function()
16449     {
16450         this.hide();
16451         this.setValue(this.time.format(this.format));
16452         
16453         this.fireEvent('select', this, this.date);
16454         
16455         
16456     },
16457     
16458     onMousedown: function(e){
16459         e.stopPropagation();
16460         e.preventDefault();
16461     },
16462     
16463     onIncrementHours: function()
16464     {
16465         Roo.log('onIncrementHours');
16466         this.time = this.time.add(Date.HOUR, 1);
16467         this.update();
16468         
16469     },
16470     
16471     onDecrementHours: function()
16472     {
16473         Roo.log('onDecrementHours');
16474         this.time = this.time.add(Date.HOUR, -1);
16475         this.update();
16476     },
16477     
16478     onIncrementMinutes: function()
16479     {
16480         Roo.log('onIncrementMinutes');
16481         this.time = this.time.add(Date.MINUTE, 1);
16482         this.update();
16483     },
16484     
16485     onDecrementMinutes: function()
16486     {
16487         Roo.log('onDecrementMinutes');
16488         this.time = this.time.add(Date.MINUTE, -1);
16489         this.update();
16490     },
16491     
16492     onTogglePeriod: function()
16493     {
16494         Roo.log('onTogglePeriod');
16495         this.time = this.time.add(Date.HOUR, 12);
16496         this.update();
16497     }
16498     
16499    
16500 });
16501
16502 Roo.apply(Roo.bootstrap.TimeField,  {
16503     
16504     content : {
16505         tag: 'tbody',
16506         cn: [
16507             {
16508                 tag: 'tr',
16509                 cn: [
16510                 {
16511                     tag: 'td',
16512                     colspan: '7'
16513                 }
16514                 ]
16515             }
16516         ]
16517     },
16518     
16519     footer : {
16520         tag: 'tfoot',
16521         cn: [
16522             {
16523                 tag: 'tr',
16524                 cn: [
16525                 {
16526                     tag: 'th',
16527                     colspan: '7',
16528                     cls: '',
16529                     cn: [
16530                         {
16531                             tag: 'button',
16532                             cls: 'btn btn-info ok',
16533                             html: 'OK'
16534                         }
16535                     ]
16536                 }
16537
16538                 ]
16539             }
16540         ]
16541     }
16542 });
16543
16544 Roo.apply(Roo.bootstrap.TimeField,  {
16545   
16546     template : {
16547         tag: 'div',
16548         cls: 'datepicker dropdown-menu',
16549         cn: [
16550             {
16551                 tag: 'div',
16552                 cls: 'datepicker-time',
16553                 cn: [
16554                 {
16555                     tag: 'table',
16556                     cls: 'table-condensed',
16557                     cn:[
16558                     Roo.bootstrap.TimeField.content,
16559                     Roo.bootstrap.TimeField.footer
16560                     ]
16561                 }
16562                 ]
16563             }
16564         ]
16565     }
16566 });
16567
16568  
16569
16570  /*
16571  * - LGPL
16572  *
16573  * MonthField
16574  * 
16575  */
16576
16577 /**
16578  * @class Roo.bootstrap.MonthField
16579  * @extends Roo.bootstrap.Input
16580  * Bootstrap MonthField class
16581  * 
16582  * @cfg {String} language default en
16583  * 
16584  * @constructor
16585  * Create a new MonthField
16586  * @param {Object} config The config object
16587  */
16588
16589 Roo.bootstrap.MonthField = function(config){
16590     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
16591     
16592     this.addEvents({
16593         /**
16594          * @event show
16595          * Fires when this field show.
16596          * @param {Roo.bootstrap.MonthField} this
16597          * @param {Mixed} date The date value
16598          */
16599         show : true,
16600         /**
16601          * @event show
16602          * Fires when this field hide.
16603          * @param {Roo.bootstrap.MonthField} this
16604          * @param {Mixed} date The date value
16605          */
16606         hide : true,
16607         /**
16608          * @event select
16609          * Fires when select a date.
16610          * @param {Roo.bootstrap.MonthField} this
16611          * @param {String} oldvalue The old value
16612          * @param {String} newvalue The new value
16613          */
16614         select : true
16615     });
16616 };
16617
16618 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
16619     
16620     onRender: function(ct, position)
16621     {
16622         
16623         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
16624         
16625         this.language = this.language || 'en';
16626         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
16627         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
16628         
16629         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
16630         this.isInline = false;
16631         this.isInput = true;
16632         this.component = this.el.select('.add-on', true).first() || false;
16633         this.component = (this.component && this.component.length === 0) ? false : this.component;
16634         this.hasInput = this.component && this.inputEL().length;
16635         
16636         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
16637         
16638         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16639         
16640         this.picker().on('mousedown', this.onMousedown, this);
16641         this.picker().on('click', this.onClick, this);
16642         
16643         this.picker().addClass('datepicker-dropdown');
16644         
16645         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16646             v.setStyle('width', '189px');
16647         });
16648         
16649         this.fillMonths();
16650         
16651         this.update();
16652         
16653         if(this.isInline) {
16654             this.show();
16655         }
16656         
16657     },
16658     
16659     setValue: function(v, suppressEvent)
16660     {   
16661         var o = this.getValue();
16662         
16663         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
16664         
16665         this.update();
16666
16667         if(suppressEvent !== true){
16668             this.fireEvent('select', this, o, v);
16669         }
16670         
16671     },
16672     
16673     getValue: function()
16674     {
16675         return this.value;
16676     },
16677     
16678     onClick: function(e) 
16679     {
16680         e.stopPropagation();
16681         e.preventDefault();
16682         
16683         var target = e.getTarget();
16684         
16685         if(target.nodeName.toLowerCase() === 'i'){
16686             target = Roo.get(target).dom.parentNode;
16687         }
16688         
16689         var nodeName = target.nodeName;
16690         var className = target.className;
16691         var html = target.innerHTML;
16692         
16693         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
16694             return;
16695         }
16696         
16697         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
16698         
16699         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16700         
16701         this.hide();
16702                         
16703     },
16704     
16705     picker : function()
16706     {
16707         return this.pickerEl;
16708     },
16709     
16710     fillMonths: function()
16711     {    
16712         var i = 0;
16713         var months = this.picker().select('>.datepicker-months td', true).first();
16714         
16715         months.dom.innerHTML = '';
16716         
16717         while (i < 12) {
16718             var month = {
16719                 tag: 'span',
16720                 cls: 'month',
16721                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
16722             }
16723             
16724             months.createChild(month);
16725         }
16726         
16727     },
16728     
16729     update: function()
16730     {
16731         var _this = this;
16732         
16733         if(typeof(this.vIndex) == 'undefined' && this.value.length){
16734             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
16735         }
16736         
16737         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
16738             e.removeClass('active');
16739             
16740             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
16741                 e.addClass('active');
16742             }
16743         })
16744     },
16745     
16746     place: function()
16747     {
16748         if(this.isInline) return;
16749         
16750         this.picker().removeClass(['bottom', 'top']);
16751         
16752         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16753             /*
16754              * place to the top of element!
16755              *
16756              */
16757             
16758             this.picker().addClass('top');
16759             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16760             
16761             return;
16762         }
16763         
16764         this.picker().addClass('bottom');
16765         
16766         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16767     },
16768     
16769     onFocus : function()
16770     {
16771         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
16772         this.show();
16773     },
16774     
16775     onBlur : function()
16776     {
16777         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
16778         
16779         var d = this.inputEl().getValue();
16780         
16781         this.setValue(d);
16782                 
16783         this.hide();
16784     },
16785     
16786     show : function()
16787     {
16788         this.picker().show();
16789         this.picker().select('>.datepicker-months', true).first().show();
16790         this.update();
16791         this.place();
16792         
16793         this.fireEvent('show', this, this.date);
16794     },
16795     
16796     hide : function()
16797     {
16798         if(this.isInline) return;
16799         this.picker().hide();
16800         this.fireEvent('hide', this, this.date);
16801         
16802     },
16803     
16804     onMousedown: function(e)
16805     {
16806         e.stopPropagation();
16807         e.preventDefault();
16808     },
16809     
16810     keyup: function(e)
16811     {
16812         Roo.bootstrap.MonthField.superclass.keyup.call(this);
16813         this.update();
16814     },
16815
16816     fireKey: function(e)
16817     {
16818         if (!this.picker().isVisible()){
16819             if (e.keyCode == 27) // allow escape to hide and re-show picker
16820                 this.show();
16821             return;
16822         }
16823         
16824         var dir;
16825         
16826         switch(e.keyCode){
16827             case 27: // escape
16828                 this.hide();
16829                 e.preventDefault();
16830                 break;
16831             case 37: // left
16832             case 39: // right
16833                 dir = e.keyCode == 37 ? -1 : 1;
16834                 
16835                 this.vIndex = this.vIndex + dir;
16836                 
16837                 if(this.vIndex < 0){
16838                     this.vIndex = 0;
16839                 }
16840                 
16841                 if(this.vIndex > 11){
16842                     this.vIndex = 11;
16843                 }
16844                 
16845                 if(isNaN(this.vIndex)){
16846                     this.vIndex = 0;
16847                 }
16848                 
16849                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16850                 
16851                 break;
16852             case 38: // up
16853             case 40: // down
16854                 
16855                 dir = e.keyCode == 38 ? -1 : 1;
16856                 
16857                 this.vIndex = this.vIndex + dir * 4;
16858                 
16859                 if(this.vIndex < 0){
16860                     this.vIndex = 0;
16861                 }
16862                 
16863                 if(this.vIndex > 11){
16864                     this.vIndex = 11;
16865                 }
16866                 
16867                 if(isNaN(this.vIndex)){
16868                     this.vIndex = 0;
16869                 }
16870                 
16871                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16872                 break;
16873                 
16874             case 13: // enter
16875                 
16876                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16877                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16878                 }
16879                 
16880                 this.hide();
16881                 e.preventDefault();
16882                 break;
16883             case 9: // tab
16884                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16885                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16886                 }
16887                 this.hide();
16888                 break;
16889             case 16: // shift
16890             case 17: // ctrl
16891             case 18: // alt
16892                 break;
16893             default :
16894                 this.hide();
16895                 
16896         }
16897     },
16898     
16899     remove: function() 
16900     {
16901         this.picker().remove();
16902     }
16903    
16904 });
16905
16906 Roo.apply(Roo.bootstrap.MonthField,  {
16907     
16908     content : {
16909         tag: 'tbody',
16910         cn: [
16911         {
16912             tag: 'tr',
16913             cn: [
16914             {
16915                 tag: 'td',
16916                 colspan: '7'
16917             }
16918             ]
16919         }
16920         ]
16921     },
16922     
16923     dates:{
16924         en: {
16925             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16926             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
16927         }
16928     }
16929 });
16930
16931 Roo.apply(Roo.bootstrap.MonthField,  {
16932   
16933     template : {
16934         tag: 'div',
16935         cls: 'datepicker dropdown-menu roo-dynamic',
16936         cn: [
16937             {
16938                 tag: 'div',
16939                 cls: 'datepicker-months',
16940                 cn: [
16941                 {
16942                     tag: 'table',
16943                     cls: 'table-condensed',
16944                     cn:[
16945                         Roo.bootstrap.DateField.content
16946                     ]
16947                 }
16948                 ]
16949             }
16950         ]
16951     }
16952 });
16953
16954  
16955
16956  
16957  /*
16958  * - LGPL
16959  *
16960  * CheckBox
16961  * 
16962  */
16963
16964 /**
16965  * @class Roo.bootstrap.CheckBox
16966  * @extends Roo.bootstrap.Input
16967  * Bootstrap CheckBox class
16968  * 
16969  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
16970  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
16971  * @cfg {String} boxLabel The text that appears beside the checkbox
16972  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
16973  * @cfg {Boolean} checked initnal the element
16974  * @cfg {Boolean} inline inline the element (default false)
16975  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
16976  * 
16977  * @constructor
16978  * Create a new CheckBox
16979  * @param {Object} config The config object
16980  */
16981
16982 Roo.bootstrap.CheckBox = function(config){
16983     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
16984    
16985     this.addEvents({
16986         /**
16987         * @event check
16988         * Fires when the element is checked or unchecked.
16989         * @param {Roo.bootstrap.CheckBox} this This input
16990         * @param {Boolean} checked The new checked value
16991         */
16992        check : true
16993     });
16994     
16995 };
16996
16997 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
16998   
16999     inputType: 'checkbox',
17000     inputValue: 1,
17001     valueOff: 0,
17002     boxLabel: false,
17003     checked: false,
17004     weight : false,
17005     inline: false,
17006     
17007     getAutoCreate : function()
17008     {
17009         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17010         
17011         var id = Roo.id();
17012         
17013         var cfg = {};
17014         
17015         cfg.cls = 'form-group ' + this.inputType; //input-group
17016         
17017         if(this.inline){
17018             cfg.cls += ' ' + this.inputType + '-inline';
17019         }
17020         
17021         var input =  {
17022             tag: 'input',
17023             id : id,
17024             type : this.inputType,
17025             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
17026             cls : 'roo-' + this.inputType, //'form-box',
17027             placeholder : this.placeholder || ''
17028             
17029         };
17030         
17031         if (this.weight) { // Validity check?
17032             cfg.cls += " " + this.inputType + "-" + this.weight;
17033         }
17034         
17035         if (this.disabled) {
17036             input.disabled=true;
17037         }
17038         
17039         if(this.checked){
17040             input.checked = this.checked;
17041         }
17042         
17043         if (this.name) {
17044             input.name = this.name;
17045         }
17046         
17047         if (this.size) {
17048             input.cls += ' input-' + this.size;
17049         }
17050         
17051         var settings=this;
17052         
17053         ['xs','sm','md','lg'].map(function(size){
17054             if (settings[size]) {
17055                 cfg.cls += ' col-' + size + '-' + settings[size];
17056             }
17057         });
17058         
17059         var inputblock = input;
17060          
17061         if (this.before || this.after) {
17062             
17063             inputblock = {
17064                 cls : 'input-group',
17065                 cn :  [] 
17066             };
17067             
17068             if (this.before) {
17069                 inputblock.cn.push({
17070                     tag :'span',
17071                     cls : 'input-group-addon',
17072                     html : this.before
17073                 });
17074             }
17075             
17076             inputblock.cn.push(input);
17077             
17078             if (this.after) {
17079                 inputblock.cn.push({
17080                     tag :'span',
17081                     cls : 'input-group-addon',
17082                     html : this.after
17083                 });
17084             }
17085             
17086         }
17087         
17088         if (align ==='left' && this.fieldLabel.length) {
17089                 Roo.log("left and has label");
17090                 cfg.cn = [
17091                     
17092                     {
17093                         tag: 'label',
17094                         'for' :  id,
17095                         cls : 'control-label col-md-' + this.labelWidth,
17096                         html : this.fieldLabel
17097                         
17098                     },
17099                     {
17100                         cls : "col-md-" + (12 - this.labelWidth), 
17101                         cn: [
17102                             inputblock
17103                         ]
17104                     }
17105                     
17106                 ];
17107         } else if ( this.fieldLabel.length) {
17108                 Roo.log(" label");
17109                 cfg.cn = [
17110                    
17111                     {
17112                         tag: this.boxLabel ? 'span' : 'label',
17113                         'for': id,
17114                         cls: 'control-label box-input-label',
17115                         //cls : 'input-group-addon',
17116                         html : this.fieldLabel
17117                         
17118                     },
17119                     
17120                     inputblock
17121                     
17122                 ];
17123
17124         } else {
17125             
17126                 Roo.log(" no label && no align");
17127                 cfg.cn = [  inputblock ] ;
17128                 
17129                 
17130         }
17131         if(this.boxLabel){
17132              var boxLabelCfg = {
17133                 tag: 'label',
17134                 //'for': id, // box label is handled by onclick - so no for...
17135                 cls: 'box-label',
17136                 html: this.boxLabel
17137             }
17138             
17139             if(this.tooltip){
17140                 boxLabelCfg.tooltip = this.tooltip;
17141             }
17142              
17143             cfg.cn.push(boxLabelCfg);
17144         }
17145         
17146         
17147        
17148         return cfg;
17149         
17150     },
17151     
17152     /**
17153      * return the real input element.
17154      */
17155     inputEl: function ()
17156     {
17157         return this.el.select('input.roo-' + this.inputType,true).first();
17158     },
17159     
17160     labelEl: function()
17161     {
17162         return this.el.select('label.control-label',true).first();
17163     },
17164     /* depricated... */
17165     
17166     label: function()
17167     {
17168         return this.labelEl();
17169     },
17170     
17171     initEvents : function()
17172     {
17173 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17174         
17175         this.inputEl().on('click', this.onClick,  this);
17176         
17177         if (this.boxLabel) { 
17178             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
17179         }
17180         
17181         this.startValue = this.getValue();
17182         
17183         if(this.groupId){
17184             Roo.bootstrap.CheckBox.register(this);
17185         }
17186     },
17187     
17188     onClick : function()
17189     {   
17190         this.setChecked(!this.checked);
17191     },
17192     
17193     setChecked : function(state,suppressEvent)
17194     {
17195         this.startValue = this.getValue();
17196         
17197         if(this.inputType == 'radio'){
17198             
17199             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17200                 e.dom.checked = false;
17201             });
17202             
17203             this.inputEl().dom.checked = true;
17204             
17205             this.inputEl().dom.value = this.inputValue;
17206             
17207             if(suppressEvent !== true){
17208                 this.fireEvent('check', this, true);
17209             }
17210             
17211             this.validate();
17212             
17213             return;
17214         }
17215         
17216         this.checked = state;
17217         
17218         this.inputEl().dom.checked = state;
17219         
17220         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17221         
17222         if(suppressEvent !== true){
17223             this.fireEvent('check', this, state);
17224         }
17225         
17226         this.validate();
17227     },
17228     
17229     getValue : function()
17230     {
17231         if(this.inputType == 'radio'){
17232             return this.getGroupValue();
17233         }
17234         
17235         return this.inputEl().getValue();
17236         
17237     },
17238     
17239     getGroupValue : function()
17240     {
17241         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
17242             return '';
17243         }
17244         
17245         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
17246     },
17247     
17248     setValue : function(v,suppressEvent)
17249     {
17250         if(this.inputType == 'radio'){
17251             this.setGroupValue(v, suppressEvent);
17252             return;
17253         }
17254         
17255         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
17256         
17257         this.validate();
17258     },
17259     
17260     setGroupValue : function(v, suppressEvent)
17261     {
17262         this.startValue = this.getValue();
17263         
17264         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17265             e.dom.checked = false;
17266             
17267             if(e.dom.value == v){
17268                 e.dom.checked = true;
17269             }
17270         });
17271         
17272         if(suppressEvent !== true){
17273             this.fireEvent('check', this, true);
17274         }
17275
17276         this.validate();
17277         
17278         return;
17279     },
17280     
17281     validate : function()
17282     {
17283         if(
17284                 this.disabled || 
17285                 (this.inputType == 'radio' && this.validateRadio()) ||
17286                 (this.inputType == 'checkbox' && this.validateCheckbox())
17287         ){
17288             this.markValid();
17289             return true;
17290         }
17291         
17292         this.markInvalid();
17293         return false;
17294     },
17295     
17296     validateRadio : function()
17297     {
17298         var valid = false;
17299         
17300         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17301             if(!e.dom.checked){
17302                 return;
17303             }
17304             
17305             valid = true;
17306             
17307             return false;
17308         });
17309         
17310         return valid;
17311     },
17312     
17313     validateCheckbox : function()
17314     {
17315         if(!this.groupId){
17316             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
17317         }
17318         
17319         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17320         
17321         if(!group){
17322             return false;
17323         }
17324         
17325         var r = false;
17326         
17327         for(var i in group){
17328             if(r){
17329                 break;
17330             }
17331             
17332             r = (group[i].getValue() == group[i].inputValue) ? true : false;
17333         }
17334         
17335         return r;
17336     },
17337     
17338     /**
17339      * Mark this field as valid
17340      */
17341     markValid : function()
17342     {
17343         if(this.allowBlank){
17344             return;
17345         }
17346         
17347         var _this = this;
17348         
17349         this.fireEvent('valid', this);
17350         
17351         if(this.inputType == 'radio'){
17352             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17353                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17354                 e.findParent('.form-group', false, true).addClass(_this.validClass);
17355             });
17356             
17357             return;
17358         }
17359         
17360         if(!this.groupId){
17361             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17362             this.el.findParent('.form-group', false, true).addClass(this.validClass);
17363             return;
17364         }
17365         
17366         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17367             
17368         if(!group){
17369             return;
17370         }
17371         
17372         for(var i in group){
17373             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17374             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
17375         }
17376     },
17377     
17378      /**
17379      * Mark this field as invalid
17380      * @param {String} msg The validation message
17381      */
17382     markInvalid : function(msg)
17383     {
17384         if(this.allowBlank){
17385             return;
17386         }
17387         
17388         var _this = this;
17389         
17390         this.fireEvent('invalid', this, msg);
17391         
17392         if(this.inputType == 'radio'){
17393             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17394                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17395                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
17396             });
17397             
17398             return;
17399         }
17400         
17401         if(!this.groupId){
17402             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17403             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
17404             return;
17405         }
17406         
17407         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17408             
17409         if(!group){
17410             return;
17411         }
17412         
17413         for(var i in group){
17414             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17415             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
17416         }
17417         
17418     }
17419     
17420 });
17421
17422 Roo.apply(Roo.bootstrap.CheckBox, {
17423     
17424     groups: {},
17425     
17426      /**
17427     * register a CheckBox Group
17428     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
17429     */
17430     register : function(checkbox)
17431     {
17432         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
17433             this.groups[checkbox.groupId] = {};
17434         }
17435         
17436         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
17437             return;
17438         }
17439         
17440         this.groups[checkbox.groupId][checkbox.name] = checkbox;
17441         
17442     },
17443     /**
17444     * fetch a CheckBox Group based on the group ID
17445     * @param {string} the group ID
17446     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
17447     */
17448     get: function(groupId) {
17449         if (typeof(this.groups[groupId]) == 'undefined') {
17450             return false;
17451         }
17452         
17453         return this.groups[groupId] ;
17454     }
17455     
17456     
17457 });
17458 /*
17459  * - LGPL
17460  *
17461  * Radio
17462  *
17463  *
17464  * not inline
17465  *<div class="radio">
17466   <label>
17467     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
17468     Option one is this and that&mdash;be sure to include why it's great
17469   </label>
17470 </div>
17471  *
17472  *
17473  *inline
17474  *<span>
17475  *<label class="radio-inline">fieldLabel</label>
17476  *<label class="radio-inline">
17477   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
17478 </label>
17479 <span>
17480  * 
17481  * 
17482  */
17483
17484 /**
17485  * @class Roo.bootstrap.Radio
17486  * @extends Roo.bootstrap.CheckBox
17487  * Bootstrap Radio class
17488
17489  * @constructor
17490  * Create a new Radio
17491  * @param {Object} config The config object
17492  */
17493
17494 Roo.bootstrap.Radio = function(config){
17495     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
17496    
17497 };
17498
17499 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
17500     
17501     inputType: 'radio',
17502     inputValue: '',
17503     valueOff: '',
17504     
17505     getAutoCreate : function()
17506     {
17507         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17508         align = align || 'left'; // default...
17509         
17510         
17511         
17512         var id = Roo.id();
17513         
17514         var cfg = {
17515                 tag : this.inline ? 'span' : 'div',
17516                 cls : '',
17517                 cn : []
17518         };
17519         
17520         var inline = this.inline ? ' radio-inline' : '';
17521         
17522         var lbl = {
17523                 tag: 'label' ,
17524                 // does not need for, as we wrap the input with it..
17525                 'for' : id,
17526                 cls : 'control-label box-label' + inline,
17527                 cn : []
17528         };
17529         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
17530         
17531         var fieldLabel = {
17532             tag: 'label' ,
17533             //cls : 'control-label' + inline,
17534             html : this.fieldLabel,
17535             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
17536         };
17537         
17538  
17539         
17540         
17541         var input =  {
17542             tag: 'input',
17543             id : id,
17544             type : this.inputType,
17545             //value : (!this.checked) ? this.valueOff : this.inputValue,
17546             value : this.inputValue,
17547             cls : 'roo-radio',
17548             placeholder : this.placeholder || '' // ?? needed????
17549             
17550         };
17551         if (this.weight) { // Validity check?
17552             input.cls += " radio-" + this.weight;
17553         }
17554         if (this.disabled) {
17555             input.disabled=true;
17556         }
17557         
17558         if(this.checked){
17559             input.checked = this.checked;
17560         }
17561         
17562         if (this.name) {
17563             input.name = this.name;
17564         }
17565         
17566         if (this.size) {
17567             input.cls += ' input-' + this.size;
17568         }
17569         
17570         //?? can span's inline have a width??
17571         
17572         var settings=this;
17573         ['xs','sm','md','lg'].map(function(size){
17574             if (settings[size]) {
17575                 cfg.cls += ' col-' + size + '-' + settings[size];
17576             }
17577         });
17578         
17579         var inputblock = input;
17580         
17581         if (this.before || this.after) {
17582             
17583             inputblock = {
17584                 cls : 'input-group',
17585                 tag : 'span',
17586                 cn :  [] 
17587             };
17588             if (this.before) {
17589                 inputblock.cn.push({
17590                     tag :'span',
17591                     cls : 'input-group-addon',
17592                     html : this.before
17593                 });
17594             }
17595             inputblock.cn.push(input);
17596             if (this.after) {
17597                 inputblock.cn.push({
17598                     tag :'span',
17599                     cls : 'input-group-addon',
17600                     html : this.after
17601                 });
17602             }
17603             
17604         };
17605         
17606         
17607         if (this.fieldLabel && this.fieldLabel.length) {
17608             cfg.cn.push(fieldLabel);
17609         }
17610        
17611         // normal bootstrap puts the input inside the label.
17612         // however with our styled version - it has to go after the input.
17613        
17614         //lbl.cn.push(inputblock);
17615         
17616         var lblwrap =  {
17617             tag: 'span',
17618             cls: 'radio' + inline,
17619             cn: [
17620                 inputblock,
17621                 lbl
17622             ]
17623         };
17624         
17625         cfg.cn.push( lblwrap);
17626         
17627         if(this.boxLabel){
17628             lbl.cn.push({
17629                 tag: 'span',
17630                 html: this.boxLabel
17631             })
17632         }
17633          
17634         
17635         return cfg;
17636         
17637     },
17638     
17639     initEvents : function()
17640     {
17641 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17642         
17643         this.inputEl().on('click', this.onClick,  this);
17644         if (this.boxLabel) {
17645             Roo.log('find label')
17646             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
17647         }
17648         
17649     },
17650     
17651     inputEl: function ()
17652     {
17653         return this.el.select('input.roo-radio',true).first();
17654     },
17655     onClick : function()
17656     {   
17657         Roo.log("click");
17658         this.setChecked(true);
17659     },
17660     
17661     setChecked : function(state,suppressEvent)
17662     {
17663         if(state){
17664             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17665                 v.dom.checked = false;
17666             });
17667         }
17668         Roo.log(this.inputEl().dom);
17669         this.checked = state;
17670         this.inputEl().dom.checked = state;
17671         
17672         if(suppressEvent !== true){
17673             this.fireEvent('check', this, state);
17674         }
17675         
17676         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17677         
17678     },
17679     
17680     getGroupValue : function()
17681     {
17682         var value = '';
17683         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17684             if(v.dom.checked == true){
17685                 value = v.dom.value;
17686             }
17687         });
17688         
17689         return value;
17690     },
17691     
17692     /**
17693      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
17694      * @return {Mixed} value The field value
17695      */
17696     getValue : function(){
17697         return this.getGroupValue();
17698     }
17699     
17700 });
17701
17702  
17703 //<script type="text/javascript">
17704
17705 /*
17706  * Based  Ext JS Library 1.1.1
17707  * Copyright(c) 2006-2007, Ext JS, LLC.
17708  * LGPL
17709  *
17710  */
17711  
17712 /**
17713  * @class Roo.HtmlEditorCore
17714  * @extends Roo.Component
17715  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
17716  *
17717  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
17718  */
17719
17720 Roo.HtmlEditorCore = function(config){
17721     
17722     
17723     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
17724     
17725     
17726     this.addEvents({
17727         /**
17728          * @event initialize
17729          * Fires when the editor is fully initialized (including the iframe)
17730          * @param {Roo.HtmlEditorCore} this
17731          */
17732         initialize: true,
17733         /**
17734          * @event activate
17735          * Fires when the editor is first receives the focus. Any insertion must wait
17736          * until after this event.
17737          * @param {Roo.HtmlEditorCore} this
17738          */
17739         activate: true,
17740          /**
17741          * @event beforesync
17742          * Fires before the textarea is updated with content from the editor iframe. Return false
17743          * to cancel the sync.
17744          * @param {Roo.HtmlEditorCore} this
17745          * @param {String} html
17746          */
17747         beforesync: true,
17748          /**
17749          * @event beforepush
17750          * Fires before the iframe editor is updated with content from the textarea. Return false
17751          * to cancel the push.
17752          * @param {Roo.HtmlEditorCore} this
17753          * @param {String} html
17754          */
17755         beforepush: true,
17756          /**
17757          * @event sync
17758          * Fires when the textarea is updated with content from the editor iframe.
17759          * @param {Roo.HtmlEditorCore} this
17760          * @param {String} html
17761          */
17762         sync: true,
17763          /**
17764          * @event push
17765          * Fires when the iframe editor is updated with content from the textarea.
17766          * @param {Roo.HtmlEditorCore} this
17767          * @param {String} html
17768          */
17769         push: true,
17770         
17771         /**
17772          * @event editorevent
17773          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17774          * @param {Roo.HtmlEditorCore} this
17775          */
17776         editorevent: true
17777         
17778     });
17779     
17780     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
17781     
17782     // defaults : white / black...
17783     this.applyBlacklists();
17784     
17785     
17786     
17787 };
17788
17789
17790 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
17791
17792
17793      /**
17794      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
17795      */
17796     
17797     owner : false,
17798     
17799      /**
17800      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17801      *                        Roo.resizable.
17802      */
17803     resizable : false,
17804      /**
17805      * @cfg {Number} height (in pixels)
17806      */   
17807     height: 300,
17808    /**
17809      * @cfg {Number} width (in pixels)
17810      */   
17811     width: 500,
17812     
17813     /**
17814      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17815      * 
17816      */
17817     stylesheets: false,
17818     
17819     // id of frame..
17820     frameId: false,
17821     
17822     // private properties
17823     validationEvent : false,
17824     deferHeight: true,
17825     initialized : false,
17826     activated : false,
17827     sourceEditMode : false,
17828     onFocus : Roo.emptyFn,
17829     iframePad:3,
17830     hideMode:'offsets',
17831     
17832     clearUp: true,
17833     
17834     // blacklist + whitelisted elements..
17835     black: false,
17836     white: false,
17837      
17838     
17839
17840     /**
17841      * Protected method that will not generally be called directly. It
17842      * is called when the editor initializes the iframe with HTML contents. Override this method if you
17843      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
17844      */
17845     getDocMarkup : function(){
17846         // body styles..
17847         var st = '';
17848         
17849         // inherit styels from page...?? 
17850         if (this.stylesheets === false) {
17851             
17852             Roo.get(document.head).select('style').each(function(node) {
17853                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17854             });
17855             
17856             Roo.get(document.head).select('link').each(function(node) { 
17857                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17858             });
17859             
17860         } else if (!this.stylesheets.length) {
17861                 // simple..
17862                 st = '<style type="text/css">' +
17863                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17864                    '</style>';
17865         } else { 
17866             
17867         }
17868         
17869         st +=  '<style type="text/css">' +
17870             'IMG { cursor: pointer } ' +
17871         '</style>';
17872
17873         
17874         return '<html><head>' + st  +
17875             //<style type="text/css">' +
17876             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17877             //'</style>' +
17878             ' </head><body class="roo-htmleditor-body"></body></html>';
17879     },
17880
17881     // private
17882     onRender : function(ct, position)
17883     {
17884         var _t = this;
17885         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
17886         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
17887         
17888         
17889         this.el.dom.style.border = '0 none';
17890         this.el.dom.setAttribute('tabIndex', -1);
17891         this.el.addClass('x-hidden hide');
17892         
17893         
17894         
17895         if(Roo.isIE){ // fix IE 1px bogus margin
17896             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
17897         }
17898        
17899         
17900         this.frameId = Roo.id();
17901         
17902          
17903         
17904         var iframe = this.owner.wrap.createChild({
17905             tag: 'iframe',
17906             cls: 'form-control', // bootstrap..
17907             id: this.frameId,
17908             name: this.frameId,
17909             frameBorder : 'no',
17910             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
17911         }, this.el
17912         );
17913         
17914         
17915         this.iframe = iframe.dom;
17916
17917          this.assignDocWin();
17918         
17919         this.doc.designMode = 'on';
17920        
17921         this.doc.open();
17922         this.doc.write(this.getDocMarkup());
17923         this.doc.close();
17924
17925         
17926         var task = { // must defer to wait for browser to be ready
17927             run : function(){
17928                 //console.log("run task?" + this.doc.readyState);
17929                 this.assignDocWin();
17930                 if(this.doc.body || this.doc.readyState == 'complete'){
17931                     try {
17932                         this.doc.designMode="on";
17933                     } catch (e) {
17934                         return;
17935                     }
17936                     Roo.TaskMgr.stop(task);
17937                     this.initEditor.defer(10, this);
17938                 }
17939             },
17940             interval : 10,
17941             duration: 10000,
17942             scope: this
17943         };
17944         Roo.TaskMgr.start(task);
17945
17946     },
17947
17948     // private
17949     onResize : function(w, h)
17950     {
17951          Roo.log('resize: ' +w + ',' + h );
17952         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
17953         if(!this.iframe){
17954             return;
17955         }
17956         if(typeof w == 'number'){
17957             
17958             this.iframe.style.width = w + 'px';
17959         }
17960         if(typeof h == 'number'){
17961             
17962             this.iframe.style.height = h + 'px';
17963             if(this.doc){
17964                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
17965             }
17966         }
17967         
17968     },
17969
17970     /**
17971      * Toggles the editor between standard and source edit mode.
17972      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17973      */
17974     toggleSourceEdit : function(sourceEditMode){
17975         
17976         this.sourceEditMode = sourceEditMode === true;
17977         
17978         if(this.sourceEditMode){
17979  
17980             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
17981             
17982         }else{
17983             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
17984             //this.iframe.className = '';
17985             this.deferFocus();
17986         }
17987         //this.setSize(this.owner.wrap.getSize());
17988         //this.fireEvent('editmodechange', this, this.sourceEditMode);
17989     },
17990
17991     
17992   
17993
17994     /**
17995      * Protected method that will not generally be called directly. If you need/want
17996      * custom HTML cleanup, this is the method you should override.
17997      * @param {String} html The HTML to be cleaned
17998      * return {String} The cleaned HTML
17999      */
18000     cleanHtml : function(html){
18001         html = String(html);
18002         if(html.length > 5){
18003             if(Roo.isSafari){ // strip safari nonsense
18004                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
18005             }
18006         }
18007         if(html == '&nbsp;'){
18008             html = '';
18009         }
18010         return html;
18011     },
18012
18013     /**
18014      * HTML Editor -> Textarea
18015      * Protected method that will not generally be called directly. Syncs the contents
18016      * of the editor iframe with the textarea.
18017      */
18018     syncValue : function(){
18019         if(this.initialized){
18020             var bd = (this.doc.body || this.doc.documentElement);
18021             //this.cleanUpPaste(); -- this is done else where and causes havoc..
18022             var html = bd.innerHTML;
18023             if(Roo.isSafari){
18024                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
18025                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
18026                 if(m && m[1]){
18027                     html = '<div style="'+m[0]+'">' + html + '</div>';
18028                 }
18029             }
18030             html = this.cleanHtml(html);
18031             // fix up the special chars.. normaly like back quotes in word...
18032             // however we do not want to do this with chinese..
18033             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
18034                 var cc = b.charCodeAt();
18035                 if (
18036                     (cc >= 0x4E00 && cc < 0xA000 ) ||
18037                     (cc >= 0x3400 && cc < 0x4E00 ) ||
18038                     (cc >= 0xf900 && cc < 0xfb00 )
18039                 ) {
18040                         return b;
18041                 }
18042                 return "&#"+cc+";" 
18043             });
18044             if(this.owner.fireEvent('beforesync', this, html) !== false){
18045                 this.el.dom.value = html;
18046                 this.owner.fireEvent('sync', this, html);
18047             }
18048         }
18049     },
18050
18051     /**
18052      * Protected method that will not generally be called directly. Pushes the value of the textarea
18053      * into the iframe editor.
18054      */
18055     pushValue : function(){
18056         if(this.initialized){
18057             var v = this.el.dom.value.trim();
18058             
18059 //            if(v.length < 1){
18060 //                v = '&#160;';
18061 //            }
18062             
18063             if(this.owner.fireEvent('beforepush', this, v) !== false){
18064                 var d = (this.doc.body || this.doc.documentElement);
18065                 d.innerHTML = v;
18066                 this.cleanUpPaste();
18067                 this.el.dom.value = d.innerHTML;
18068                 this.owner.fireEvent('push', this, v);
18069             }
18070         }
18071     },
18072
18073     // private
18074     deferFocus : function(){
18075         this.focus.defer(10, this);
18076     },
18077
18078     // doc'ed in Field
18079     focus : function(){
18080         if(this.win && !this.sourceEditMode){
18081             this.win.focus();
18082         }else{
18083             this.el.focus();
18084         }
18085     },
18086     
18087     assignDocWin: function()
18088     {
18089         var iframe = this.iframe;
18090         
18091          if(Roo.isIE){
18092             this.doc = iframe.contentWindow.document;
18093             this.win = iframe.contentWindow;
18094         } else {
18095 //            if (!Roo.get(this.frameId)) {
18096 //                return;
18097 //            }
18098 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18099 //            this.win = Roo.get(this.frameId).dom.contentWindow;
18100             
18101             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
18102                 return;
18103             }
18104             
18105             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18106             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
18107         }
18108     },
18109     
18110     // private
18111     initEditor : function(){
18112         //console.log("INIT EDITOR");
18113         this.assignDocWin();
18114         
18115         
18116         
18117         this.doc.designMode="on";
18118         this.doc.open();
18119         this.doc.write(this.getDocMarkup());
18120         this.doc.close();
18121         
18122         var dbody = (this.doc.body || this.doc.documentElement);
18123         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
18124         // this copies styles from the containing element into thsi one..
18125         // not sure why we need all of this..
18126         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
18127         
18128         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
18129         //ss['background-attachment'] = 'fixed'; // w3c
18130         dbody.bgProperties = 'fixed'; // ie
18131         //Roo.DomHelper.applyStyles(dbody, ss);
18132         Roo.EventManager.on(this.doc, {
18133             //'mousedown': this.onEditorEvent,
18134             'mouseup': this.onEditorEvent,
18135             'dblclick': this.onEditorEvent,
18136             'click': this.onEditorEvent,
18137             'keyup': this.onEditorEvent,
18138             buffer:100,
18139             scope: this
18140         });
18141         if(Roo.isGecko){
18142             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
18143         }
18144         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
18145             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
18146         }
18147         this.initialized = true;
18148
18149         this.owner.fireEvent('initialize', this);
18150         this.pushValue();
18151     },
18152
18153     // private
18154     onDestroy : function(){
18155         
18156         
18157         
18158         if(this.rendered){
18159             
18160             //for (var i =0; i < this.toolbars.length;i++) {
18161             //    // fixme - ask toolbars for heights?
18162             //    this.toolbars[i].onDestroy();
18163            // }
18164             
18165             //this.wrap.dom.innerHTML = '';
18166             //this.wrap.remove();
18167         }
18168     },
18169
18170     // private
18171     onFirstFocus : function(){
18172         
18173         this.assignDocWin();
18174         
18175         
18176         this.activated = true;
18177          
18178     
18179         if(Roo.isGecko){ // prevent silly gecko errors
18180             this.win.focus();
18181             var s = this.win.getSelection();
18182             if(!s.focusNode || s.focusNode.nodeType != 3){
18183                 var r = s.getRangeAt(0);
18184                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
18185                 r.collapse(true);
18186                 this.deferFocus();
18187             }
18188             try{
18189                 this.execCmd('useCSS', true);
18190                 this.execCmd('styleWithCSS', false);
18191             }catch(e){}
18192         }
18193         this.owner.fireEvent('activate', this);
18194     },
18195
18196     // private
18197     adjustFont: function(btn){
18198         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
18199         //if(Roo.isSafari){ // safari
18200         //    adjust *= 2;
18201        // }
18202         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
18203         if(Roo.isSafari){ // safari
18204             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
18205             v =  (v < 10) ? 10 : v;
18206             v =  (v > 48) ? 48 : v;
18207             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
18208             
18209         }
18210         
18211         
18212         v = Math.max(1, v+adjust);
18213         
18214         this.execCmd('FontSize', v  );
18215     },
18216
18217     onEditorEvent : function(e){
18218         this.owner.fireEvent('editorevent', this, e);
18219       //  this.updateToolbar();
18220         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
18221     },
18222
18223     insertTag : function(tg)
18224     {
18225         // could be a bit smarter... -> wrap the current selected tRoo..
18226         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
18227             
18228             range = this.createRange(this.getSelection());
18229             var wrappingNode = this.doc.createElement(tg.toLowerCase());
18230             wrappingNode.appendChild(range.extractContents());
18231             range.insertNode(wrappingNode);
18232
18233             return;
18234             
18235             
18236             
18237         }
18238         this.execCmd("formatblock",   tg);
18239         
18240     },
18241     
18242     insertText : function(txt)
18243     {
18244         
18245         
18246         var range = this.createRange();
18247         range.deleteContents();
18248                //alert(Sender.getAttribute('label'));
18249                
18250         range.insertNode(this.doc.createTextNode(txt));
18251     } ,
18252     
18253      
18254
18255     /**
18256      * Executes a Midas editor command on the editor document and performs necessary focus and
18257      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
18258      * @param {String} cmd The Midas command
18259      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18260      */
18261     relayCmd : function(cmd, value){
18262         this.win.focus();
18263         this.execCmd(cmd, value);
18264         this.owner.fireEvent('editorevent', this);
18265         //this.updateToolbar();
18266         this.owner.deferFocus();
18267     },
18268
18269     /**
18270      * Executes a Midas editor command directly on the editor document.
18271      * For visual commands, you should use {@link #relayCmd} instead.
18272      * <b>This should only be called after the editor is initialized.</b>
18273      * @param {String} cmd The Midas command
18274      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18275      */
18276     execCmd : function(cmd, value){
18277         this.doc.execCommand(cmd, false, value === undefined ? null : value);
18278         this.syncValue();
18279     },
18280  
18281  
18282    
18283     /**
18284      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
18285      * to insert tRoo.
18286      * @param {String} text | dom node.. 
18287      */
18288     insertAtCursor : function(text)
18289     {
18290         
18291         
18292         
18293         if(!this.activated){
18294             return;
18295         }
18296         /*
18297         if(Roo.isIE){
18298             this.win.focus();
18299             var r = this.doc.selection.createRange();
18300             if(r){
18301                 r.collapse(true);
18302                 r.pasteHTML(text);
18303                 this.syncValue();
18304                 this.deferFocus();
18305             
18306             }
18307             return;
18308         }
18309         */
18310         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
18311             this.win.focus();
18312             
18313             
18314             // from jquery ui (MIT licenced)
18315             var range, node;
18316             var win = this.win;
18317             
18318             if (win.getSelection && win.getSelection().getRangeAt) {
18319                 range = win.getSelection().getRangeAt(0);
18320                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
18321                 range.insertNode(node);
18322             } else if (win.document.selection && win.document.selection.createRange) {
18323                 // no firefox support
18324                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18325                 win.document.selection.createRange().pasteHTML(txt);
18326             } else {
18327                 // no firefox support
18328                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18329                 this.execCmd('InsertHTML', txt);
18330             } 
18331             
18332             this.syncValue();
18333             
18334             this.deferFocus();
18335         }
18336     },
18337  // private
18338     mozKeyPress : function(e){
18339         if(e.ctrlKey){
18340             var c = e.getCharCode(), cmd;
18341           
18342             if(c > 0){
18343                 c = String.fromCharCode(c).toLowerCase();
18344                 switch(c){
18345                     case 'b':
18346                         cmd = 'bold';
18347                         break;
18348                     case 'i':
18349                         cmd = 'italic';
18350                         break;
18351                     
18352                     case 'u':
18353                         cmd = 'underline';
18354                         break;
18355                     
18356                     case 'v':
18357                         this.cleanUpPaste.defer(100, this);
18358                         return;
18359                         
18360                 }
18361                 if(cmd){
18362                     this.win.focus();
18363                     this.execCmd(cmd);
18364                     this.deferFocus();
18365                     e.preventDefault();
18366                 }
18367                 
18368             }
18369         }
18370     },
18371
18372     // private
18373     fixKeys : function(){ // load time branching for fastest keydown performance
18374         if(Roo.isIE){
18375             return function(e){
18376                 var k = e.getKey(), r;
18377                 if(k == e.TAB){
18378                     e.stopEvent();
18379                     r = this.doc.selection.createRange();
18380                     if(r){
18381                         r.collapse(true);
18382                         r.pasteHTML('&#160;&#160;&#160;&#160;');
18383                         this.deferFocus();
18384                     }
18385                     return;
18386                 }
18387                 
18388                 if(k == e.ENTER){
18389                     r = this.doc.selection.createRange();
18390                     if(r){
18391                         var target = r.parentElement();
18392                         if(!target || target.tagName.toLowerCase() != 'li'){
18393                             e.stopEvent();
18394                             r.pasteHTML('<br />');
18395                             r.collapse(false);
18396                             r.select();
18397                         }
18398                     }
18399                 }
18400                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18401                     this.cleanUpPaste.defer(100, this);
18402                     return;
18403                 }
18404                 
18405                 
18406             };
18407         }else if(Roo.isOpera){
18408             return function(e){
18409                 var k = e.getKey();
18410                 if(k == e.TAB){
18411                     e.stopEvent();
18412                     this.win.focus();
18413                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
18414                     this.deferFocus();
18415                 }
18416                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18417                     this.cleanUpPaste.defer(100, this);
18418                     return;
18419                 }
18420                 
18421             };
18422         }else if(Roo.isSafari){
18423             return function(e){
18424                 var k = e.getKey();
18425                 
18426                 if(k == e.TAB){
18427                     e.stopEvent();
18428                     this.execCmd('InsertText','\t');
18429                     this.deferFocus();
18430                     return;
18431                 }
18432                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18433                     this.cleanUpPaste.defer(100, this);
18434                     return;
18435                 }
18436                 
18437              };
18438         }
18439     }(),
18440     
18441     getAllAncestors: function()
18442     {
18443         var p = this.getSelectedNode();
18444         var a = [];
18445         if (!p) {
18446             a.push(p); // push blank onto stack..
18447             p = this.getParentElement();
18448         }
18449         
18450         
18451         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
18452             a.push(p);
18453             p = p.parentNode;
18454         }
18455         a.push(this.doc.body);
18456         return a;
18457     },
18458     lastSel : false,
18459     lastSelNode : false,
18460     
18461     
18462     getSelection : function() 
18463     {
18464         this.assignDocWin();
18465         return Roo.isIE ? this.doc.selection : this.win.getSelection();
18466     },
18467     
18468     getSelectedNode: function() 
18469     {
18470         // this may only work on Gecko!!!
18471         
18472         // should we cache this!!!!
18473         
18474         
18475         
18476          
18477         var range = this.createRange(this.getSelection()).cloneRange();
18478         
18479         if (Roo.isIE) {
18480             var parent = range.parentElement();
18481             while (true) {
18482                 var testRange = range.duplicate();
18483                 testRange.moveToElementText(parent);
18484                 if (testRange.inRange(range)) {
18485                     break;
18486                 }
18487                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
18488                     break;
18489                 }
18490                 parent = parent.parentElement;
18491             }
18492             return parent;
18493         }
18494         
18495         // is ancestor a text element.
18496         var ac =  range.commonAncestorContainer;
18497         if (ac.nodeType == 3) {
18498             ac = ac.parentNode;
18499         }
18500         
18501         var ar = ac.childNodes;
18502          
18503         var nodes = [];
18504         var other_nodes = [];
18505         var has_other_nodes = false;
18506         for (var i=0;i<ar.length;i++) {
18507             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
18508                 continue;
18509             }
18510             // fullly contained node.
18511             
18512             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
18513                 nodes.push(ar[i]);
18514                 continue;
18515             }
18516             
18517             // probably selected..
18518             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
18519                 other_nodes.push(ar[i]);
18520                 continue;
18521             }
18522             // outer..
18523             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
18524                 continue;
18525             }
18526             
18527             
18528             has_other_nodes = true;
18529         }
18530         if (!nodes.length && other_nodes.length) {
18531             nodes= other_nodes;
18532         }
18533         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
18534             return false;
18535         }
18536         
18537         return nodes[0];
18538     },
18539     createRange: function(sel)
18540     {
18541         // this has strange effects when using with 
18542         // top toolbar - not sure if it's a great idea.
18543         //this.editor.contentWindow.focus();
18544         if (typeof sel != "undefined") {
18545             try {
18546                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
18547             } catch(e) {
18548                 return this.doc.createRange();
18549             }
18550         } else {
18551             return this.doc.createRange();
18552         }
18553     },
18554     getParentElement: function()
18555     {
18556         
18557         this.assignDocWin();
18558         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
18559         
18560         var range = this.createRange(sel);
18561          
18562         try {
18563             var p = range.commonAncestorContainer;
18564             while (p.nodeType == 3) { // text node
18565                 p = p.parentNode;
18566             }
18567             return p;
18568         } catch (e) {
18569             return null;
18570         }
18571     
18572     },
18573     /***
18574      *
18575      * Range intersection.. the hard stuff...
18576      *  '-1' = before
18577      *  '0' = hits..
18578      *  '1' = after.
18579      *         [ -- selected range --- ]
18580      *   [fail]                        [fail]
18581      *
18582      *    basically..
18583      *      if end is before start or  hits it. fail.
18584      *      if start is after end or hits it fail.
18585      *
18586      *   if either hits (but other is outside. - then it's not 
18587      *   
18588      *    
18589      **/
18590     
18591     
18592     // @see http://www.thismuchiknow.co.uk/?p=64.
18593     rangeIntersectsNode : function(range, node)
18594     {
18595         var nodeRange = node.ownerDocument.createRange();
18596         try {
18597             nodeRange.selectNode(node);
18598         } catch (e) {
18599             nodeRange.selectNodeContents(node);
18600         }
18601     
18602         var rangeStartRange = range.cloneRange();
18603         rangeStartRange.collapse(true);
18604     
18605         var rangeEndRange = range.cloneRange();
18606         rangeEndRange.collapse(false);
18607     
18608         var nodeStartRange = nodeRange.cloneRange();
18609         nodeStartRange.collapse(true);
18610     
18611         var nodeEndRange = nodeRange.cloneRange();
18612         nodeEndRange.collapse(false);
18613     
18614         return rangeStartRange.compareBoundaryPoints(
18615                  Range.START_TO_START, nodeEndRange) == -1 &&
18616                rangeEndRange.compareBoundaryPoints(
18617                  Range.START_TO_START, nodeStartRange) == 1;
18618         
18619          
18620     },
18621     rangeCompareNode : function(range, node)
18622     {
18623         var nodeRange = node.ownerDocument.createRange();
18624         try {
18625             nodeRange.selectNode(node);
18626         } catch (e) {
18627             nodeRange.selectNodeContents(node);
18628         }
18629         
18630         
18631         range.collapse(true);
18632     
18633         nodeRange.collapse(true);
18634      
18635         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
18636         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
18637          
18638         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
18639         
18640         var nodeIsBefore   =  ss == 1;
18641         var nodeIsAfter    = ee == -1;
18642         
18643         if (nodeIsBefore && nodeIsAfter)
18644             return 0; // outer
18645         if (!nodeIsBefore && nodeIsAfter)
18646             return 1; //right trailed.
18647         
18648         if (nodeIsBefore && !nodeIsAfter)
18649             return 2;  // left trailed.
18650         // fully contined.
18651         return 3;
18652     },
18653
18654     // private? - in a new class?
18655     cleanUpPaste :  function()
18656     {
18657         // cleans up the whole document..
18658         Roo.log('cleanuppaste');
18659         
18660         this.cleanUpChildren(this.doc.body);
18661         var clean = this.cleanWordChars(this.doc.body.innerHTML);
18662         if (clean != this.doc.body.innerHTML) {
18663             this.doc.body.innerHTML = clean;
18664         }
18665         
18666     },
18667     
18668     cleanWordChars : function(input) {// change the chars to hex code
18669         var he = Roo.HtmlEditorCore;
18670         
18671         var output = input;
18672         Roo.each(he.swapCodes, function(sw) { 
18673             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
18674             
18675             output = output.replace(swapper, sw[1]);
18676         });
18677         
18678         return output;
18679     },
18680     
18681     
18682     cleanUpChildren : function (n)
18683     {
18684         if (!n.childNodes.length) {
18685             return;
18686         }
18687         for (var i = n.childNodes.length-1; i > -1 ; i--) {
18688            this.cleanUpChild(n.childNodes[i]);
18689         }
18690     },
18691     
18692     
18693         
18694     
18695     cleanUpChild : function (node)
18696     {
18697         var ed = this;
18698         //console.log(node);
18699         if (node.nodeName == "#text") {
18700             // clean up silly Windows -- stuff?
18701             return; 
18702         }
18703         if (node.nodeName == "#comment") {
18704             node.parentNode.removeChild(node);
18705             // clean up silly Windows -- stuff?
18706             return; 
18707         }
18708         var lcname = node.tagName.toLowerCase();
18709         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
18710         // whitelist of tags..
18711         
18712         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
18713             // remove node.
18714             node.parentNode.removeChild(node);
18715             return;
18716             
18717         }
18718         
18719         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
18720         
18721         // remove <a name=....> as rendering on yahoo mailer is borked with this.
18722         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
18723         
18724         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
18725         //    remove_keep_children = true;
18726         //}
18727         
18728         if (remove_keep_children) {
18729             this.cleanUpChildren(node);
18730             // inserts everything just before this node...
18731             while (node.childNodes.length) {
18732                 var cn = node.childNodes[0];
18733                 node.removeChild(cn);
18734                 node.parentNode.insertBefore(cn, node);
18735             }
18736             node.parentNode.removeChild(node);
18737             return;
18738         }
18739         
18740         if (!node.attributes || !node.attributes.length) {
18741             this.cleanUpChildren(node);
18742             return;
18743         }
18744         
18745         function cleanAttr(n,v)
18746         {
18747             
18748             if (v.match(/^\./) || v.match(/^\//)) {
18749                 return;
18750             }
18751             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
18752                 return;
18753             }
18754             if (v.match(/^#/)) {
18755                 return;
18756             }
18757 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
18758             node.removeAttribute(n);
18759             
18760         }
18761         
18762         var cwhite = this.cwhite;
18763         var cblack = this.cblack;
18764             
18765         function cleanStyle(n,v)
18766         {
18767             if (v.match(/expression/)) { //XSS?? should we even bother..
18768                 node.removeAttribute(n);
18769                 return;
18770             }
18771             
18772             var parts = v.split(/;/);
18773             var clean = [];
18774             
18775             Roo.each(parts, function(p) {
18776                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
18777                 if (!p.length) {
18778                     return true;
18779                 }
18780                 var l = p.split(':').shift().replace(/\s+/g,'');
18781                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
18782                 
18783                 if ( cwhite.length && cblack.indexOf(l) > -1) {
18784 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18785                     //node.removeAttribute(n);
18786                     return true;
18787                 }
18788                 //Roo.log()
18789                 // only allow 'c whitelisted system attributes'
18790                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
18791 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18792                     //node.removeAttribute(n);
18793                     return true;
18794                 }
18795                 
18796                 
18797                  
18798                 
18799                 clean.push(p);
18800                 return true;
18801             });
18802             if (clean.length) { 
18803                 node.setAttribute(n, clean.join(';'));
18804             } else {
18805                 node.removeAttribute(n);
18806             }
18807             
18808         }
18809         
18810         
18811         for (var i = node.attributes.length-1; i > -1 ; i--) {
18812             var a = node.attributes[i];
18813             //console.log(a);
18814             
18815             if (a.name.toLowerCase().substr(0,2)=='on')  {
18816                 node.removeAttribute(a.name);
18817                 continue;
18818             }
18819             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
18820                 node.removeAttribute(a.name);
18821                 continue;
18822             }
18823             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
18824                 cleanAttr(a.name,a.value); // fixme..
18825                 continue;
18826             }
18827             if (a.name == 'style') {
18828                 cleanStyle(a.name,a.value);
18829                 continue;
18830             }
18831             /// clean up MS crap..
18832             // tecnically this should be a list of valid class'es..
18833             
18834             
18835             if (a.name == 'class') {
18836                 if (a.value.match(/^Mso/)) {
18837                     node.className = '';
18838                 }
18839                 
18840                 if (a.value.match(/body/)) {
18841                     node.className = '';
18842                 }
18843                 continue;
18844             }
18845             
18846             // style cleanup!?
18847             // class cleanup?
18848             
18849         }
18850         
18851         
18852         this.cleanUpChildren(node);
18853         
18854         
18855     },
18856     /**
18857      * Clean up MS wordisms...
18858      */
18859     cleanWord : function(node)
18860     {
18861         var _t = this;
18862         var cleanWordChildren = function()
18863         {
18864             if (!node.childNodes.length) {
18865                 return;
18866             }
18867             for (var i = node.childNodes.length-1; i > -1 ; i--) {
18868                _t.cleanWord(node.childNodes[i]);
18869             }
18870         }
18871         
18872         
18873         if (!node) {
18874             this.cleanWord(this.doc.body);
18875             return;
18876         }
18877         if (node.nodeName == "#text") {
18878             // clean up silly Windows -- stuff?
18879             return; 
18880         }
18881         if (node.nodeName == "#comment") {
18882             node.parentNode.removeChild(node);
18883             // clean up silly Windows -- stuff?
18884             return; 
18885         }
18886         
18887         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
18888             node.parentNode.removeChild(node);
18889             return;
18890         }
18891         
18892         // remove - but keep children..
18893         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
18894             while (node.childNodes.length) {
18895                 var cn = node.childNodes[0];
18896                 node.removeChild(cn);
18897                 node.parentNode.insertBefore(cn, node);
18898             }
18899             node.parentNode.removeChild(node);
18900             cleanWordChildren();
18901             return;
18902         }
18903         // clean styles
18904         if (node.className.length) {
18905             
18906             var cn = node.className.split(/\W+/);
18907             var cna = [];
18908             Roo.each(cn, function(cls) {
18909                 if (cls.match(/Mso[a-zA-Z]+/)) {
18910                     return;
18911                 }
18912                 cna.push(cls);
18913             });
18914             node.className = cna.length ? cna.join(' ') : '';
18915             if (!cna.length) {
18916                 node.removeAttribute("class");
18917             }
18918         }
18919         
18920         if (node.hasAttribute("lang")) {
18921             node.removeAttribute("lang");
18922         }
18923         
18924         if (node.hasAttribute("style")) {
18925             
18926             var styles = node.getAttribute("style").split(";");
18927             var nstyle = [];
18928             Roo.each(styles, function(s) {
18929                 if (!s.match(/:/)) {
18930                     return;
18931                 }
18932                 var kv = s.split(":");
18933                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
18934                     return;
18935                 }
18936                 // what ever is left... we allow.
18937                 nstyle.push(s);
18938             });
18939             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
18940             if (!nstyle.length) {
18941                 node.removeAttribute('style');
18942             }
18943         }
18944         
18945         cleanWordChildren();
18946         
18947         
18948     },
18949     domToHTML : function(currentElement, depth, nopadtext) {
18950         
18951         depth = depth || 0;
18952         nopadtext = nopadtext || false;
18953     
18954         if (!currentElement) {
18955             return this.domToHTML(this.doc.body);
18956         }
18957         
18958         //Roo.log(currentElement);
18959         var j;
18960         var allText = false;
18961         var nodeName = currentElement.nodeName;
18962         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
18963         
18964         if  (nodeName == '#text') {
18965             
18966             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
18967         }
18968         
18969         
18970         var ret = '';
18971         if (nodeName != 'BODY') {
18972              
18973             var i = 0;
18974             // Prints the node tagName, such as <A>, <IMG>, etc
18975             if (tagName) {
18976                 var attr = [];
18977                 for(i = 0; i < currentElement.attributes.length;i++) {
18978                     // quoting?
18979                     var aname = currentElement.attributes.item(i).name;
18980                     if (!currentElement.attributes.item(i).value.length) {
18981                         continue;
18982                     }
18983                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
18984                 }
18985                 
18986                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
18987             } 
18988             else {
18989                 
18990                 // eack
18991             }
18992         } else {
18993             tagName = false;
18994         }
18995         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
18996             return ret;
18997         }
18998         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
18999             nopadtext = true;
19000         }
19001         
19002         
19003         // Traverse the tree
19004         i = 0;
19005         var currentElementChild = currentElement.childNodes.item(i);
19006         var allText = true;
19007         var innerHTML  = '';
19008         lastnode = '';
19009         while (currentElementChild) {
19010             // Formatting code (indent the tree so it looks nice on the screen)
19011             var nopad = nopadtext;
19012             if (lastnode == 'SPAN') {
19013                 nopad  = true;
19014             }
19015             // text
19016             if  (currentElementChild.nodeName == '#text') {
19017                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
19018                 toadd = nopadtext ? toadd : toadd.trim();
19019                 if (!nopad && toadd.length > 80) {
19020                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
19021                 }
19022                 innerHTML  += toadd;
19023                 
19024                 i++;
19025                 currentElementChild = currentElement.childNodes.item(i);
19026                 lastNode = '';
19027                 continue;
19028             }
19029             allText = false;
19030             
19031             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
19032                 
19033             // Recursively traverse the tree structure of the child node
19034             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
19035             lastnode = currentElementChild.nodeName;
19036             i++;
19037             currentElementChild=currentElement.childNodes.item(i);
19038         }
19039         
19040         ret += innerHTML;
19041         
19042         if (!allText) {
19043                 // The remaining code is mostly for formatting the tree
19044             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
19045         }
19046         
19047         
19048         if (tagName) {
19049             ret+= "</"+tagName+">";
19050         }
19051         return ret;
19052         
19053     },
19054         
19055     applyBlacklists : function()
19056     {
19057         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
19058         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
19059         
19060         this.white = [];
19061         this.black = [];
19062         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
19063             if (b.indexOf(tag) > -1) {
19064                 return;
19065             }
19066             this.white.push(tag);
19067             
19068         }, this);
19069         
19070         Roo.each(w, function(tag) {
19071             if (b.indexOf(tag) > -1) {
19072                 return;
19073             }
19074             if (this.white.indexOf(tag) > -1) {
19075                 return;
19076             }
19077             this.white.push(tag);
19078             
19079         }, this);
19080         
19081         
19082         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
19083             if (w.indexOf(tag) > -1) {
19084                 return;
19085             }
19086             this.black.push(tag);
19087             
19088         }, this);
19089         
19090         Roo.each(b, function(tag) {
19091             if (w.indexOf(tag) > -1) {
19092                 return;
19093             }
19094             if (this.black.indexOf(tag) > -1) {
19095                 return;
19096             }
19097             this.black.push(tag);
19098             
19099         }, this);
19100         
19101         
19102         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
19103         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
19104         
19105         this.cwhite = [];
19106         this.cblack = [];
19107         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
19108             if (b.indexOf(tag) > -1) {
19109                 return;
19110             }
19111             this.cwhite.push(tag);
19112             
19113         }, this);
19114         
19115         Roo.each(w, function(tag) {
19116             if (b.indexOf(tag) > -1) {
19117                 return;
19118             }
19119             if (this.cwhite.indexOf(tag) > -1) {
19120                 return;
19121             }
19122             this.cwhite.push(tag);
19123             
19124         }, this);
19125         
19126         
19127         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
19128             if (w.indexOf(tag) > -1) {
19129                 return;
19130             }
19131             this.cblack.push(tag);
19132             
19133         }, this);
19134         
19135         Roo.each(b, function(tag) {
19136             if (w.indexOf(tag) > -1) {
19137                 return;
19138             }
19139             if (this.cblack.indexOf(tag) > -1) {
19140                 return;
19141             }
19142             this.cblack.push(tag);
19143             
19144         }, this);
19145     },
19146     
19147     setStylesheets : function(stylesheets)
19148     {
19149         if(typeof(stylesheets) == 'string'){
19150             Roo.get(this.iframe.contentDocument.head).createChild({
19151                 tag : 'link',
19152                 rel : 'stylesheet',
19153                 type : 'text/css',
19154                 href : stylesheets
19155             });
19156             
19157             return;
19158         }
19159         var _this = this;
19160      
19161         Roo.each(stylesheets, function(s) {
19162             if(!s.length){
19163                 return;
19164             }
19165             
19166             Roo.get(_this.iframe.contentDocument.head).createChild({
19167                 tag : 'link',
19168                 rel : 'stylesheet',
19169                 type : 'text/css',
19170                 href : s
19171             });
19172         });
19173
19174         
19175     },
19176     
19177     removeStylesheets : function()
19178     {
19179         var _this = this;
19180         
19181         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
19182             s.remove();
19183         });
19184     }
19185     
19186     // hide stuff that is not compatible
19187     /**
19188      * @event blur
19189      * @hide
19190      */
19191     /**
19192      * @event change
19193      * @hide
19194      */
19195     /**
19196      * @event focus
19197      * @hide
19198      */
19199     /**
19200      * @event specialkey
19201      * @hide
19202      */
19203     /**
19204      * @cfg {String} fieldClass @hide
19205      */
19206     /**
19207      * @cfg {String} focusClass @hide
19208      */
19209     /**
19210      * @cfg {String} autoCreate @hide
19211      */
19212     /**
19213      * @cfg {String} inputType @hide
19214      */
19215     /**
19216      * @cfg {String} invalidClass @hide
19217      */
19218     /**
19219      * @cfg {String} invalidText @hide
19220      */
19221     /**
19222      * @cfg {String} msgFx @hide
19223      */
19224     /**
19225      * @cfg {String} validateOnBlur @hide
19226      */
19227 });
19228
19229 Roo.HtmlEditorCore.white = [
19230         'area', 'br', 'img', 'input', 'hr', 'wbr',
19231         
19232        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
19233        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
19234        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
19235        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
19236        'table',   'ul',         'xmp', 
19237        
19238        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
19239       'thead',   'tr', 
19240      
19241       'dir', 'menu', 'ol', 'ul', 'dl',
19242        
19243       'embed',  'object'
19244 ];
19245
19246
19247 Roo.HtmlEditorCore.black = [
19248     //    'embed',  'object', // enable - backend responsiblity to clean thiese
19249         'applet', // 
19250         'base',   'basefont', 'bgsound', 'blink',  'body', 
19251         'frame',  'frameset', 'head',    'html',   'ilayer', 
19252         'iframe', 'layer',  'link',     'meta',    'object',   
19253         'script', 'style' ,'title',  'xml' // clean later..
19254 ];
19255 Roo.HtmlEditorCore.clean = [
19256     'script', 'style', 'title', 'xml'
19257 ];
19258 Roo.HtmlEditorCore.remove = [
19259     'font'
19260 ];
19261 // attributes..
19262
19263 Roo.HtmlEditorCore.ablack = [
19264     'on'
19265 ];
19266     
19267 Roo.HtmlEditorCore.aclean = [ 
19268     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
19269 ];
19270
19271 // protocols..
19272 Roo.HtmlEditorCore.pwhite= [
19273         'http',  'https',  'mailto'
19274 ];
19275
19276 // white listed style attributes.
19277 Roo.HtmlEditorCore.cwhite= [
19278       //  'text-align', /// default is to allow most things..
19279       
19280          
19281 //        'font-size'//??
19282 ];
19283
19284 // black listed style attributes.
19285 Roo.HtmlEditorCore.cblack= [
19286       //  'font-size' -- this can be set by the project 
19287 ];
19288
19289
19290 Roo.HtmlEditorCore.swapCodes   =[ 
19291     [    8211, "--" ], 
19292     [    8212, "--" ], 
19293     [    8216,  "'" ],  
19294     [    8217, "'" ],  
19295     [    8220, '"' ],  
19296     [    8221, '"' ],  
19297     [    8226, "*" ],  
19298     [    8230, "..." ]
19299 ]; 
19300
19301     /*
19302  * - LGPL
19303  *
19304  * HtmlEditor
19305  * 
19306  */
19307
19308 /**
19309  * @class Roo.bootstrap.HtmlEditor
19310  * @extends Roo.bootstrap.TextArea
19311  * Bootstrap HtmlEditor class
19312
19313  * @constructor
19314  * Create a new HtmlEditor
19315  * @param {Object} config The config object
19316  */
19317
19318 Roo.bootstrap.HtmlEditor = function(config){
19319     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
19320     if (!this.toolbars) {
19321         this.toolbars = [];
19322     }
19323     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
19324     this.addEvents({
19325             /**
19326              * @event initialize
19327              * Fires when the editor is fully initialized (including the iframe)
19328              * @param {HtmlEditor} this
19329              */
19330             initialize: true,
19331             /**
19332              * @event activate
19333              * Fires when the editor is first receives the focus. Any insertion must wait
19334              * until after this event.
19335              * @param {HtmlEditor} this
19336              */
19337             activate: true,
19338              /**
19339              * @event beforesync
19340              * Fires before the textarea is updated with content from the editor iframe. Return false
19341              * to cancel the sync.
19342              * @param {HtmlEditor} this
19343              * @param {String} html
19344              */
19345             beforesync: true,
19346              /**
19347              * @event beforepush
19348              * Fires before the iframe editor is updated with content from the textarea. Return false
19349              * to cancel the push.
19350              * @param {HtmlEditor} this
19351              * @param {String} html
19352              */
19353             beforepush: true,
19354              /**
19355              * @event sync
19356              * Fires when the textarea is updated with content from the editor iframe.
19357              * @param {HtmlEditor} this
19358              * @param {String} html
19359              */
19360             sync: true,
19361              /**
19362              * @event push
19363              * Fires when the iframe editor is updated with content from the textarea.
19364              * @param {HtmlEditor} this
19365              * @param {String} html
19366              */
19367             push: true,
19368              /**
19369              * @event editmodechange
19370              * Fires when the editor switches edit modes
19371              * @param {HtmlEditor} this
19372              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
19373              */
19374             editmodechange: true,
19375             /**
19376              * @event editorevent
19377              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19378              * @param {HtmlEditor} this
19379              */
19380             editorevent: true,
19381             /**
19382              * @event firstfocus
19383              * Fires when on first focus - needed by toolbars..
19384              * @param {HtmlEditor} this
19385              */
19386             firstfocus: true,
19387             /**
19388              * @event autosave
19389              * Auto save the htmlEditor value as a file into Events
19390              * @param {HtmlEditor} this
19391              */
19392             autosave: true,
19393             /**
19394              * @event savedpreview
19395              * preview the saved version of htmlEditor
19396              * @param {HtmlEditor} this
19397              */
19398             savedpreview: true
19399         });
19400 };
19401
19402
19403 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
19404     
19405     
19406       /**
19407      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
19408      */
19409     toolbars : false,
19410    
19411      /**
19412      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19413      *                        Roo.resizable.
19414      */
19415     resizable : false,
19416      /**
19417      * @cfg {Number} height (in pixels)
19418      */   
19419     height: 300,
19420    /**
19421      * @cfg {Number} width (in pixels)
19422      */   
19423     width: false,
19424     
19425     /**
19426      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19427      * 
19428      */
19429     stylesheets: false,
19430     
19431     // id of frame..
19432     frameId: false,
19433     
19434     // private properties
19435     validationEvent : false,
19436     deferHeight: true,
19437     initialized : false,
19438     activated : false,
19439     
19440     onFocus : Roo.emptyFn,
19441     iframePad:3,
19442     hideMode:'offsets',
19443     
19444     
19445     tbContainer : false,
19446     
19447     toolbarContainer :function() {
19448         return this.wrap.select('.x-html-editor-tb',true).first();
19449     },
19450
19451     /**
19452      * Protected method that will not generally be called directly. It
19453      * is called when the editor creates its toolbar. Override this method if you need to
19454      * add custom toolbar buttons.
19455      * @param {HtmlEditor} editor
19456      */
19457     createToolbar : function(){
19458         
19459         Roo.log("create toolbars");
19460         
19461         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
19462         this.toolbars[0].render(this.toolbarContainer());
19463         
19464         return;
19465         
19466 //        if (!editor.toolbars || !editor.toolbars.length) {
19467 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
19468 //        }
19469 //        
19470 //        for (var i =0 ; i < editor.toolbars.length;i++) {
19471 //            editor.toolbars[i] = Roo.factory(
19472 //                    typeof(editor.toolbars[i]) == 'string' ?
19473 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
19474 //                Roo.bootstrap.HtmlEditor);
19475 //            editor.toolbars[i].init(editor);
19476 //        }
19477     },
19478
19479      
19480     // private
19481     onRender : function(ct, position)
19482     {
19483        // Roo.log("Call onRender: " + this.xtype);
19484         var _t = this;
19485         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
19486       
19487         this.wrap = this.inputEl().wrap({
19488             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
19489         });
19490         
19491         this.editorcore.onRender(ct, position);
19492          
19493         if (this.resizable) {
19494             this.resizeEl = new Roo.Resizable(this.wrap, {
19495                 pinned : true,
19496                 wrap: true,
19497                 dynamic : true,
19498                 minHeight : this.height,
19499                 height: this.height,
19500                 handles : this.resizable,
19501                 width: this.width,
19502                 listeners : {
19503                     resize : function(r, w, h) {
19504                         _t.onResize(w,h); // -something
19505                     }
19506                 }
19507             });
19508             
19509         }
19510         this.createToolbar(this);
19511        
19512         
19513         if(!this.width && this.resizable){
19514             this.setSize(this.wrap.getSize());
19515         }
19516         if (this.resizeEl) {
19517             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
19518             // should trigger onReize..
19519         }
19520         
19521     },
19522
19523     // private
19524     onResize : function(w, h)
19525     {
19526         Roo.log('resize: ' +w + ',' + h );
19527         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
19528         var ew = false;
19529         var eh = false;
19530         
19531         if(this.inputEl() ){
19532             if(typeof w == 'number'){
19533                 var aw = w - this.wrap.getFrameWidth('lr');
19534                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
19535                 ew = aw;
19536             }
19537             if(typeof h == 'number'){
19538                  var tbh = -11;  // fixme it needs to tool bar size!
19539                 for (var i =0; i < this.toolbars.length;i++) {
19540                     // fixme - ask toolbars for heights?
19541                     tbh += this.toolbars[i].el.getHeight();
19542                     //if (this.toolbars[i].footer) {
19543                     //    tbh += this.toolbars[i].footer.el.getHeight();
19544                     //}
19545                 }
19546               
19547                 
19548                 
19549                 
19550                 
19551                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
19552                 ah -= 5; // knock a few pixes off for look..
19553                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
19554                 var eh = ah;
19555             }
19556         }
19557         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
19558         this.editorcore.onResize(ew,eh);
19559         
19560     },
19561
19562     /**
19563      * Toggles the editor between standard and source edit mode.
19564      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19565      */
19566     toggleSourceEdit : function(sourceEditMode)
19567     {
19568         this.editorcore.toggleSourceEdit(sourceEditMode);
19569         
19570         if(this.editorcore.sourceEditMode){
19571             Roo.log('editor - showing textarea');
19572             
19573 //            Roo.log('in');
19574 //            Roo.log(this.syncValue());
19575             this.syncValue();
19576             this.inputEl().removeClass(['hide', 'x-hidden']);
19577             this.inputEl().dom.removeAttribute('tabIndex');
19578             this.inputEl().focus();
19579         }else{
19580             Roo.log('editor - hiding textarea');
19581 //            Roo.log('out')
19582 //            Roo.log(this.pushValue()); 
19583             this.pushValue();
19584             
19585             this.inputEl().addClass(['hide', 'x-hidden']);
19586             this.inputEl().dom.setAttribute('tabIndex', -1);
19587             //this.deferFocus();
19588         }
19589          
19590         if(this.resizable){
19591             this.setSize(this.wrap.getSize());
19592         }
19593         
19594         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
19595     },
19596  
19597     // private (for BoxComponent)
19598     adjustSize : Roo.BoxComponent.prototype.adjustSize,
19599
19600     // private (for BoxComponent)
19601     getResizeEl : function(){
19602         return this.wrap;
19603     },
19604
19605     // private (for BoxComponent)
19606     getPositionEl : function(){
19607         return this.wrap;
19608     },
19609
19610     // private
19611     initEvents : function(){
19612         this.originalValue = this.getValue();
19613     },
19614
19615 //    /**
19616 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19617 //     * @method
19618 //     */
19619 //    markInvalid : Roo.emptyFn,
19620 //    /**
19621 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19622 //     * @method
19623 //     */
19624 //    clearInvalid : Roo.emptyFn,
19625
19626     setValue : function(v){
19627         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
19628         this.editorcore.pushValue();
19629     },
19630
19631      
19632     // private
19633     deferFocus : function(){
19634         this.focus.defer(10, this);
19635     },
19636
19637     // doc'ed in Field
19638     focus : function(){
19639         this.editorcore.focus();
19640         
19641     },
19642       
19643
19644     // private
19645     onDestroy : function(){
19646         
19647         
19648         
19649         if(this.rendered){
19650             
19651             for (var i =0; i < this.toolbars.length;i++) {
19652                 // fixme - ask toolbars for heights?
19653                 this.toolbars[i].onDestroy();
19654             }
19655             
19656             this.wrap.dom.innerHTML = '';
19657             this.wrap.remove();
19658         }
19659     },
19660
19661     // private
19662     onFirstFocus : function(){
19663         //Roo.log("onFirstFocus");
19664         this.editorcore.onFirstFocus();
19665          for (var i =0; i < this.toolbars.length;i++) {
19666             this.toolbars[i].onFirstFocus();
19667         }
19668         
19669     },
19670     
19671     // private
19672     syncValue : function()
19673     {   
19674         this.editorcore.syncValue();
19675     },
19676     
19677     pushValue : function()
19678     {   
19679         this.editorcore.pushValue();
19680     }
19681      
19682     
19683     // hide stuff that is not compatible
19684     /**
19685      * @event blur
19686      * @hide
19687      */
19688     /**
19689      * @event change
19690      * @hide
19691      */
19692     /**
19693      * @event focus
19694      * @hide
19695      */
19696     /**
19697      * @event specialkey
19698      * @hide
19699      */
19700     /**
19701      * @cfg {String} fieldClass @hide
19702      */
19703     /**
19704      * @cfg {String} focusClass @hide
19705      */
19706     /**
19707      * @cfg {String} autoCreate @hide
19708      */
19709     /**
19710      * @cfg {String} inputType @hide
19711      */
19712     /**
19713      * @cfg {String} invalidClass @hide
19714      */
19715     /**
19716      * @cfg {String} invalidText @hide
19717      */
19718     /**
19719      * @cfg {String} msgFx @hide
19720      */
19721     /**
19722      * @cfg {String} validateOnBlur @hide
19723      */
19724 });
19725  
19726     
19727    
19728    
19729    
19730       
19731 Roo.namespace('Roo.bootstrap.htmleditor');
19732 /**
19733  * @class Roo.bootstrap.HtmlEditorToolbar1
19734  * Basic Toolbar
19735  * 
19736  * Usage:
19737  *
19738  new Roo.bootstrap.HtmlEditor({
19739     ....
19740     toolbars : [
19741         new Roo.bootstrap.HtmlEditorToolbar1({
19742             disable : { fonts: 1 , format: 1, ..., ... , ...],
19743             btns : [ .... ]
19744         })
19745     }
19746      
19747  * 
19748  * @cfg {Object} disable List of elements to disable..
19749  * @cfg {Array} btns List of additional buttons.
19750  * 
19751  * 
19752  * NEEDS Extra CSS? 
19753  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
19754  */
19755  
19756 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
19757 {
19758     
19759     Roo.apply(this, config);
19760     
19761     // default disabled, based on 'good practice'..
19762     this.disable = this.disable || {};
19763     Roo.applyIf(this.disable, {
19764         fontSize : true,
19765         colors : true,
19766         specialElements : true
19767     });
19768     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
19769     
19770     this.editor = config.editor;
19771     this.editorcore = config.editor.editorcore;
19772     
19773     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
19774     
19775     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
19776     // dont call parent... till later.
19777 }
19778 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
19779      
19780     bar : true,
19781     
19782     editor : false,
19783     editorcore : false,
19784     
19785     
19786     formats : [
19787         "p" ,  
19788         "h1","h2","h3","h4","h5","h6", 
19789         "pre", "code", 
19790         "abbr", "acronym", "address", "cite", "samp", "var",
19791         'div','span'
19792     ],
19793     
19794     onRender : function(ct, position)
19795     {
19796        // Roo.log("Call onRender: " + this.xtype);
19797         
19798        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
19799        Roo.log(this.el);
19800        this.el.dom.style.marginBottom = '0';
19801        var _this = this;
19802        var editorcore = this.editorcore;
19803        var editor= this.editor;
19804        
19805        var children = [];
19806        var btn = function(id,cmd , toggle, handler){
19807        
19808             var  event = toggle ? 'toggle' : 'click';
19809        
19810             var a = {
19811                 size : 'sm',
19812                 xtype: 'Button',
19813                 xns: Roo.bootstrap,
19814                 glyphicon : id,
19815                 cmd : id || cmd,
19816                 enableToggle:toggle !== false,
19817                 //html : 'submit'
19818                 pressed : toggle ? false : null,
19819                 listeners : {}
19820             }
19821             a.listeners[toggle ? 'toggle' : 'click'] = function() {
19822                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
19823             }
19824             children.push(a);
19825             return a;
19826        }
19827         
19828         var style = {
19829                 xtype: 'Button',
19830                 size : 'sm',
19831                 xns: Roo.bootstrap,
19832                 glyphicon : 'font',
19833                 //html : 'submit'
19834                 menu : {
19835                     xtype: 'Menu',
19836                     xns: Roo.bootstrap,
19837                     items:  []
19838                 }
19839         };
19840         Roo.each(this.formats, function(f) {
19841             style.menu.items.push({
19842                 xtype :'MenuItem',
19843                 xns: Roo.bootstrap,
19844                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
19845                 tagname : f,
19846                 listeners : {
19847                     click : function()
19848                     {
19849                         editorcore.insertTag(this.tagname);
19850                         editor.focus();
19851                     }
19852                 }
19853                 
19854             });
19855         });
19856          children.push(style);   
19857             
19858             
19859         btn('bold',false,true);
19860         btn('italic',false,true);
19861         btn('align-left', 'justifyleft',true);
19862         btn('align-center', 'justifycenter',true);
19863         btn('align-right' , 'justifyright',true);
19864         btn('link', false, false, function(btn) {
19865             //Roo.log("create link?");
19866             var url = prompt(this.createLinkText, this.defaultLinkValue);
19867             if(url && url != 'http:/'+'/'){
19868                 this.editorcore.relayCmd('createlink', url);
19869             }
19870         }),
19871         btn('list','insertunorderedlist',true);
19872         btn('pencil', false,true, function(btn){
19873                 Roo.log(this);
19874                 
19875                 this.toggleSourceEdit(btn.pressed);
19876         });
19877         /*
19878         var cog = {
19879                 xtype: 'Button',
19880                 size : 'sm',
19881                 xns: Roo.bootstrap,
19882                 glyphicon : 'cog',
19883                 //html : 'submit'
19884                 menu : {
19885                     xtype: 'Menu',
19886                     xns: Roo.bootstrap,
19887                     items:  []
19888                 }
19889         };
19890         
19891         cog.menu.items.push({
19892             xtype :'MenuItem',
19893             xns: Roo.bootstrap,
19894             html : Clean styles,
19895             tagname : f,
19896             listeners : {
19897                 click : function()
19898                 {
19899                     editorcore.insertTag(this.tagname);
19900                     editor.focus();
19901                 }
19902             }
19903             
19904         });
19905        */
19906         
19907          
19908        this.xtype = 'NavSimplebar';
19909         
19910         for(var i=0;i< children.length;i++) {
19911             
19912             this.buttons.add(this.addxtypeChild(children[i]));
19913             
19914         }
19915         
19916         editor.on('editorevent', this.updateToolbar, this);
19917     },
19918     onBtnClick : function(id)
19919     {
19920        this.editorcore.relayCmd(id);
19921        this.editorcore.focus();
19922     },
19923     
19924     /**
19925      * Protected method that will not generally be called directly. It triggers
19926      * a toolbar update by reading the markup state of the current selection in the editor.
19927      */
19928     updateToolbar: function(){
19929
19930         if(!this.editorcore.activated){
19931             this.editor.onFirstFocus(); // is this neeed?
19932             return;
19933         }
19934
19935         var btns = this.buttons; 
19936         var doc = this.editorcore.doc;
19937         btns.get('bold').setActive(doc.queryCommandState('bold'));
19938         btns.get('italic').setActive(doc.queryCommandState('italic'));
19939         //btns.get('underline').setActive(doc.queryCommandState('underline'));
19940         
19941         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
19942         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
19943         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
19944         
19945         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
19946         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
19947          /*
19948         
19949         var ans = this.editorcore.getAllAncestors();
19950         if (this.formatCombo) {
19951             
19952             
19953             var store = this.formatCombo.store;
19954             this.formatCombo.setValue("");
19955             for (var i =0; i < ans.length;i++) {
19956                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
19957                     // select it..
19958                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
19959                     break;
19960                 }
19961             }
19962         }
19963         
19964         
19965         
19966         // hides menus... - so this cant be on a menu...
19967         Roo.bootstrap.MenuMgr.hideAll();
19968         */
19969         Roo.bootstrap.MenuMgr.hideAll();
19970         //this.editorsyncValue();
19971     },
19972     onFirstFocus: function() {
19973         this.buttons.each(function(item){
19974            item.enable();
19975         });
19976     },
19977     toggleSourceEdit : function(sourceEditMode){
19978         
19979           
19980         if(sourceEditMode){
19981             Roo.log("disabling buttons");
19982            this.buttons.each( function(item){
19983                 if(item.cmd != 'pencil'){
19984                     item.disable();
19985                 }
19986             });
19987           
19988         }else{
19989             Roo.log("enabling buttons");
19990             if(this.editorcore.initialized){
19991                 this.buttons.each( function(item){
19992                     item.enable();
19993                 });
19994             }
19995             
19996         }
19997         Roo.log("calling toggole on editor");
19998         // tell the editor that it's been pressed..
19999         this.editor.toggleSourceEdit(sourceEditMode);
20000        
20001     }
20002 });
20003
20004
20005
20006
20007
20008 /**
20009  * @class Roo.bootstrap.Table.AbstractSelectionModel
20010  * @extends Roo.util.Observable
20011  * Abstract base class for grid SelectionModels.  It provides the interface that should be
20012  * implemented by descendant classes.  This class should not be directly instantiated.
20013  * @constructor
20014  */
20015 Roo.bootstrap.Table.AbstractSelectionModel = function(){
20016     this.locked = false;
20017     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
20018 };
20019
20020
20021 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
20022     /** @ignore Called by the grid automatically. Do not call directly. */
20023     init : function(grid){
20024         this.grid = grid;
20025         this.initEvents();
20026     },
20027
20028     /**
20029      * Locks the selections.
20030      */
20031     lock : function(){
20032         this.locked = true;
20033     },
20034
20035     /**
20036      * Unlocks the selections.
20037      */
20038     unlock : function(){
20039         this.locked = false;
20040     },
20041
20042     /**
20043      * Returns true if the selections are locked.
20044      * @return {Boolean}
20045      */
20046     isLocked : function(){
20047         return this.locked;
20048     }
20049 });
20050 /**
20051  * @extends Roo.bootstrap.Table.AbstractSelectionModel
20052  * @class Roo.bootstrap.Table.RowSelectionModel
20053  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
20054  * It supports multiple selections and keyboard selection/navigation. 
20055  * @constructor
20056  * @param {Object} config
20057  */
20058
20059 Roo.bootstrap.Table.RowSelectionModel = function(config){
20060     Roo.apply(this, config);
20061     this.selections = new Roo.util.MixedCollection(false, function(o){
20062         return o.id;
20063     });
20064
20065     this.last = false;
20066     this.lastActive = false;
20067
20068     this.addEvents({
20069         /**
20070              * @event selectionchange
20071              * Fires when the selection changes
20072              * @param {SelectionModel} this
20073              */
20074             "selectionchange" : true,
20075         /**
20076              * @event afterselectionchange
20077              * Fires after the selection changes (eg. by key press or clicking)
20078              * @param {SelectionModel} this
20079              */
20080             "afterselectionchange" : true,
20081         /**
20082              * @event beforerowselect
20083              * Fires when a row is selected being selected, return false to cancel.
20084              * @param {SelectionModel} this
20085              * @param {Number} rowIndex The selected index
20086              * @param {Boolean} keepExisting False if other selections will be cleared
20087              */
20088             "beforerowselect" : true,
20089         /**
20090              * @event rowselect
20091              * Fires when a row is selected.
20092              * @param {SelectionModel} this
20093              * @param {Number} rowIndex The selected index
20094              * @param {Roo.data.Record} r The record
20095              */
20096             "rowselect" : true,
20097         /**
20098              * @event rowdeselect
20099              * Fires when a row is deselected.
20100              * @param {SelectionModel} this
20101              * @param {Number} rowIndex The selected index
20102              */
20103         "rowdeselect" : true
20104     });
20105     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
20106     this.locked = false;
20107 };
20108
20109 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
20110     /**
20111      * @cfg {Boolean} singleSelect
20112      * True to allow selection of only one row at a time (defaults to false)
20113      */
20114     singleSelect : false,
20115
20116     // private
20117     initEvents : function(){
20118
20119         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
20120             this.grid.on("mousedown", this.handleMouseDown, this);
20121         }else{ // allow click to work like normal
20122             this.grid.on("rowclick", this.handleDragableRowClick, this);
20123         }
20124
20125         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
20126             "up" : function(e){
20127                 if(!e.shiftKey){
20128                     this.selectPrevious(e.shiftKey);
20129                 }else if(this.last !== false && this.lastActive !== false){
20130                     var last = this.last;
20131                     this.selectRange(this.last,  this.lastActive-1);
20132                     this.grid.getView().focusRow(this.lastActive);
20133                     if(last !== false){
20134                         this.last = last;
20135                     }
20136                 }else{
20137                     this.selectFirstRow();
20138                 }
20139                 this.fireEvent("afterselectionchange", this);
20140             },
20141             "down" : function(e){
20142                 if(!e.shiftKey){
20143                     this.selectNext(e.shiftKey);
20144                 }else if(this.last !== false && this.lastActive !== false){
20145                     var last = this.last;
20146                     this.selectRange(this.last,  this.lastActive+1);
20147                     this.grid.getView().focusRow(this.lastActive);
20148                     if(last !== false){
20149                         this.last = last;
20150                     }
20151                 }else{
20152                     this.selectFirstRow();
20153                 }
20154                 this.fireEvent("afterselectionchange", this);
20155             },
20156             scope: this
20157         });
20158
20159         var view = this.grid.view;
20160         view.on("refresh", this.onRefresh, this);
20161         view.on("rowupdated", this.onRowUpdated, this);
20162         view.on("rowremoved", this.onRemove, this);
20163     },
20164
20165     // private
20166     onRefresh : function(){
20167         var ds = this.grid.dataSource, i, v = this.grid.view;
20168         var s = this.selections;
20169         s.each(function(r){
20170             if((i = ds.indexOfId(r.id)) != -1){
20171                 v.onRowSelect(i);
20172             }else{
20173                 s.remove(r);
20174             }
20175         });
20176     },
20177
20178     // private
20179     onRemove : function(v, index, r){
20180         this.selections.remove(r);
20181     },
20182
20183     // private
20184     onRowUpdated : function(v, index, r){
20185         if(this.isSelected(r)){
20186             v.onRowSelect(index);
20187         }
20188     },
20189
20190     /**
20191      * Select records.
20192      * @param {Array} records The records to select
20193      * @param {Boolean} keepExisting (optional) True to keep existing selections
20194      */
20195     selectRecords : function(records, keepExisting){
20196         if(!keepExisting){
20197             this.clearSelections();
20198         }
20199         var ds = this.grid.dataSource;
20200         for(var i = 0, len = records.length; i < len; i++){
20201             this.selectRow(ds.indexOf(records[i]), true);
20202         }
20203     },
20204
20205     /**
20206      * Gets the number of selected rows.
20207      * @return {Number}
20208      */
20209     getCount : function(){
20210         return this.selections.length;
20211     },
20212
20213     /**
20214      * Selects the first row in the grid.
20215      */
20216     selectFirstRow : function(){
20217         this.selectRow(0);
20218     },
20219
20220     /**
20221      * Select the last row.
20222      * @param {Boolean} keepExisting (optional) True to keep existing selections
20223      */
20224     selectLastRow : function(keepExisting){
20225         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
20226     },
20227
20228     /**
20229      * Selects the row immediately following the last selected row.
20230      * @param {Boolean} keepExisting (optional) True to keep existing selections
20231      */
20232     selectNext : function(keepExisting){
20233         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
20234             this.selectRow(this.last+1, keepExisting);
20235             this.grid.getView().focusRow(this.last);
20236         }
20237     },
20238
20239     /**
20240      * Selects the row that precedes the last selected row.
20241      * @param {Boolean} keepExisting (optional) True to keep existing selections
20242      */
20243     selectPrevious : function(keepExisting){
20244         if(this.last){
20245             this.selectRow(this.last-1, keepExisting);
20246             this.grid.getView().focusRow(this.last);
20247         }
20248     },
20249
20250     /**
20251      * Returns the selected records
20252      * @return {Array} Array of selected records
20253      */
20254     getSelections : function(){
20255         return [].concat(this.selections.items);
20256     },
20257
20258     /**
20259      * Returns the first selected record.
20260      * @return {Record}
20261      */
20262     getSelected : function(){
20263         return this.selections.itemAt(0);
20264     },
20265
20266
20267     /**
20268      * Clears all selections.
20269      */
20270     clearSelections : function(fast){
20271         if(this.locked) return;
20272         if(fast !== true){
20273             var ds = this.grid.dataSource;
20274             var s = this.selections;
20275             s.each(function(r){
20276                 this.deselectRow(ds.indexOfId(r.id));
20277             }, this);
20278             s.clear();
20279         }else{
20280             this.selections.clear();
20281         }
20282         this.last = false;
20283     },
20284
20285
20286     /**
20287      * Selects all rows.
20288      */
20289     selectAll : function(){
20290         if(this.locked) return;
20291         this.selections.clear();
20292         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
20293             this.selectRow(i, true);
20294         }
20295     },
20296
20297     /**
20298      * Returns True if there is a selection.
20299      * @return {Boolean}
20300      */
20301     hasSelection : function(){
20302         return this.selections.length > 0;
20303     },
20304
20305     /**
20306      * Returns True if the specified row is selected.
20307      * @param {Number/Record} record The record or index of the record to check
20308      * @return {Boolean}
20309      */
20310     isSelected : function(index){
20311         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
20312         return (r && this.selections.key(r.id) ? true : false);
20313     },
20314
20315     /**
20316      * Returns True if the specified record id is selected.
20317      * @param {String} id The id of record to check
20318      * @return {Boolean}
20319      */
20320     isIdSelected : function(id){
20321         return (this.selections.key(id) ? true : false);
20322     },
20323
20324     // private
20325     handleMouseDown : function(e, t){
20326         var view = this.grid.getView(), rowIndex;
20327         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
20328             return;
20329         };
20330         if(e.shiftKey && this.last !== false){
20331             var last = this.last;
20332             this.selectRange(last, rowIndex, e.ctrlKey);
20333             this.last = last; // reset the last
20334             view.focusRow(rowIndex);
20335         }else{
20336             var isSelected = this.isSelected(rowIndex);
20337             if(e.button !== 0 && isSelected){
20338                 view.focusRow(rowIndex);
20339             }else if(e.ctrlKey && isSelected){
20340                 this.deselectRow(rowIndex);
20341             }else if(!isSelected){
20342                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
20343                 view.focusRow(rowIndex);
20344             }
20345         }
20346         this.fireEvent("afterselectionchange", this);
20347     },
20348     // private
20349     handleDragableRowClick :  function(grid, rowIndex, e) 
20350     {
20351         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
20352             this.selectRow(rowIndex, false);
20353             grid.view.focusRow(rowIndex);
20354              this.fireEvent("afterselectionchange", this);
20355         }
20356     },
20357     
20358     /**
20359      * Selects multiple rows.
20360      * @param {Array} rows Array of the indexes of the row to select
20361      * @param {Boolean} keepExisting (optional) True to keep existing selections
20362      */
20363     selectRows : function(rows, keepExisting){
20364         if(!keepExisting){
20365             this.clearSelections();
20366         }
20367         for(var i = 0, len = rows.length; i < len; i++){
20368             this.selectRow(rows[i], true);
20369         }
20370     },
20371
20372     /**
20373      * Selects a range of rows. All rows in between startRow and endRow are also selected.
20374      * @param {Number} startRow The index of the first row in the range
20375      * @param {Number} endRow The index of the last row in the range
20376      * @param {Boolean} keepExisting (optional) True to retain existing selections
20377      */
20378     selectRange : function(startRow, endRow, keepExisting){
20379         if(this.locked) return;
20380         if(!keepExisting){
20381             this.clearSelections();
20382         }
20383         if(startRow <= endRow){
20384             for(var i = startRow; i <= endRow; i++){
20385                 this.selectRow(i, true);
20386             }
20387         }else{
20388             for(var i = startRow; i >= endRow; i--){
20389                 this.selectRow(i, true);
20390             }
20391         }
20392     },
20393
20394     /**
20395      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
20396      * @param {Number} startRow The index of the first row in the range
20397      * @param {Number} endRow The index of the last row in the range
20398      */
20399     deselectRange : function(startRow, endRow, preventViewNotify){
20400         if(this.locked) return;
20401         for(var i = startRow; i <= endRow; i++){
20402             this.deselectRow(i, preventViewNotify);
20403         }
20404     },
20405
20406     /**
20407      * Selects a row.
20408      * @param {Number} row The index of the row to select
20409      * @param {Boolean} keepExisting (optional) True to keep existing selections
20410      */
20411     selectRow : function(index, keepExisting, preventViewNotify){
20412         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
20413         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
20414             if(!keepExisting || this.singleSelect){
20415                 this.clearSelections();
20416             }
20417             var r = this.grid.dataSource.getAt(index);
20418             this.selections.add(r);
20419             this.last = this.lastActive = index;
20420             if(!preventViewNotify){
20421                 this.grid.getView().onRowSelect(index);
20422             }
20423             this.fireEvent("rowselect", this, index, r);
20424             this.fireEvent("selectionchange", this);
20425         }
20426     },
20427
20428     /**
20429      * Deselects a row.
20430      * @param {Number} row The index of the row to deselect
20431      */
20432     deselectRow : function(index, preventViewNotify){
20433         if(this.locked) return;
20434         if(this.last == index){
20435             this.last = false;
20436         }
20437         if(this.lastActive == index){
20438             this.lastActive = false;
20439         }
20440         var r = this.grid.dataSource.getAt(index);
20441         this.selections.remove(r);
20442         if(!preventViewNotify){
20443             this.grid.getView().onRowDeselect(index);
20444         }
20445         this.fireEvent("rowdeselect", this, index);
20446         this.fireEvent("selectionchange", this);
20447     },
20448
20449     // private
20450     restoreLast : function(){
20451         if(this._last){
20452             this.last = this._last;
20453         }
20454     },
20455
20456     // private
20457     acceptsNav : function(row, col, cm){
20458         return !cm.isHidden(col) && cm.isCellEditable(col, row);
20459     },
20460
20461     // private
20462     onEditorKey : function(field, e){
20463         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
20464         if(k == e.TAB){
20465             e.stopEvent();
20466             ed.completeEdit();
20467             if(e.shiftKey){
20468                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
20469             }else{
20470                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
20471             }
20472         }else if(k == e.ENTER && !e.ctrlKey){
20473             e.stopEvent();
20474             ed.completeEdit();
20475             if(e.shiftKey){
20476                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
20477             }else{
20478                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
20479             }
20480         }else if(k == e.ESC){
20481             ed.cancelEdit();
20482         }
20483         if(newCell){
20484             g.startEditing(newCell[0], newCell[1]);
20485         }
20486     }
20487 });/*
20488  * Based on:
20489  * Ext JS Library 1.1.1
20490  * Copyright(c) 2006-2007, Ext JS, LLC.
20491  *
20492  * Originally Released Under LGPL - original licence link has changed is not relivant.
20493  *
20494  * Fork - LGPL
20495  * <script type="text/javascript">
20496  */
20497  
20498 /**
20499  * @class Roo.bootstrap.PagingToolbar
20500  * @extends Roo.Row
20501  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
20502  * @constructor
20503  * Create a new PagingToolbar
20504  * @param {Object} config The config object
20505  */
20506 Roo.bootstrap.PagingToolbar = function(config)
20507 {
20508     // old args format still supported... - xtype is prefered..
20509         // created from xtype...
20510     var ds = config.dataSource;
20511     this.toolbarItems = [];
20512     if (config.items) {
20513         this.toolbarItems = config.items;
20514 //        config.items = [];
20515     }
20516     
20517     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
20518     this.ds = ds;
20519     this.cursor = 0;
20520     if (ds) { 
20521         this.bind(ds);
20522     }
20523     
20524     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
20525     
20526 };
20527
20528 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
20529     /**
20530      * @cfg {Roo.data.Store} dataSource
20531      * The underlying data store providing the paged data
20532      */
20533     /**
20534      * @cfg {String/HTMLElement/Element} container
20535      * container The id or element that will contain the toolbar
20536      */
20537     /**
20538      * @cfg {Boolean} displayInfo
20539      * True to display the displayMsg (defaults to false)
20540      */
20541     /**
20542      * @cfg {Number} pageSize
20543      * The number of records to display per page (defaults to 20)
20544      */
20545     pageSize: 20,
20546     /**
20547      * @cfg {String} displayMsg
20548      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
20549      */
20550     displayMsg : 'Displaying {0} - {1} of {2}',
20551     /**
20552      * @cfg {String} emptyMsg
20553      * The message to display when no records are found (defaults to "No data to display")
20554      */
20555     emptyMsg : 'No data to display',
20556     /**
20557      * Customizable piece of the default paging text (defaults to "Page")
20558      * @type String
20559      */
20560     beforePageText : "Page",
20561     /**
20562      * Customizable piece of the default paging text (defaults to "of %0")
20563      * @type String
20564      */
20565     afterPageText : "of {0}",
20566     /**
20567      * Customizable piece of the default paging text (defaults to "First Page")
20568      * @type String
20569      */
20570     firstText : "First Page",
20571     /**
20572      * Customizable piece of the default paging text (defaults to "Previous Page")
20573      * @type String
20574      */
20575     prevText : "Previous Page",
20576     /**
20577      * Customizable piece of the default paging text (defaults to "Next Page")
20578      * @type String
20579      */
20580     nextText : "Next Page",
20581     /**
20582      * Customizable piece of the default paging text (defaults to "Last Page")
20583      * @type String
20584      */
20585     lastText : "Last Page",
20586     /**
20587      * Customizable piece of the default paging text (defaults to "Refresh")
20588      * @type String
20589      */
20590     refreshText : "Refresh",
20591
20592     buttons : false,
20593     // private
20594     onRender : function(ct, position) 
20595     {
20596         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
20597         this.navgroup.parentId = this.id;
20598         this.navgroup.onRender(this.el, null);
20599         // add the buttons to the navgroup
20600         
20601         if(this.displayInfo){
20602             Roo.log(this.el.select('ul.navbar-nav',true).first());
20603             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
20604             this.displayEl = this.el.select('.x-paging-info', true).first();
20605 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
20606 //            this.displayEl = navel.el.select('span',true).first();
20607         }
20608         
20609         var _this = this;
20610         
20611         if(this.buttons){
20612             Roo.each(_this.buttons, function(e){
20613                Roo.factory(e).onRender(_this.el, null);
20614             });
20615         }
20616             
20617         Roo.each(_this.toolbarItems, function(e) {
20618             _this.navgroup.addItem(e);
20619         });
20620         
20621         
20622         this.first = this.navgroup.addItem({
20623             tooltip: this.firstText,
20624             cls: "prev",
20625             icon : 'fa fa-backward',
20626             disabled: true,
20627             preventDefault: true,
20628             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
20629         });
20630         
20631         this.prev =  this.navgroup.addItem({
20632             tooltip: this.prevText,
20633             cls: "prev",
20634             icon : 'fa fa-step-backward',
20635             disabled: true,
20636             preventDefault: true,
20637             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
20638         });
20639     //this.addSeparator();
20640         
20641         
20642         var field = this.navgroup.addItem( {
20643             tagtype : 'span',
20644             cls : 'x-paging-position',
20645             
20646             html : this.beforePageText  +
20647                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
20648                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
20649          } ); //?? escaped?
20650         
20651         this.field = field.el.select('input', true).first();
20652         this.field.on("keydown", this.onPagingKeydown, this);
20653         this.field.on("focus", function(){this.dom.select();});
20654     
20655     
20656         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
20657         //this.field.setHeight(18);
20658         //this.addSeparator();
20659         this.next = this.navgroup.addItem({
20660             tooltip: this.nextText,
20661             cls: "next",
20662             html : ' <i class="fa fa-step-forward">',
20663             disabled: true,
20664             preventDefault: true,
20665             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
20666         });
20667         this.last = this.navgroup.addItem({
20668             tooltip: this.lastText,
20669             icon : 'fa fa-forward',
20670             cls: "next",
20671             disabled: true,
20672             preventDefault: true,
20673             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
20674         });
20675     //this.addSeparator();
20676         this.loading = this.navgroup.addItem({
20677             tooltip: this.refreshText,
20678             icon: 'fa fa-refresh',
20679             preventDefault: true,
20680             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
20681         });
20682
20683     },
20684
20685     // private
20686     updateInfo : function(){
20687         if(this.displayEl){
20688             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
20689             var msg = count == 0 ?
20690                 this.emptyMsg :
20691                 String.format(
20692                     this.displayMsg,
20693                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
20694                 );
20695             this.displayEl.update(msg);
20696         }
20697     },
20698
20699     // private
20700     onLoad : function(ds, r, o){
20701        this.cursor = o.params ? o.params.start : 0;
20702        var d = this.getPageData(),
20703             ap = d.activePage,
20704             ps = d.pages;
20705         
20706        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
20707        this.field.dom.value = ap;
20708        this.first.setDisabled(ap == 1);
20709        this.prev.setDisabled(ap == 1);
20710        this.next.setDisabled(ap == ps);
20711        this.last.setDisabled(ap == ps);
20712        this.loading.enable();
20713        this.updateInfo();
20714     },
20715
20716     // private
20717     getPageData : function(){
20718         var total = this.ds.getTotalCount();
20719         return {
20720             total : total,
20721             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
20722             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
20723         };
20724     },
20725
20726     // private
20727     onLoadError : function(){
20728         this.loading.enable();
20729     },
20730
20731     // private
20732     onPagingKeydown : function(e){
20733         var k = e.getKey();
20734         var d = this.getPageData();
20735         if(k == e.RETURN){
20736             var v = this.field.dom.value, pageNum;
20737             if(!v || isNaN(pageNum = parseInt(v, 10))){
20738                 this.field.dom.value = d.activePage;
20739                 return;
20740             }
20741             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
20742             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20743             e.stopEvent();
20744         }
20745         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))
20746         {
20747           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
20748           this.field.dom.value = pageNum;
20749           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
20750           e.stopEvent();
20751         }
20752         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20753         {
20754           var v = this.field.dom.value, pageNum; 
20755           var increment = (e.shiftKey) ? 10 : 1;
20756           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20757             increment *= -1;
20758           if(!v || isNaN(pageNum = parseInt(v, 10))) {
20759             this.field.dom.value = d.activePage;
20760             return;
20761           }
20762           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
20763           {
20764             this.field.dom.value = parseInt(v, 10) + increment;
20765             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
20766             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20767           }
20768           e.stopEvent();
20769         }
20770     },
20771
20772     // private
20773     beforeLoad : function(){
20774         if(this.loading){
20775             this.loading.disable();
20776         }
20777     },
20778
20779     // private
20780     onClick : function(which){
20781         
20782         var ds = this.ds;
20783         if (!ds) {
20784             return;
20785         }
20786         
20787         switch(which){
20788             case "first":
20789                 ds.load({params:{start: 0, limit: this.pageSize}});
20790             break;
20791             case "prev":
20792                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
20793             break;
20794             case "next":
20795                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
20796             break;
20797             case "last":
20798                 var total = ds.getTotalCount();
20799                 var extra = total % this.pageSize;
20800                 var lastStart = extra ? (total - extra) : total-this.pageSize;
20801                 ds.load({params:{start: lastStart, limit: this.pageSize}});
20802             break;
20803             case "refresh":
20804                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
20805             break;
20806         }
20807     },
20808
20809     /**
20810      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
20811      * @param {Roo.data.Store} store The data store to unbind
20812      */
20813     unbind : function(ds){
20814         ds.un("beforeload", this.beforeLoad, this);
20815         ds.un("load", this.onLoad, this);
20816         ds.un("loadexception", this.onLoadError, this);
20817         ds.un("remove", this.updateInfo, this);
20818         ds.un("add", this.updateInfo, this);
20819         this.ds = undefined;
20820     },
20821
20822     /**
20823      * Binds the paging toolbar to the specified {@link Roo.data.Store}
20824      * @param {Roo.data.Store} store The data store to bind
20825      */
20826     bind : function(ds){
20827         ds.on("beforeload", this.beforeLoad, this);
20828         ds.on("load", this.onLoad, this);
20829         ds.on("loadexception", this.onLoadError, this);
20830         ds.on("remove", this.updateInfo, this);
20831         ds.on("add", this.updateInfo, this);
20832         this.ds = ds;
20833     }
20834 });/*
20835  * - LGPL
20836  *
20837  * element
20838  * 
20839  */
20840
20841 /**
20842  * @class Roo.bootstrap.MessageBar
20843  * @extends Roo.bootstrap.Component
20844  * Bootstrap MessageBar class
20845  * @cfg {String} html contents of the MessageBar
20846  * @cfg {String} weight (info | success | warning | danger) default info
20847  * @cfg {String} beforeClass insert the bar before the given class
20848  * @cfg {Boolean} closable (true | false) default false
20849  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
20850  * 
20851  * @constructor
20852  * Create a new Element
20853  * @param {Object} config The config object
20854  */
20855
20856 Roo.bootstrap.MessageBar = function(config){
20857     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
20858 };
20859
20860 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
20861     
20862     html: '',
20863     weight: 'info',
20864     closable: false,
20865     fixed: false,
20866     beforeClass: 'bootstrap-sticky-wrap',
20867     
20868     getAutoCreate : function(){
20869         
20870         var cfg = {
20871             tag: 'div',
20872             cls: 'alert alert-dismissable alert-' + this.weight,
20873             cn: [
20874                 {
20875                     tag: 'span',
20876                     cls: 'message',
20877                     html: this.html || ''
20878                 }
20879             ]
20880         }
20881         
20882         if(this.fixed){
20883             cfg.cls += ' alert-messages-fixed';
20884         }
20885         
20886         if(this.closable){
20887             cfg.cn.push({
20888                 tag: 'button',
20889                 cls: 'close',
20890                 html: 'x'
20891             });
20892         }
20893         
20894         return cfg;
20895     },
20896     
20897     onRender : function(ct, position)
20898     {
20899         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20900         
20901         if(!this.el){
20902             var cfg = Roo.apply({},  this.getAutoCreate());
20903             cfg.id = Roo.id();
20904             
20905             if (this.cls) {
20906                 cfg.cls += ' ' + this.cls;
20907             }
20908             if (this.style) {
20909                 cfg.style = this.style;
20910             }
20911             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
20912             
20913             this.el.setVisibilityMode(Roo.Element.DISPLAY);
20914         }
20915         
20916         this.el.select('>button.close').on('click', this.hide, this);
20917         
20918     },
20919     
20920     show : function()
20921     {
20922         if (!this.rendered) {
20923             this.render();
20924         }
20925         
20926         this.el.show();
20927         
20928         this.fireEvent('show', this);
20929         
20930     },
20931     
20932     hide : function()
20933     {
20934         if (!this.rendered) {
20935             this.render();
20936         }
20937         
20938         this.el.hide();
20939         
20940         this.fireEvent('hide', this);
20941     },
20942     
20943     update : function()
20944     {
20945 //        var e = this.el.dom.firstChild;
20946 //        
20947 //        if(this.closable){
20948 //            e = e.nextSibling;
20949 //        }
20950 //        
20951 //        e.data = this.html || '';
20952
20953         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
20954     }
20955    
20956 });
20957
20958  
20959
20960      /*
20961  * - LGPL
20962  *
20963  * Graph
20964  * 
20965  */
20966
20967
20968 /**
20969  * @class Roo.bootstrap.Graph
20970  * @extends Roo.bootstrap.Component
20971  * Bootstrap Graph class
20972 > Prameters
20973  -sm {number} sm 4
20974  -md {number} md 5
20975  @cfg {String} graphtype  bar | vbar | pie
20976  @cfg {number} g_x coodinator | centre x (pie)
20977  @cfg {number} g_y coodinator | centre y (pie)
20978  @cfg {number} g_r radius (pie)
20979  @cfg {number} g_height height of the chart (respected by all elements in the set)
20980  @cfg {number} g_width width of the chart (respected by all elements in the set)
20981  @cfg {Object} title The title of the chart
20982     
20983  -{Array}  values
20984  -opts (object) options for the chart 
20985      o {
20986      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
20987      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
20988      o vgutter (number)
20989      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.
20990      o stacked (boolean) whether or not to tread values as in a stacked bar chart
20991      o to
20992      o stretch (boolean)
20993      o }
20994  -opts (object) options for the pie
20995      o{
20996      o cut
20997      o startAngle (number)
20998      o endAngle (number)
20999      } 
21000  *
21001  * @constructor
21002  * Create a new Input
21003  * @param {Object} config The config object
21004  */
21005
21006 Roo.bootstrap.Graph = function(config){
21007     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
21008     
21009     this.addEvents({
21010         // img events
21011         /**
21012          * @event click
21013          * The img click event for the img.
21014          * @param {Roo.EventObject} e
21015          */
21016         "click" : true
21017     });
21018 };
21019
21020 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
21021     
21022     sm: 4,
21023     md: 5,
21024     graphtype: 'bar',
21025     g_height: 250,
21026     g_width: 400,
21027     g_x: 50,
21028     g_y: 50,
21029     g_r: 30,
21030     opts:{
21031         //g_colors: this.colors,
21032         g_type: 'soft',
21033         g_gutter: '20%'
21034
21035     },
21036     title : false,
21037
21038     getAutoCreate : function(){
21039         
21040         var cfg = {
21041             tag: 'div',
21042             html : null
21043         }
21044         
21045         
21046         return  cfg;
21047     },
21048
21049     onRender : function(ct,position){
21050         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
21051         this.raphael = Raphael(this.el.dom);
21052         
21053                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21054                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21055                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21056                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
21057                 /*
21058                 r.text(160, 10, "Single Series Chart").attr(txtattr);
21059                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
21060                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
21061                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
21062                 
21063                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
21064                 r.barchart(330, 10, 300, 220, data1);
21065                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
21066                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
21067                 */
21068                 
21069                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21070                 // r.barchart(30, 30, 560, 250,  xdata, {
21071                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
21072                 //     axis : "0 0 1 1",
21073                 //     axisxlabels :  xdata
21074                 //     //yvalues : cols,
21075                    
21076                 // });
21077 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21078 //        
21079 //        this.load(null,xdata,{
21080 //                axis : "0 0 1 1",
21081 //                axisxlabels :  xdata
21082 //                });
21083
21084     },
21085
21086     load : function(graphtype,xdata,opts){
21087         this.raphael.clear();
21088         if(!graphtype) {
21089             graphtype = this.graphtype;
21090         }
21091         if(!opts){
21092             opts = this.opts;
21093         }
21094         var r = this.raphael,
21095             fin = function () {
21096                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
21097             },
21098             fout = function () {
21099                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
21100             },
21101             pfin = function() {
21102                 this.sector.stop();
21103                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
21104
21105                 if (this.label) {
21106                     this.label[0].stop();
21107                     this.label[0].attr({ r: 7.5 });
21108                     this.label[1].attr({ "font-weight": 800 });
21109                 }
21110             },
21111             pfout = function() {
21112                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
21113
21114                 if (this.label) {
21115                     this.label[0].animate({ r: 5 }, 500, "bounce");
21116                     this.label[1].attr({ "font-weight": 400 });
21117                 }
21118             };
21119
21120         switch(graphtype){
21121             case 'bar':
21122                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21123                 break;
21124             case 'hbar':
21125                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21126                 break;
21127             case 'pie':
21128 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
21129 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
21130 //            
21131                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
21132                 
21133                 break;
21134
21135         }
21136         
21137         if(this.title){
21138             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
21139         }
21140         
21141     },
21142     
21143     setTitle: function(o)
21144     {
21145         this.title = o;
21146     },
21147     
21148     initEvents: function() {
21149         
21150         if(!this.href){
21151             this.el.on('click', this.onClick, this);
21152         }
21153     },
21154     
21155     onClick : function(e)
21156     {
21157         Roo.log('img onclick');
21158         this.fireEvent('click', this, e);
21159     }
21160    
21161 });
21162
21163  
21164 /*
21165  * - LGPL
21166  *
21167  * numberBox
21168  * 
21169  */
21170 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21171
21172 /**
21173  * @class Roo.bootstrap.dash.NumberBox
21174  * @extends Roo.bootstrap.Component
21175  * Bootstrap NumberBox class
21176  * @cfg {String} headline Box headline
21177  * @cfg {String} content Box content
21178  * @cfg {String} icon Box icon
21179  * @cfg {String} footer Footer text
21180  * @cfg {String} fhref Footer href
21181  * 
21182  * @constructor
21183  * Create a new NumberBox
21184  * @param {Object} config The config object
21185  */
21186
21187
21188 Roo.bootstrap.dash.NumberBox = function(config){
21189     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
21190     
21191 };
21192
21193 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
21194     
21195     headline : '',
21196     content : '',
21197     icon : '',
21198     footer : '',
21199     fhref : '',
21200     ficon : '',
21201     
21202     getAutoCreate : function(){
21203         
21204         var cfg = {
21205             tag : 'div',
21206             cls : 'small-box ',
21207             cn : [
21208                 {
21209                     tag : 'div',
21210                     cls : 'inner',
21211                     cn :[
21212                         {
21213                             tag : 'h3',
21214                             cls : 'roo-headline',
21215                             html : this.headline
21216                         },
21217                         {
21218                             tag : 'p',
21219                             cls : 'roo-content',
21220                             html : this.content
21221                         }
21222                     ]
21223                 }
21224             ]
21225         }
21226         
21227         if(this.icon){
21228             cfg.cn.push({
21229                 tag : 'div',
21230                 cls : 'icon',
21231                 cn :[
21232                     {
21233                         tag : 'i',
21234                         cls : 'ion ' + this.icon
21235                     }
21236                 ]
21237             });
21238         }
21239         
21240         if(this.footer){
21241             var footer = {
21242                 tag : 'a',
21243                 cls : 'small-box-footer',
21244                 href : this.fhref || '#',
21245                 html : this.footer
21246             };
21247             
21248             cfg.cn.push(footer);
21249             
21250         }
21251         
21252         return  cfg;
21253     },
21254
21255     onRender : function(ct,position){
21256         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
21257
21258
21259        
21260                 
21261     },
21262
21263     setHeadline: function (value)
21264     {
21265         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
21266     },
21267     
21268     setFooter: function (value, href)
21269     {
21270         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
21271         
21272         if(href){
21273             this.el.select('a.small-box-footer',true).first().attr('href', href);
21274         }
21275         
21276     },
21277
21278     setContent: function (value)
21279     {
21280         this.el.select('.roo-content',true).first().dom.innerHTML = value;
21281     },
21282
21283     initEvents: function() 
21284     {   
21285         
21286     }
21287     
21288 });
21289
21290  
21291 /*
21292  * - LGPL
21293  *
21294  * TabBox
21295  * 
21296  */
21297 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21298
21299 /**
21300  * @class Roo.bootstrap.dash.TabBox
21301  * @extends Roo.bootstrap.Component
21302  * Bootstrap TabBox class
21303  * @cfg {String} title Title of the TabBox
21304  * @cfg {String} icon Icon of the TabBox
21305  * @cfg {Boolean} showtabs (true|false) show the tabs default true
21306  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
21307  * 
21308  * @constructor
21309  * Create a new TabBox
21310  * @param {Object} config The config object
21311  */
21312
21313
21314 Roo.bootstrap.dash.TabBox = function(config){
21315     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
21316     this.addEvents({
21317         // raw events
21318         /**
21319          * @event addpane
21320          * When a pane is added
21321          * @param {Roo.bootstrap.dash.TabPane} pane
21322          */
21323         "addpane" : true,
21324         /**
21325          * @event activatepane
21326          * When a pane is activated
21327          * @param {Roo.bootstrap.dash.TabPane} pane
21328          */
21329         "activatepane" : true
21330         
21331          
21332     });
21333     
21334     this.panes = [];
21335 };
21336
21337 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
21338
21339     title : '',
21340     icon : false,
21341     showtabs : true,
21342     tabScrollable : false,
21343     
21344     getChildContainer : function()
21345     {
21346         return this.el.select('.tab-content', true).first();
21347     },
21348     
21349     getAutoCreate : function(){
21350         
21351         var header = {
21352             tag: 'li',
21353             cls: 'pull-left header',
21354             html: this.title,
21355             cn : []
21356         };
21357         
21358         if(this.icon){
21359             header.cn.push({
21360                 tag: 'i',
21361                 cls: 'fa ' + this.icon
21362             });
21363         }
21364         
21365         var h = {
21366             tag: 'ul',
21367             cls: 'nav nav-tabs pull-right',
21368             cn: [
21369                 header
21370             ]
21371         };
21372         
21373         if(this.tabScrollable){
21374             h = {
21375                 tag: 'div',
21376                 cls: 'tab-header',
21377                 cn: [
21378                     {
21379                         tag: 'ul',
21380                         cls: 'nav nav-tabs pull-right',
21381                         cn: [
21382                             header
21383                         ]
21384                     }
21385                 ]
21386             }
21387         }
21388         
21389         var cfg = {
21390             tag: 'div',
21391             cls: 'nav-tabs-custom',
21392             cn: [
21393                 h,
21394                 {
21395                     tag: 'div',
21396                     cls: 'tab-content no-padding',
21397                     cn: []
21398                 }
21399             ]
21400         }
21401
21402         return  cfg;
21403     },
21404     initEvents : function()
21405     {
21406         //Roo.log('add add pane handler');
21407         this.on('addpane', this.onAddPane, this);
21408     },
21409      /**
21410      * Updates the box title
21411      * @param {String} html to set the title to.
21412      */
21413     setTitle : function(value)
21414     {
21415         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
21416     },
21417     onAddPane : function(pane)
21418     {
21419         this.panes.push(pane);
21420         //Roo.log('addpane');
21421         //Roo.log(pane);
21422         // tabs are rendere left to right..
21423         if(!this.showtabs){
21424             return;
21425         }
21426         
21427         var ctr = this.el.select('.nav-tabs', true).first();
21428          
21429          
21430         var existing = ctr.select('.nav-tab',true);
21431         var qty = existing.getCount();;
21432         
21433         
21434         var tab = ctr.createChild({
21435             tag : 'li',
21436             cls : 'nav-tab' + (qty ? '' : ' active'),
21437             cn : [
21438                 {
21439                     tag : 'a',
21440                     href:'#',
21441                     html : pane.title
21442                 }
21443             ]
21444         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
21445         pane.tab = tab;
21446         
21447         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
21448         if (!qty) {
21449             pane.el.addClass('active');
21450         }
21451         
21452                 
21453     },
21454     onTabClick : function(ev,un,ob,pane)
21455     {
21456         //Roo.log('tab - prev default');
21457         ev.preventDefault();
21458         
21459         
21460         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
21461         pane.tab.addClass('active');
21462         //Roo.log(pane.title);
21463         this.getChildContainer().select('.tab-pane',true).removeClass('active');
21464         // technically we should have a deactivate event.. but maybe add later.
21465         // and it should not de-activate the selected tab...
21466         this.fireEvent('activatepane', pane);
21467         pane.el.addClass('active');
21468         pane.fireEvent('activate');
21469         
21470         
21471     },
21472     
21473     getActivePane : function()
21474     {
21475         var r = false;
21476         Roo.each(this.panes, function(p) {
21477             if(p.el.hasClass('active')){
21478                 r = p;
21479                 return false;
21480             }
21481             
21482             return;
21483         });
21484         
21485         return r;
21486     }
21487     
21488     
21489 });
21490
21491  
21492 /*
21493  * - LGPL
21494  *
21495  * Tab pane
21496  * 
21497  */
21498 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21499 /**
21500  * @class Roo.bootstrap.TabPane
21501  * @extends Roo.bootstrap.Component
21502  * Bootstrap TabPane class
21503  * @cfg {Boolean} active (false | true) Default false
21504  * @cfg {String} title title of panel
21505
21506  * 
21507  * @constructor
21508  * Create a new TabPane
21509  * @param {Object} config The config object
21510  */
21511
21512 Roo.bootstrap.dash.TabPane = function(config){
21513     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
21514     
21515     this.addEvents({
21516         // raw events
21517         /**
21518          * @event activate
21519          * When a pane is activated
21520          * @param {Roo.bootstrap.dash.TabPane} pane
21521          */
21522         "activate" : true
21523          
21524     });
21525 };
21526
21527 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
21528     
21529     active : false,
21530     title : '',
21531     
21532     // the tabBox that this is attached to.
21533     tab : false,
21534      
21535     getAutoCreate : function() 
21536     {
21537         var cfg = {
21538             tag: 'div',
21539             cls: 'tab-pane'
21540         }
21541         
21542         if(this.active){
21543             cfg.cls += ' active';
21544         }
21545         
21546         return cfg;
21547     },
21548     initEvents  : function()
21549     {
21550         //Roo.log('trigger add pane handler');
21551         this.parent().fireEvent('addpane', this)
21552     },
21553     
21554      /**
21555      * Updates the tab title 
21556      * @param {String} html to set the title to.
21557      */
21558     setTitle: function(str)
21559     {
21560         if (!this.tab) {
21561             return;
21562         }
21563         this.title = str;
21564         this.tab.select('a', true).first().dom.innerHTML = str;
21565         
21566     }
21567     
21568     
21569     
21570 });
21571
21572  
21573
21574
21575  /*
21576  * - LGPL
21577  *
21578  * menu
21579  * 
21580  */
21581 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21582
21583 /**
21584  * @class Roo.bootstrap.menu.Menu
21585  * @extends Roo.bootstrap.Component
21586  * Bootstrap Menu class - container for Menu
21587  * @cfg {String} html Text of the menu
21588  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
21589  * @cfg {String} icon Font awesome icon
21590  * @cfg {String} pos Menu align to (top | bottom) default bottom
21591  * 
21592  * 
21593  * @constructor
21594  * Create a new Menu
21595  * @param {Object} config The config object
21596  */
21597
21598
21599 Roo.bootstrap.menu.Menu = function(config){
21600     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
21601     
21602     this.addEvents({
21603         /**
21604          * @event beforeshow
21605          * Fires before this menu is displayed
21606          * @param {Roo.bootstrap.menu.Menu} this
21607          */
21608         beforeshow : true,
21609         /**
21610          * @event beforehide
21611          * Fires before this menu is hidden
21612          * @param {Roo.bootstrap.menu.Menu} this
21613          */
21614         beforehide : true,
21615         /**
21616          * @event show
21617          * Fires after this menu is displayed
21618          * @param {Roo.bootstrap.menu.Menu} this
21619          */
21620         show : true,
21621         /**
21622          * @event hide
21623          * Fires after this menu is hidden
21624          * @param {Roo.bootstrap.menu.Menu} this
21625          */
21626         hide : true,
21627         /**
21628          * @event click
21629          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
21630          * @param {Roo.bootstrap.menu.Menu} this
21631          * @param {Roo.EventObject} e
21632          */
21633         click : true
21634     });
21635     
21636 };
21637
21638 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
21639     
21640     submenu : false,
21641     html : '',
21642     weight : 'default',
21643     icon : false,
21644     pos : 'bottom',
21645     
21646     
21647     getChildContainer : function() {
21648         if(this.isSubMenu){
21649             return this.el;
21650         }
21651         
21652         return this.el.select('ul.dropdown-menu', true).first();  
21653     },
21654     
21655     getAutoCreate : function()
21656     {
21657         var text = [
21658             {
21659                 tag : 'span',
21660                 cls : 'roo-menu-text',
21661                 html : this.html
21662             }
21663         ];
21664         
21665         if(this.icon){
21666             text.unshift({
21667                 tag : 'i',
21668                 cls : 'fa ' + this.icon
21669             })
21670         }
21671         
21672         
21673         var cfg = {
21674             tag : 'div',
21675             cls : 'btn-group',
21676             cn : [
21677                 {
21678                     tag : 'button',
21679                     cls : 'dropdown-button btn btn-' + this.weight,
21680                     cn : text
21681                 },
21682                 {
21683                     tag : 'button',
21684                     cls : 'dropdown-toggle btn btn-' + this.weight,
21685                     cn : [
21686                         {
21687                             tag : 'span',
21688                             cls : 'caret'
21689                         }
21690                     ]
21691                 },
21692                 {
21693                     tag : 'ul',
21694                     cls : 'dropdown-menu'
21695                 }
21696             ]
21697             
21698         };
21699         
21700         if(this.pos == 'top'){
21701             cfg.cls += ' dropup';
21702         }
21703         
21704         if(this.isSubMenu){
21705             cfg = {
21706                 tag : 'ul',
21707                 cls : 'dropdown-menu'
21708             }
21709         }
21710         
21711         return cfg;
21712     },
21713     
21714     onRender : function(ct, position)
21715     {
21716         this.isSubMenu = ct.hasClass('dropdown-submenu');
21717         
21718         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
21719     },
21720     
21721     initEvents : function() 
21722     {
21723         if(this.isSubMenu){
21724             return;
21725         }
21726         
21727         this.hidden = true;
21728         
21729         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
21730         this.triggerEl.on('click', this.onTriggerPress, this);
21731         
21732         this.buttonEl = this.el.select('button.dropdown-button', true).first();
21733         this.buttonEl.on('click', this.onClick, this);
21734         
21735     },
21736     
21737     list : function()
21738     {
21739         if(this.isSubMenu){
21740             return this.el;
21741         }
21742         
21743         return this.el.select('ul.dropdown-menu', true).first();
21744     },
21745     
21746     onClick : function(e)
21747     {
21748         this.fireEvent("click", this, e);
21749     },
21750     
21751     onTriggerPress  : function(e)
21752     {   
21753         if (this.isVisible()) {
21754             this.hide();
21755         } else {
21756             this.show();
21757         }
21758     },
21759     
21760     isVisible : function(){
21761         return !this.hidden;
21762     },
21763     
21764     show : function()
21765     {
21766         this.fireEvent("beforeshow", this);
21767         
21768         this.hidden = false;
21769         this.el.addClass('open');
21770         
21771         Roo.get(document).on("mouseup", this.onMouseUp, this);
21772         
21773         this.fireEvent("show", this);
21774         
21775         
21776     },
21777     
21778     hide : function()
21779     {
21780         this.fireEvent("beforehide", this);
21781         
21782         this.hidden = true;
21783         this.el.removeClass('open');
21784         
21785         Roo.get(document).un("mouseup", this.onMouseUp);
21786         
21787         this.fireEvent("hide", this);
21788     },
21789     
21790     onMouseUp : function()
21791     {
21792         this.hide();
21793     }
21794     
21795 });
21796
21797  
21798  /*
21799  * - LGPL
21800  *
21801  * menu item
21802  * 
21803  */
21804 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21805
21806 /**
21807  * @class Roo.bootstrap.menu.Item
21808  * @extends Roo.bootstrap.Component
21809  * Bootstrap MenuItem class
21810  * @cfg {Boolean} submenu (true | false) default false
21811  * @cfg {String} html text of the item
21812  * @cfg {String} href the link
21813  * @cfg {Boolean} disable (true | false) default false
21814  * @cfg {Boolean} preventDefault (true | false) default true
21815  * @cfg {String} icon Font awesome icon
21816  * @cfg {String} pos Submenu align to (left | right) default right 
21817  * 
21818  * 
21819  * @constructor
21820  * Create a new Item
21821  * @param {Object} config The config object
21822  */
21823
21824
21825 Roo.bootstrap.menu.Item = function(config){
21826     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
21827     this.addEvents({
21828         /**
21829          * @event mouseover
21830          * Fires when the mouse is hovering over this menu
21831          * @param {Roo.bootstrap.menu.Item} this
21832          * @param {Roo.EventObject} e
21833          */
21834         mouseover : true,
21835         /**
21836          * @event mouseout
21837          * Fires when the mouse exits this menu
21838          * @param {Roo.bootstrap.menu.Item} this
21839          * @param {Roo.EventObject} e
21840          */
21841         mouseout : true,
21842         // raw events
21843         /**
21844          * @event click
21845          * The raw click event for the entire grid.
21846          * @param {Roo.EventObject} e
21847          */
21848         click : true
21849     });
21850 };
21851
21852 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
21853     
21854     submenu : false,
21855     href : '',
21856     html : '',
21857     preventDefault: true,
21858     disable : false,
21859     icon : false,
21860     pos : 'right',
21861     
21862     getAutoCreate : function()
21863     {
21864         var text = [
21865             {
21866                 tag : 'span',
21867                 cls : 'roo-menu-item-text',
21868                 html : this.html
21869             }
21870         ];
21871         
21872         if(this.icon){
21873             text.unshift({
21874                 tag : 'i',
21875                 cls : 'fa ' + this.icon
21876             })
21877         }
21878         
21879         var cfg = {
21880             tag : 'li',
21881             cn : [
21882                 {
21883                     tag : 'a',
21884                     href : this.href || '#',
21885                     cn : text
21886                 }
21887             ]
21888         };
21889         
21890         if(this.disable){
21891             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
21892         }
21893         
21894         if(this.submenu){
21895             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
21896             
21897             if(this.pos == 'left'){
21898                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
21899             }
21900         }
21901         
21902         return cfg;
21903     },
21904     
21905     initEvents : function() 
21906     {
21907         this.el.on('mouseover', this.onMouseOver, this);
21908         this.el.on('mouseout', this.onMouseOut, this);
21909         
21910         this.el.select('a', true).first().on('click', this.onClick, this);
21911         
21912     },
21913     
21914     onClick : function(e)
21915     {
21916         if(this.preventDefault){
21917             e.preventDefault();
21918         }
21919         
21920         this.fireEvent("click", this, e);
21921     },
21922     
21923     onMouseOver : function(e)
21924     {
21925         if(this.submenu && this.pos == 'left'){
21926             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
21927         }
21928         
21929         this.fireEvent("mouseover", this, e);
21930     },
21931     
21932     onMouseOut : function(e)
21933     {
21934         this.fireEvent("mouseout", this, e);
21935     }
21936 });
21937
21938  
21939
21940  /*
21941  * - LGPL
21942  *
21943  * menu separator
21944  * 
21945  */
21946 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21947
21948 /**
21949  * @class Roo.bootstrap.menu.Separator
21950  * @extends Roo.bootstrap.Component
21951  * Bootstrap Separator class
21952  * 
21953  * @constructor
21954  * Create a new Separator
21955  * @param {Object} config The config object
21956  */
21957
21958
21959 Roo.bootstrap.menu.Separator = function(config){
21960     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
21961 };
21962
21963 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
21964     
21965     getAutoCreate : function(){
21966         var cfg = {
21967             tag : 'li',
21968             cls: 'divider'
21969         };
21970         
21971         return cfg;
21972     }
21973    
21974 });
21975
21976  
21977
21978  /*
21979  * - LGPL
21980  *
21981  * Tooltip
21982  * 
21983  */
21984
21985 /**
21986  * @class Roo.bootstrap.Tooltip
21987  * Bootstrap Tooltip class
21988  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
21989  * to determine which dom element triggers the tooltip.
21990  * 
21991  * It needs to add support for additional attributes like tooltip-position
21992  * 
21993  * @constructor
21994  * Create a new Toolti
21995  * @param {Object} config The config object
21996  */
21997
21998 Roo.bootstrap.Tooltip = function(config){
21999     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
22000 };
22001
22002 Roo.apply(Roo.bootstrap.Tooltip, {
22003     /**
22004      * @function init initialize tooltip monitoring.
22005      * @static
22006      */
22007     currentEl : false,
22008     currentTip : false,
22009     currentRegion : false,
22010     
22011     //  init : delay?
22012     
22013     init : function()
22014     {
22015         Roo.get(document).on('mouseover', this.enter ,this);
22016         Roo.get(document).on('mouseout', this.leave, this);
22017          
22018         
22019         this.currentTip = new Roo.bootstrap.Tooltip();
22020     },
22021     
22022     enter : function(ev)
22023     {
22024         var dom = ev.getTarget();
22025         
22026         //Roo.log(['enter',dom]);
22027         var el = Roo.fly(dom);
22028         if (this.currentEl) {
22029             //Roo.log(dom);
22030             //Roo.log(this.currentEl);
22031             //Roo.log(this.currentEl.contains(dom));
22032             if (this.currentEl == el) {
22033                 return;
22034             }
22035             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
22036                 return;
22037             }
22038
22039         }
22040         
22041         
22042         
22043         if (this.currentTip.el) {
22044             this.currentTip.el.hide(); // force hiding...
22045         }    
22046         //Roo.log(ev);
22047         var bindEl = el;
22048         
22049         // you can not look for children, as if el is the body.. then everythign is the child..
22050         if (!el.attr('tooltip')) { //
22051             if (!el.select("[tooltip]").elements.length) {
22052                 return;
22053             }
22054             // is the mouse over this child...?
22055             bindEl = el.select("[tooltip]").first();
22056             var xy = ev.getXY();
22057             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
22058                 //Roo.log("not in region.");
22059                 return;
22060             }
22061             //Roo.log("child element over..");
22062             
22063         }
22064         this.currentEl = bindEl;
22065         this.currentTip.bind(bindEl);
22066         this.currentRegion = Roo.lib.Region.getRegion(dom);
22067         this.currentTip.enter();
22068         
22069     },
22070     leave : function(ev)
22071     {
22072         var dom = ev.getTarget();
22073         //Roo.log(['leave',dom]);
22074         if (!this.currentEl) {
22075             return;
22076         }
22077         
22078         
22079         if (dom != this.currentEl.dom) {
22080             return;
22081         }
22082         var xy = ev.getXY();
22083         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
22084             return;
22085         }
22086         // only activate leave if mouse cursor is outside... bounding box..
22087         
22088         
22089         
22090         
22091         if (this.currentTip) {
22092             this.currentTip.leave();
22093         }
22094         //Roo.log('clear currentEl');
22095         this.currentEl = false;
22096         
22097         
22098     },
22099     alignment : {
22100         'left' : ['r-l', [-2,0], 'right'],
22101         'right' : ['l-r', [2,0], 'left'],
22102         'bottom' : ['t-b', [0,2], 'top'],
22103         'top' : [ 'b-t', [0,-2], 'bottom']
22104     }
22105     
22106 });
22107
22108
22109 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
22110     
22111     
22112     bindEl : false,
22113     
22114     delay : null, // can be { show : 300 , hide: 500}
22115     
22116     timeout : null,
22117     
22118     hoverState : null, //???
22119     
22120     placement : 'bottom', 
22121     
22122     getAutoCreate : function(){
22123     
22124         var cfg = {
22125            cls : 'tooltip',
22126            role : 'tooltip',
22127            cn : [
22128                 {
22129                     cls : 'tooltip-arrow'
22130                 },
22131                 {
22132                     cls : 'tooltip-inner'
22133                 }
22134            ]
22135         };
22136         
22137         return cfg;
22138     },
22139     bind : function(el)
22140     {
22141         this.bindEl = el;
22142     },
22143       
22144     
22145     enter : function () {
22146        
22147         if (this.timeout != null) {
22148             clearTimeout(this.timeout);
22149         }
22150         
22151         this.hoverState = 'in';
22152          //Roo.log("enter - show");
22153         if (!this.delay || !this.delay.show) {
22154             this.show();
22155             return;
22156         }
22157         var _t = this;
22158         this.timeout = setTimeout(function () {
22159             if (_t.hoverState == 'in') {
22160                 _t.show();
22161             }
22162         }, this.delay.show);
22163     },
22164     leave : function()
22165     {
22166         clearTimeout(this.timeout);
22167     
22168         this.hoverState = 'out';
22169          if (!this.delay || !this.delay.hide) {
22170             this.hide();
22171             return;
22172         }
22173        
22174         var _t = this;
22175         this.timeout = setTimeout(function () {
22176             //Roo.log("leave - timeout");
22177             
22178             if (_t.hoverState == 'out') {
22179                 _t.hide();
22180                 Roo.bootstrap.Tooltip.currentEl = false;
22181             }
22182         }, delay);
22183     },
22184     
22185     show : function ()
22186     {
22187         if (!this.el) {
22188             this.render(document.body);
22189         }
22190         // set content.
22191         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
22192         
22193         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
22194         
22195         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
22196         
22197         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
22198         
22199         var placement = typeof this.placement == 'function' ?
22200             this.placement.call(this, this.el, on_el) :
22201             this.placement;
22202             
22203         var autoToken = /\s?auto?\s?/i;
22204         var autoPlace = autoToken.test(placement);
22205         if (autoPlace) {
22206             placement = placement.replace(autoToken, '') || 'top';
22207         }
22208         
22209         //this.el.detach()
22210         //this.el.setXY([0,0]);
22211         this.el.show();
22212         //this.el.dom.style.display='block';
22213         this.el.addClass(placement);
22214         
22215         //this.el.appendTo(on_el);
22216         
22217         var p = this.getPosition();
22218         var box = this.el.getBox();
22219         
22220         if (autoPlace) {
22221             // fixme..
22222         }
22223         var align = Roo.bootstrap.Tooltip.alignment[placement];
22224         this.el.alignTo(this.bindEl, align[0],align[1]);
22225         //var arrow = this.el.select('.arrow',true).first();
22226         //arrow.set(align[2], 
22227         
22228         this.el.addClass('in fade');
22229         this.hoverState = null;
22230         
22231         if (this.el.hasClass('fade')) {
22232             // fade it?
22233         }
22234         
22235     },
22236     hide : function()
22237     {
22238          
22239         if (!this.el) {
22240             return;
22241         }
22242         //this.el.setXY([0,0]);
22243         this.el.removeClass('in');
22244         //this.el.hide();
22245         
22246     }
22247     
22248 });
22249  
22250
22251  /*
22252  * - LGPL
22253  *
22254  * Location Picker
22255  * 
22256  */
22257
22258 /**
22259  * @class Roo.bootstrap.LocationPicker
22260  * @extends Roo.bootstrap.Component
22261  * Bootstrap LocationPicker class
22262  * @cfg {Number} latitude Position when init default 0
22263  * @cfg {Number} longitude Position when init default 0
22264  * @cfg {Number} zoom default 15
22265  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
22266  * @cfg {Boolean} mapTypeControl default false
22267  * @cfg {Boolean} disableDoubleClickZoom default false
22268  * @cfg {Boolean} scrollwheel default true
22269  * @cfg {Boolean} streetViewControl default false
22270  * @cfg {Number} radius default 0
22271  * @cfg {String} locationName
22272  * @cfg {Boolean} draggable default true
22273  * @cfg {Boolean} enableAutocomplete default false
22274  * @cfg {Boolean} enableReverseGeocode default true
22275  * @cfg {String} markerTitle
22276  * 
22277  * @constructor
22278  * Create a new LocationPicker
22279  * @param {Object} config The config object
22280  */
22281
22282
22283 Roo.bootstrap.LocationPicker = function(config){
22284     
22285     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
22286     
22287     this.addEvents({
22288         /**
22289          * @event initial
22290          * Fires when the picker initialized.
22291          * @param {Roo.bootstrap.LocationPicker} this
22292          * @param {Google Location} location
22293          */
22294         initial : true,
22295         /**
22296          * @event positionchanged
22297          * Fires when the picker position changed.
22298          * @param {Roo.bootstrap.LocationPicker} this
22299          * @param {Google Location} location
22300          */
22301         positionchanged : true,
22302         /**
22303          * @event resize
22304          * Fires when the map resize.
22305          * @param {Roo.bootstrap.LocationPicker} this
22306          */
22307         resize : true,
22308         /**
22309          * @event show
22310          * Fires when the map show.
22311          * @param {Roo.bootstrap.LocationPicker} this
22312          */
22313         show : true,
22314         /**
22315          * @event hide
22316          * Fires when the map hide.
22317          * @param {Roo.bootstrap.LocationPicker} this
22318          */
22319         hide : true,
22320         /**
22321          * @event mapClick
22322          * Fires when click the map.
22323          * @param {Roo.bootstrap.LocationPicker} this
22324          * @param {Map event} e
22325          */
22326         mapClick : true,
22327         /**
22328          * @event mapRightClick
22329          * Fires when right click the map.
22330          * @param {Roo.bootstrap.LocationPicker} this
22331          * @param {Map event} e
22332          */
22333         mapRightClick : true,
22334         /**
22335          * @event markerClick
22336          * Fires when click the marker.
22337          * @param {Roo.bootstrap.LocationPicker} this
22338          * @param {Map event} e
22339          */
22340         markerClick : true,
22341         /**
22342          * @event markerRightClick
22343          * Fires when right click the marker.
22344          * @param {Roo.bootstrap.LocationPicker} this
22345          * @param {Map event} e
22346          */
22347         markerRightClick : true,
22348         /**
22349          * @event OverlayViewDraw
22350          * Fires when OverlayView Draw
22351          * @param {Roo.bootstrap.LocationPicker} this
22352          */
22353         OverlayViewDraw : true,
22354         /**
22355          * @event OverlayViewOnAdd
22356          * Fires when OverlayView Draw
22357          * @param {Roo.bootstrap.LocationPicker} this
22358          */
22359         OverlayViewOnAdd : true,
22360         /**
22361          * @event OverlayViewOnRemove
22362          * Fires when OverlayView Draw
22363          * @param {Roo.bootstrap.LocationPicker} this
22364          */
22365         OverlayViewOnRemove : true,
22366         /**
22367          * @event OverlayViewShow
22368          * Fires when OverlayView Draw
22369          * @param {Roo.bootstrap.LocationPicker} this
22370          * @param {Pixel} cpx
22371          */
22372         OverlayViewShow : true,
22373         /**
22374          * @event OverlayViewHide
22375          * Fires when OverlayView Draw
22376          * @param {Roo.bootstrap.LocationPicker} this
22377          */
22378         OverlayViewHide : true
22379     });
22380         
22381 };
22382
22383 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
22384     
22385     gMapContext: false,
22386     
22387     latitude: 0,
22388     longitude: 0,
22389     zoom: 15,
22390     mapTypeId: false,
22391     mapTypeControl: false,
22392     disableDoubleClickZoom: false,
22393     scrollwheel: true,
22394     streetViewControl: false,
22395     radius: 0,
22396     locationName: '',
22397     draggable: true,
22398     enableAutocomplete: false,
22399     enableReverseGeocode: true,
22400     markerTitle: '',
22401     
22402     getAutoCreate: function()
22403     {
22404
22405         var cfg = {
22406             tag: 'div',
22407             cls: 'roo-location-picker'
22408         };
22409         
22410         return cfg
22411     },
22412     
22413     initEvents: function(ct, position)
22414     {       
22415         if(!this.el.getWidth() || this.isApplied()){
22416             return;
22417         }
22418         
22419         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22420         
22421         this.initial();
22422     },
22423     
22424     initial: function()
22425     {
22426         if(!this.mapTypeId){
22427             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
22428         }
22429         
22430         this.gMapContext = this.GMapContext();
22431         
22432         this.initOverlayView();
22433         
22434         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
22435         
22436         var _this = this;
22437                 
22438         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
22439             _this.setPosition(_this.gMapContext.marker.position);
22440         });
22441         
22442         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
22443             _this.fireEvent('mapClick', this, event);
22444             
22445         });
22446
22447         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
22448             _this.fireEvent('mapRightClick', this, event);
22449             
22450         });
22451         
22452         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
22453             _this.fireEvent('markerClick', this, event);
22454             
22455         });
22456
22457         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
22458             _this.fireEvent('markerRightClick', this, event);
22459             
22460         });
22461         
22462         this.setPosition(this.gMapContext.location);
22463         
22464         this.fireEvent('initial', this, this.gMapContext.location);
22465     },
22466     
22467     initOverlayView: function()
22468     {
22469         var _this = this;
22470         
22471         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
22472             
22473             draw: function()
22474             {
22475                 _this.fireEvent('OverlayViewDraw', _this);
22476             },
22477             
22478             onAdd: function()
22479             {
22480                 _this.fireEvent('OverlayViewOnAdd', _this);
22481             },
22482             
22483             onRemove: function()
22484             {
22485                 _this.fireEvent('OverlayViewOnRemove', _this);
22486             },
22487             
22488             show: function(cpx)
22489             {
22490                 _this.fireEvent('OverlayViewShow', _this, cpx);
22491             },
22492             
22493             hide: function()
22494             {
22495                 _this.fireEvent('OverlayViewHide', _this);
22496             }
22497             
22498         });
22499     },
22500     
22501     fromLatLngToContainerPixel: function(event)
22502     {
22503         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
22504     },
22505     
22506     isApplied: function() 
22507     {
22508         return this.getGmapContext() == false ? false : true;
22509     },
22510     
22511     getGmapContext: function() 
22512     {
22513         return this.gMapContext
22514     },
22515     
22516     GMapContext: function() 
22517     {
22518         var position = new google.maps.LatLng(this.latitude, this.longitude);
22519         
22520         var _map = new google.maps.Map(this.el.dom, {
22521             center: position,
22522             zoom: this.zoom,
22523             mapTypeId: this.mapTypeId,
22524             mapTypeControl: this.mapTypeControl,
22525             disableDoubleClickZoom: this.disableDoubleClickZoom,
22526             scrollwheel: this.scrollwheel,
22527             streetViewControl: this.streetViewControl,
22528             locationName: this.locationName,
22529             draggable: this.draggable,
22530             enableAutocomplete: this.enableAutocomplete,
22531             enableReverseGeocode: this.enableReverseGeocode
22532         });
22533         
22534         var _marker = new google.maps.Marker({
22535             position: position,
22536             map: _map,
22537             title: this.markerTitle,
22538             draggable: this.draggable
22539         });
22540         
22541         return {
22542             map: _map,
22543             marker: _marker,
22544             circle: null,
22545             location: position,
22546             radius: this.radius,
22547             locationName: this.locationName,
22548             addressComponents: {
22549                 formatted_address: null,
22550                 addressLine1: null,
22551                 addressLine2: null,
22552                 streetName: null,
22553                 streetNumber: null,
22554                 city: null,
22555                 district: null,
22556                 state: null,
22557                 stateOrProvince: null
22558             },
22559             settings: this,
22560             domContainer: this.el.dom,
22561             geodecoder: new google.maps.Geocoder()
22562         };
22563     },
22564     
22565     drawCircle: function(center, radius, options) 
22566     {
22567         if (this.gMapContext.circle != null) {
22568             this.gMapContext.circle.setMap(null);
22569         }
22570         if (radius > 0) {
22571             radius *= 1;
22572             options = Roo.apply({}, options, {
22573                 strokeColor: "#0000FF",
22574                 strokeOpacity: .35,
22575                 strokeWeight: 2,
22576                 fillColor: "#0000FF",
22577                 fillOpacity: .2
22578             });
22579             
22580             options.map = this.gMapContext.map;
22581             options.radius = radius;
22582             options.center = center;
22583             this.gMapContext.circle = new google.maps.Circle(options);
22584             return this.gMapContext.circle;
22585         }
22586         
22587         return null;
22588     },
22589     
22590     setPosition: function(location) 
22591     {
22592         this.gMapContext.location = location;
22593         this.gMapContext.marker.setPosition(location);
22594         this.gMapContext.map.panTo(location);
22595         this.drawCircle(location, this.gMapContext.radius, {});
22596         
22597         var _this = this;
22598         
22599         if (this.gMapContext.settings.enableReverseGeocode) {
22600             this.gMapContext.geodecoder.geocode({
22601                 latLng: this.gMapContext.location
22602             }, function(results, status) {
22603                 
22604                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
22605                     _this.gMapContext.locationName = results[0].formatted_address;
22606                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
22607                     
22608                     _this.fireEvent('positionchanged', this, location);
22609                 }
22610             });
22611             
22612             return;
22613         }
22614         
22615         this.fireEvent('positionchanged', this, location);
22616     },
22617     
22618     resize: function()
22619     {
22620         google.maps.event.trigger(this.gMapContext.map, "resize");
22621         
22622         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
22623         
22624         this.fireEvent('resize', this);
22625     },
22626     
22627     setPositionByLatLng: function(latitude, longitude)
22628     {
22629         this.setPosition(new google.maps.LatLng(latitude, longitude));
22630     },
22631     
22632     getCurrentPosition: function() 
22633     {
22634         return {
22635             latitude: this.gMapContext.location.lat(),
22636             longitude: this.gMapContext.location.lng()
22637         };
22638     },
22639     
22640     getAddressName: function() 
22641     {
22642         return this.gMapContext.locationName;
22643     },
22644     
22645     getAddressComponents: function() 
22646     {
22647         return this.gMapContext.addressComponents;
22648     },
22649     
22650     address_component_from_google_geocode: function(address_components) 
22651     {
22652         var result = {};
22653         
22654         for (var i = 0; i < address_components.length; i++) {
22655             var component = address_components[i];
22656             if (component.types.indexOf("postal_code") >= 0) {
22657                 result.postalCode = component.short_name;
22658             } else if (component.types.indexOf("street_number") >= 0) {
22659                 result.streetNumber = component.short_name;
22660             } else if (component.types.indexOf("route") >= 0) {
22661                 result.streetName = component.short_name;
22662             } else if (component.types.indexOf("neighborhood") >= 0) {
22663                 result.city = component.short_name;
22664             } else if (component.types.indexOf("locality") >= 0) {
22665                 result.city = component.short_name;
22666             } else if (component.types.indexOf("sublocality") >= 0) {
22667                 result.district = component.short_name;
22668             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
22669                 result.stateOrProvince = component.short_name;
22670             } else if (component.types.indexOf("country") >= 0) {
22671                 result.country = component.short_name;
22672             }
22673         }
22674         
22675         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
22676         result.addressLine2 = "";
22677         return result;
22678     },
22679     
22680     setZoomLevel: function(zoom)
22681     {
22682         this.gMapContext.map.setZoom(zoom);
22683     },
22684     
22685     show: function()
22686     {
22687         if(!this.el){
22688             return;
22689         }
22690         
22691         this.el.show();
22692         
22693         this.resize();
22694         
22695         this.fireEvent('show', this);
22696     },
22697     
22698     hide: function()
22699     {
22700         if(!this.el){
22701             return;
22702         }
22703         
22704         this.el.hide();
22705         
22706         this.fireEvent('hide', this);
22707     }
22708     
22709 });
22710
22711 Roo.apply(Roo.bootstrap.LocationPicker, {
22712     
22713     OverlayView : function(map, options)
22714     {
22715         options = options || {};
22716         
22717         this.setMap(map);
22718     }
22719     
22720     
22721 });/*
22722  * - LGPL
22723  *
22724  * Alert
22725  * 
22726  */
22727
22728 /**
22729  * @class Roo.bootstrap.Alert
22730  * @extends Roo.bootstrap.Component
22731  * Bootstrap Alert class
22732  * @cfg {String} title The title of alert
22733  * @cfg {String} html The content of alert
22734  * @cfg {String} weight (  success | info | warning | danger )
22735  * @cfg {String} faicon font-awesomeicon
22736  * 
22737  * @constructor
22738  * Create a new alert
22739  * @param {Object} config The config object
22740  */
22741
22742
22743 Roo.bootstrap.Alert = function(config){
22744     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
22745     
22746 };
22747
22748 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
22749     
22750     title: '',
22751     html: '',
22752     weight: false,
22753     faicon: false,
22754     
22755     getAutoCreate : function()
22756     {
22757         
22758         var cfg = {
22759             tag : 'div',
22760             cls : 'alert',
22761             cn : [
22762                 {
22763                     tag : 'i',
22764                     cls : 'roo-alert-icon'
22765                     
22766                 },
22767                 {
22768                     tag : 'b',
22769                     cls : 'roo-alert-title',
22770                     html : this.title
22771                 },
22772                 {
22773                     tag : 'span',
22774                     cls : 'roo-alert-text',
22775                     html : this.html
22776                 }
22777             ]
22778         };
22779         
22780         if(this.faicon){
22781             cfg.cn[0].cls += ' fa ' + this.faicon;
22782         }
22783         
22784         if(this.weight){
22785             cfg.cls += ' alert-' + this.weight;
22786         }
22787         
22788         return cfg;
22789     },
22790     
22791     initEvents: function() 
22792     {
22793         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22794     },
22795     
22796     setTitle : function(str)
22797     {
22798         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
22799     },
22800     
22801     setText : function(str)
22802     {
22803         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
22804     },
22805     
22806     setWeight : function(weight)
22807     {
22808         if(this.weight){
22809             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
22810         }
22811         
22812         this.weight = weight;
22813         
22814         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
22815     },
22816     
22817     setIcon : function(icon)
22818     {
22819         if(this.faicon){
22820             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
22821         }
22822         
22823         this.faicon = icon
22824         
22825         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
22826     },
22827     
22828     hide: function() 
22829     {
22830         this.el.hide();   
22831     },
22832     
22833     show: function() 
22834     {  
22835         this.el.show();   
22836     }
22837     
22838 });
22839
22840