Roo/bootstrap/NavItem.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             //xy = this.el.adjustForConstraints(xy);
1846         }
1847         
1848         //this.el.show();
1849         this.hideMenuItems();
1850         this.hidden = false;
1851         this.triggerEl.addClass('open');
1852         
1853         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
1854             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
1855         }
1856         
1857         this.el.setXY(xy);
1858         this.focus();
1859         this.fireEvent("show", this);
1860     },
1861     
1862     focus : function(){
1863         return;
1864         if(!this.hidden){
1865             this.doFocus.defer(50, this);
1866         }
1867     },
1868
1869     doFocus : function(){
1870         if(!this.hidden){
1871             this.focusEl.focus();
1872         }
1873     },
1874
1875     /**
1876      * Hides this menu and optionally all parent menus
1877      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1878      */
1879     hide : function(deep){
1880         
1881         this.hideMenuItems();
1882         if(this.el && this.isVisible()){
1883             this.fireEvent("beforehide", this);
1884             if(this.activeItem){
1885                 this.activeItem.deactivate();
1886                 this.activeItem = null;
1887             }
1888             this.triggerEl.removeClass('open');;
1889             this.hidden = true;
1890             this.fireEvent("hide", this);
1891         }
1892         if(deep === true && this.parentMenu){
1893             this.parentMenu.hide(true);
1894         }
1895     },
1896     
1897     onTriggerPress  : function(e)
1898     {
1899         
1900         Roo.log('trigger press');
1901         //Roo.log(e.getTarget());
1902        // Roo.log(this.triggerEl.dom);
1903         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1904             return;
1905         }
1906         if (this.isVisible()) {
1907             Roo.log('hide');
1908             this.hide();
1909         } else {
1910             this.show(this.triggerEl, false, false);
1911         }
1912         
1913         
1914     },
1915     
1916          
1917        
1918     
1919     hideMenuItems : function()
1920     {
1921         //$(backdrop).remove()
1922         Roo.select('.open',true).each(function(aa) {
1923             
1924             aa.removeClass('open');
1925           //var parent = getParent($(this))
1926           //var relatedTarget = { relatedTarget: this }
1927           
1928            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1929           //if (e.isDefaultPrevented()) return
1930            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1931         })
1932     },
1933     addxtypeChild : function (tree, cntr) {
1934         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1935           
1936         this.menuitems.add(comp);
1937         return comp;
1938
1939     },
1940     getEl : function()
1941     {
1942         Roo.log(this.el);
1943         return this.el;
1944     }
1945 });
1946
1947  
1948  /*
1949  * - LGPL
1950  *
1951  * menu item
1952  * 
1953  */
1954
1955
1956 /**
1957  * @class Roo.bootstrap.MenuItem
1958  * @extends Roo.bootstrap.Component
1959  * Bootstrap MenuItem class
1960  * @cfg {String} html the menu label
1961  * @cfg {String} href the link
1962  * @cfg {Boolean} preventDefault (true | false) default true
1963  * @cfg {Boolean} isContainer (true | false) default false
1964  * 
1965  * 
1966  * @constructor
1967  * Create a new MenuItem
1968  * @param {Object} config The config object
1969  */
1970
1971
1972 Roo.bootstrap.MenuItem = function(config){
1973     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1974     this.addEvents({
1975         // raw events
1976         /**
1977          * @event click
1978          * The raw click event for the entire grid.
1979          * @param {Roo.bootstrap.MenuItem} this
1980          * @param {Roo.EventObject} e
1981          */
1982         "click" : true
1983     });
1984 };
1985
1986 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1987     
1988     href : false,
1989     html : false,
1990     preventDefault: true,
1991     isContainer : false,
1992     
1993     getAutoCreate : function(){
1994         
1995         if(this.isContainer){
1996             return {
1997                 tag: 'li',
1998                 cls: 'dropdown-menu-item'
1999             };
2000         }
2001         
2002         var cfg= {
2003             tag: 'li',
2004             cls: 'dropdown-menu-item',
2005             cn: [
2006                     {
2007                         tag : 'a',
2008                         href : '#',
2009                         html : 'Link'
2010                     }
2011                 ]
2012         };
2013         if (this.parent().type == 'treeview') {
2014             cfg.cls = 'treeview-menu';
2015         }
2016         
2017         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2018         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2019         return cfg;
2020     },
2021     
2022     initEvents: function() {
2023         
2024         //this.el.select('a').on('click', this.onClick, this);
2025         
2026     },
2027     onClick : function(e)
2028     {
2029         Roo.log('item on click ');
2030         //if(this.preventDefault){
2031         //    e.preventDefault();
2032         //}
2033         //this.parent().hideMenuItems();
2034         
2035         this.fireEvent('click', this, e);
2036     },
2037     getEl : function()
2038     {
2039         return this.el;
2040     }
2041 });
2042
2043  
2044
2045  /*
2046  * - LGPL
2047  *
2048  * menu separator
2049  * 
2050  */
2051
2052
2053 /**
2054  * @class Roo.bootstrap.MenuSeparator
2055  * @extends Roo.bootstrap.Component
2056  * Bootstrap MenuSeparator class
2057  * 
2058  * @constructor
2059  * Create a new MenuItem
2060  * @param {Object} config The config object
2061  */
2062
2063
2064 Roo.bootstrap.MenuSeparator = function(config){
2065     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2066 };
2067
2068 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2069     
2070     getAutoCreate : function(){
2071         var cfg = {
2072             cls: 'divider',
2073             tag : 'li'
2074         };
2075         
2076         return cfg;
2077     }
2078    
2079 });
2080
2081  
2082
2083  
2084 /*
2085 * Licence: LGPL
2086 */
2087
2088 /**
2089  * @class Roo.bootstrap.Modal
2090  * @extends Roo.bootstrap.Component
2091  * Bootstrap Modal class
2092  * @cfg {String} title Title of dialog
2093  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2094  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2095  * @cfg {Boolean} specificTitle default false
2096  * @cfg {Array} buttons Array of buttons or standard button set..
2097  * @cfg {String} buttonPosition (left|right|center) default right
2098  * @cfg {Boolean} animate default true
2099  * @cfg {Boolean} allow_close default true
2100  * 
2101  * @constructor
2102  * Create a new Modal Dialog
2103  * @param {Object} config The config object
2104  */
2105
2106 Roo.bootstrap.Modal = function(config){
2107     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2108     this.addEvents({
2109         // raw events
2110         /**
2111          * @event btnclick
2112          * The raw btnclick event for the button
2113          * @param {Roo.EventObject} e
2114          */
2115         "btnclick" : true
2116     });
2117     this.buttons = this.buttons || [];
2118      
2119     if (this.tmpl) {
2120         this.tmpl = Roo.factory(this.tmpl);
2121     }
2122     
2123 };
2124
2125 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2126     
2127     title : 'test dialog',
2128    
2129     buttons : false,
2130     
2131     // set on load...
2132      
2133     html: false,
2134     
2135     tmp: false,
2136     
2137     specificTitle: false,
2138     
2139     buttonPosition: 'right',
2140     
2141     allow_close : true,
2142     
2143     animate : true,
2144     
2145     
2146      // private
2147     bodyEl:  false,
2148     footerEl:  false,
2149     titleEl:  false,
2150     closeEl:  false,
2151     
2152     
2153     onRender : function(ct, position)
2154     {
2155         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2156      
2157         if(!this.el){
2158             var cfg = Roo.apply({},  this.getAutoCreate());
2159             cfg.id = Roo.id();
2160             //if(!cfg.name){
2161             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2162             //}
2163             //if (!cfg.name.length) {
2164             //    delete cfg.name;
2165            // }
2166             if (this.cls) {
2167                 cfg.cls += ' ' + this.cls;
2168             }
2169             if (this.style) {
2170                 cfg.style = this.style;
2171             }
2172             this.el = Roo.get(document.body).createChild(cfg, position);
2173         }
2174         //var type = this.el.dom.type;
2175         
2176         
2177         
2178         
2179         if(this.tabIndex !== undefined){
2180             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2181         }
2182         
2183         
2184         this.bodyEl = this.el.select('.modal-body',true).first();
2185         this.closeEl = this.el.select('.modal-header .close', true).first();
2186         this.footerEl = this.el.select('.modal-footer',true).first();
2187         this.titleEl = this.el.select('.modal-title',true).first();
2188         
2189         
2190          
2191         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2192         this.maskEl.enableDisplayMode("block");
2193         this.maskEl.hide();
2194         //this.el.addClass("x-dlg-modal");
2195     
2196         if (this.buttons.length) {
2197             Roo.each(this.buttons, function(bb) {
2198                 b = Roo.apply({}, bb);
2199                 b.xns = b.xns || Roo.bootstrap;
2200                 b.xtype = b.xtype || 'Button';
2201                 if (typeof(b.listeners) == 'undefined') {
2202                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2203                 }
2204                 
2205                 var btn = Roo.factory(b);
2206                 
2207                 btn.onRender(this.el.select('.modal-footer div').first());
2208                 
2209             },this);
2210         }
2211         // render the children.
2212         var nitems = [];
2213         
2214         if(typeof(this.items) != 'undefined'){
2215             var items = this.items;
2216             delete this.items;
2217
2218             for(var i =0;i < items.length;i++) {
2219                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2220             }
2221         }
2222         
2223         this.items = nitems;
2224         
2225         // where are these used - they used to be body/close/footer
2226         
2227        
2228         this.initEvents();
2229         //this.el.addClass([this.fieldClass, this.cls]);
2230         
2231     },
2232     getAutoCreate : function(){
2233         
2234         
2235         var bdy = {
2236                 cls : 'modal-body',
2237                 html : this.html || ''
2238         };
2239         
2240         var title = {
2241             tag: 'h4',
2242             cls : 'modal-title',
2243             html : this.title
2244         };
2245         
2246         if(this.specificTitle){
2247             title = this.title;
2248             
2249         };
2250         
2251         var header = [];
2252         if (this.allow_close) {
2253             header.push({
2254                 tag: 'button',
2255                 cls : 'close',
2256                 html : '&times'
2257             });
2258         }
2259         header.push(title);
2260         
2261         var modal = {
2262             cls: "modal",
2263             style : 'display: none',
2264             cn : [
2265                 {
2266                     cls: "modal-dialog",
2267                     cn : [
2268                         {
2269                             cls : "modal-content",
2270                             cn : [
2271                                 {
2272                                     cls : 'modal-header',
2273                                     cn : header
2274                                 },
2275                                 bdy,
2276                                 {
2277                                     cls : 'modal-footer',
2278                                     cn : [
2279                                         {
2280                                             tag: 'div',
2281                                             cls: 'btn-' + this.buttonPosition
2282                                         }
2283                                     ]
2284                                     
2285                                 }
2286                                 
2287                                 
2288                             ]
2289                             
2290                         }
2291                     ]
2292                         
2293                 }
2294             ]
2295         };
2296         
2297         if(this.animate){
2298             modal.cls += ' fade';
2299         }
2300         
2301         return modal;
2302           
2303     },
2304     getChildContainer : function() {
2305          
2306          return this.bodyEl;
2307         
2308     },
2309     getButtonContainer : function() {
2310          return this.el.select('.modal-footer div',true).first();
2311         
2312     },
2313     initEvents : function()
2314     {
2315         if (this.allow_close) {
2316             this.closeEl.on('click', this.hide, this);
2317         }
2318
2319     },
2320     show : function() {
2321         
2322         if (!this.rendered) {
2323             this.render();
2324         }
2325         
2326         this.el.setStyle('display', 'block');
2327         
2328         if(this.animate){
2329             var _this = this;
2330             (function(){ _this.el.addClass('in'); }).defer(50);
2331         }else{
2332             this.el.addClass('in');
2333         }
2334         
2335         // not sure how we can show data in here.. 
2336         //if (this.tmpl) {
2337         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2338         //}
2339         
2340         Roo.get(document.body).addClass("x-body-masked");
2341         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2342         this.maskEl.show();
2343         this.el.setStyle('zIndex', '10001');
2344        
2345         this.fireEvent('show', this);
2346         
2347         
2348     },
2349     hide : function()
2350     {
2351         this.maskEl.hide();
2352         Roo.get(document.body).removeClass("x-body-masked");
2353         this.el.removeClass('in');
2354         
2355         if(this.animate){
2356             var _this = this;
2357             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2358         }else{
2359             this.el.setStyle('display', 'none');
2360         }
2361         
2362         this.fireEvent('hide', this);
2363     },
2364     
2365     addButton : function(str, cb)
2366     {
2367          
2368         
2369         var b = Roo.apply({}, { html : str } );
2370         b.xns = b.xns || Roo.bootstrap;
2371         b.xtype = b.xtype || 'Button';
2372         if (typeof(b.listeners) == 'undefined') {
2373             b.listeners = { click : cb.createDelegate(this)  };
2374         }
2375         
2376         var btn = Roo.factory(b);
2377            
2378         btn.onRender(this.el.select('.modal-footer div').first());
2379         
2380         return btn;   
2381        
2382     },
2383     
2384     setDefaultButton : function(btn)
2385     {
2386         //this.el.select('.modal-footer').()
2387     },
2388     resizeTo: function(w,h)
2389     {
2390         // skip..
2391     },
2392     setContentSize  : function(w, h)
2393     {
2394         
2395     },
2396     onButtonClick: function(btn,e)
2397     {
2398         //Roo.log([a,b,c]);
2399         this.fireEvent('btnclick', btn.name, e);
2400     },
2401      /**
2402      * Set the title of the Dialog
2403      * @param {String} str new Title
2404      */
2405     setTitle: function(str) {
2406         this.titleEl.dom.innerHTML = str;    
2407     },
2408     /**
2409      * Set the body of the Dialog
2410      * @param {String} str new Title
2411      */
2412     setBody: function(str) {
2413         this.bodyEl.dom.innerHTML = str;    
2414     },
2415     /**
2416      * Set the body of the Dialog using the template
2417      * @param {Obj} data - apply this data to the template and replace the body contents.
2418      */
2419     applyBody: function(obj)
2420     {
2421         if (!this.tmpl) {
2422             Roo.log("Error - using apply Body without a template");
2423             //code
2424         }
2425         this.tmpl.overwrite(this.bodyEl, obj);
2426     }
2427     
2428 });
2429
2430
2431 Roo.apply(Roo.bootstrap.Modal,  {
2432     /**
2433          * Button config that displays a single OK button
2434          * @type Object
2435          */
2436         OK :  [{
2437             name : 'ok',
2438             weight : 'primary',
2439             html : 'OK'
2440         }], 
2441         /**
2442          * Button config that displays Yes and No buttons
2443          * @type Object
2444          */
2445         YESNO : [
2446             {
2447                 name  : 'no',
2448                 html : 'No'
2449             },
2450             {
2451                 name  :'yes',
2452                 weight : 'primary',
2453                 html : 'Yes'
2454             }
2455         ],
2456         
2457         /**
2458          * Button config that displays OK and Cancel buttons
2459          * @type Object
2460          */
2461         OKCANCEL : [
2462             {
2463                name : 'cancel',
2464                 html : 'Cancel'
2465             },
2466             {
2467                 name : 'ok',
2468                 weight : 'primary',
2469                 html : 'OK'
2470             }
2471         ],
2472         /**
2473          * Button config that displays Yes, No and Cancel buttons
2474          * @type Object
2475          */
2476         YESNOCANCEL : [
2477             {
2478                 name : 'yes',
2479                 weight : 'primary',
2480                 html : 'Yes'
2481             },
2482             {
2483                 name : 'no',
2484                 html : 'No'
2485             },
2486             {
2487                 name : 'cancel',
2488                 html : 'Cancel'
2489             }
2490         ]
2491 });
2492  
2493  /*
2494  * - LGPL
2495  *
2496  * messagebox - can be used as a replace
2497  * 
2498  */
2499 /**
2500  * @class Roo.MessageBox
2501  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2502  * Example usage:
2503  *<pre><code>
2504 // Basic alert:
2505 Roo.Msg.alert('Status', 'Changes saved successfully.');
2506
2507 // Prompt for user data:
2508 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2509     if (btn == 'ok'){
2510         // process text value...
2511     }
2512 });
2513
2514 // Show a dialog using config options:
2515 Roo.Msg.show({
2516    title:'Save Changes?',
2517    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2518    buttons: Roo.Msg.YESNOCANCEL,
2519    fn: processResult,
2520    animEl: 'elId'
2521 });
2522 </code></pre>
2523  * @singleton
2524  */
2525 Roo.bootstrap.MessageBox = function(){
2526     var dlg, opt, mask, waitTimer;
2527     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2528     var buttons, activeTextEl, bwidth;
2529
2530     
2531     // private
2532     var handleButton = function(button){
2533         dlg.hide();
2534         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2535     };
2536
2537     // private
2538     var handleHide = function(){
2539         if(opt && opt.cls){
2540             dlg.el.removeClass(opt.cls);
2541         }
2542         //if(waitTimer){
2543         //    Roo.TaskMgr.stop(waitTimer);
2544         //    waitTimer = null;
2545         //}
2546     };
2547
2548     // private
2549     var updateButtons = function(b){
2550         var width = 0;
2551         if(!b){
2552             buttons["ok"].hide();
2553             buttons["cancel"].hide();
2554             buttons["yes"].hide();
2555             buttons["no"].hide();
2556             //dlg.footer.dom.style.display = 'none';
2557             return width;
2558         }
2559         dlg.footerEl.dom.style.display = '';
2560         for(var k in buttons){
2561             if(typeof buttons[k] != "function"){
2562                 if(b[k]){
2563                     buttons[k].show();
2564                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2565                     width += buttons[k].el.getWidth()+15;
2566                 }else{
2567                     buttons[k].hide();
2568                 }
2569             }
2570         }
2571         return width;
2572     };
2573
2574     // private
2575     var handleEsc = function(d, k, e){
2576         if(opt && opt.closable !== false){
2577             dlg.hide();
2578         }
2579         if(e){
2580             e.stopEvent();
2581         }
2582     };
2583
2584     return {
2585         /**
2586          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2587          * @return {Roo.BasicDialog} The BasicDialog element
2588          */
2589         getDialog : function(){
2590            if(!dlg){
2591                 dlg = new Roo.bootstrap.Modal( {
2592                     //draggable: true,
2593                     //resizable:false,
2594                     //constraintoviewport:false,
2595                     //fixedcenter:true,
2596                     //collapsible : false,
2597                     //shim:true,
2598                     //modal: true,
2599                   //  width:400,
2600                   //  height:100,
2601                     //buttonAlign:"center",
2602                     closeClick : function(){
2603                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2604                             handleButton("no");
2605                         }else{
2606                             handleButton("cancel");
2607                         }
2608                     }
2609                 });
2610                 dlg.render();
2611                 dlg.on("hide", handleHide);
2612                 mask = dlg.mask;
2613                 //dlg.addKeyListener(27, handleEsc);
2614                 buttons = {};
2615                 this.buttons = buttons;
2616                 var bt = this.buttonText;
2617                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2618                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2619                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2620                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2621                 Roo.log(buttons)
2622                 bodyEl = dlg.bodyEl.createChild({
2623
2624                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2625                         '<textarea class="roo-mb-textarea"></textarea>' +
2626                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2627                 });
2628                 msgEl = bodyEl.dom.firstChild;
2629                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2630                 textboxEl.enableDisplayMode();
2631                 textboxEl.addKeyListener([10,13], function(){
2632                     if(dlg.isVisible() && opt && opt.buttons){
2633                         if(opt.buttons.ok){
2634                             handleButton("ok");
2635                         }else if(opt.buttons.yes){
2636                             handleButton("yes");
2637                         }
2638                     }
2639                 });
2640                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2641                 textareaEl.enableDisplayMode();
2642                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2643                 progressEl.enableDisplayMode();
2644                 var pf = progressEl.dom.firstChild;
2645                 if (pf) {
2646                     pp = Roo.get(pf.firstChild);
2647                     pp.setHeight(pf.offsetHeight);
2648                 }
2649                 
2650             }
2651             return dlg;
2652         },
2653
2654         /**
2655          * Updates the message box body text
2656          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2657          * the XHTML-compliant non-breaking space character '&amp;#160;')
2658          * @return {Roo.MessageBox} This message box
2659          */
2660         updateText : function(text){
2661             if(!dlg.isVisible() && !opt.width){
2662                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2663             }
2664             msgEl.innerHTML = text || '&#160;';
2665       
2666             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2667             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2668             var w = Math.max(
2669                     Math.min(opt.width || cw , this.maxWidth), 
2670                     Math.max(opt.minWidth || this.minWidth, bwidth)
2671             );
2672             if(opt.prompt){
2673                 activeTextEl.setWidth(w);
2674             }
2675             if(dlg.isVisible()){
2676                 dlg.fixedcenter = false;
2677             }
2678             // to big, make it scroll. = But as usual stupid IE does not support
2679             // !important..
2680             
2681             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2682                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2683                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2684             } else {
2685                 bodyEl.dom.style.height = '';
2686                 bodyEl.dom.style.overflowY = '';
2687             }
2688             if (cw > w) {
2689                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2690             } else {
2691                 bodyEl.dom.style.overflowX = '';
2692             }
2693             
2694             dlg.setContentSize(w, bodyEl.getHeight());
2695             if(dlg.isVisible()){
2696                 dlg.fixedcenter = true;
2697             }
2698             return this;
2699         },
2700
2701         /**
2702          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2703          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2704          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2705          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2706          * @return {Roo.MessageBox} This message box
2707          */
2708         updateProgress : function(value, text){
2709             if(text){
2710                 this.updateText(text);
2711             }
2712             if (pp) { // weird bug on my firefox - for some reason this is not defined
2713                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2714             }
2715             return this;
2716         },        
2717
2718         /**
2719          * Returns true if the message box is currently displayed
2720          * @return {Boolean} True if the message box is visible, else false
2721          */
2722         isVisible : function(){
2723             return dlg && dlg.isVisible();  
2724         },
2725
2726         /**
2727          * Hides the message box if it is displayed
2728          */
2729         hide : function(){
2730             if(this.isVisible()){
2731                 dlg.hide();
2732             }  
2733         },
2734
2735         /**
2736          * Displays a new message box, or reinitializes an existing message box, based on the config options
2737          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2738          * The following config object properties are supported:
2739          * <pre>
2740 Property    Type             Description
2741 ----------  ---------------  ------------------------------------------------------------------------------------
2742 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2743                                    closes (defaults to undefined)
2744 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2745                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2746 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2747                                    progress and wait dialogs will ignore this property and always hide the
2748                                    close button as they can only be closed programmatically.
2749 cls               String           A custom CSS class to apply to the message box element
2750 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2751                                    displayed (defaults to 75)
2752 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2753                                    function will be btn (the name of the button that was clicked, if applicable,
2754                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2755                                    Progress and wait dialogs will ignore this option since they do not respond to
2756                                    user actions and can only be closed programmatically, so any required function
2757                                    should be called by the same code after it closes the dialog.
2758 icon              String           A CSS class that provides a background image to be used as an icon for
2759                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2760 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2761 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2762 modal             Boolean          False to allow user interaction with the page while the message box is
2763                                    displayed (defaults to true)
2764 msg               String           A string that will replace the existing message box body text (defaults
2765                                    to the XHTML-compliant non-breaking space character '&#160;')
2766 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2767 progress          Boolean          True to display a progress bar (defaults to false)
2768 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2769 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2770 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2771 title             String           The title text
2772 value             String           The string value to set into the active textbox element if displayed
2773 wait              Boolean          True to display a progress bar (defaults to false)
2774 width             Number           The width of the dialog in pixels
2775 </pre>
2776          *
2777          * Example usage:
2778          * <pre><code>
2779 Roo.Msg.show({
2780    title: 'Address',
2781    msg: 'Please enter your address:',
2782    width: 300,
2783    buttons: Roo.MessageBox.OKCANCEL,
2784    multiline: true,
2785    fn: saveAddress,
2786    animEl: 'addAddressBtn'
2787 });
2788 </code></pre>
2789          * @param {Object} config Configuration options
2790          * @return {Roo.MessageBox} This message box
2791          */
2792         show : function(options)
2793         {
2794             
2795             // this causes nightmares if you show one dialog after another
2796             // especially on callbacks..
2797              
2798             if(this.isVisible()){
2799                 
2800                 this.hide();
2801                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2802                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2803                 Roo.log("New Dialog Message:" +  options.msg )
2804                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2805                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2806                 
2807             }
2808             var d = this.getDialog();
2809             opt = options;
2810             d.setTitle(opt.title || "&#160;");
2811             d.closeEl.setDisplayed(opt.closable !== false);
2812             activeTextEl = textboxEl;
2813             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2814             if(opt.prompt){
2815                 if(opt.multiline){
2816                     textboxEl.hide();
2817                     textareaEl.show();
2818                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2819                         opt.multiline : this.defaultTextHeight);
2820                     activeTextEl = textareaEl;
2821                 }else{
2822                     textboxEl.show();
2823                     textareaEl.hide();
2824                 }
2825             }else{
2826                 textboxEl.hide();
2827                 textareaEl.hide();
2828             }
2829             progressEl.setDisplayed(opt.progress === true);
2830             this.updateProgress(0);
2831             activeTextEl.dom.value = opt.value || "";
2832             if(opt.prompt){
2833                 dlg.setDefaultButton(activeTextEl);
2834             }else{
2835                 var bs = opt.buttons;
2836                 var db = null;
2837                 if(bs && bs.ok){
2838                     db = buttons["ok"];
2839                 }else if(bs && bs.yes){
2840                     db = buttons["yes"];
2841                 }
2842                 dlg.setDefaultButton(db);
2843             }
2844             bwidth = updateButtons(opt.buttons);
2845             this.updateText(opt.msg);
2846             if(opt.cls){
2847                 d.el.addClass(opt.cls);
2848             }
2849             d.proxyDrag = opt.proxyDrag === true;
2850             d.modal = opt.modal !== false;
2851             d.mask = opt.modal !== false ? mask : false;
2852             if(!d.isVisible()){
2853                 // force it to the end of the z-index stack so it gets a cursor in FF
2854                 document.body.appendChild(dlg.el.dom);
2855                 d.animateTarget = null;
2856                 d.show(options.animEl);
2857             }
2858             return this;
2859         },
2860
2861         /**
2862          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2863          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2864          * and closing the message box when the process is complete.
2865          * @param {String} title The title bar text
2866          * @param {String} msg The message box body text
2867          * @return {Roo.MessageBox} This message box
2868          */
2869         progress : function(title, msg){
2870             this.show({
2871                 title : title,
2872                 msg : msg,
2873                 buttons: false,
2874                 progress:true,
2875                 closable:false,
2876                 minWidth: this.minProgressWidth,
2877                 modal : true
2878             });
2879             return this;
2880         },
2881
2882         /**
2883          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2884          * If a callback function is passed it will be called after the user clicks the button, and the
2885          * id of the button that was clicked will be passed as the only parameter to the callback
2886          * (could also be the top-right close button).
2887          * @param {String} title The title bar text
2888          * @param {String} msg The message box body text
2889          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2890          * @param {Object} scope (optional) The scope of the callback function
2891          * @return {Roo.MessageBox} This message box
2892          */
2893         alert : function(title, msg, fn, scope){
2894             this.show({
2895                 title : title,
2896                 msg : msg,
2897                 buttons: this.OK,
2898                 fn: fn,
2899                 scope : scope,
2900                 modal : true
2901             });
2902             return this;
2903         },
2904
2905         /**
2906          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2907          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2908          * You are responsible for closing the message box when the process is complete.
2909          * @param {String} msg The message box body text
2910          * @param {String} title (optional) The title bar text
2911          * @return {Roo.MessageBox} This message box
2912          */
2913         wait : function(msg, title){
2914             this.show({
2915                 title : title,
2916                 msg : msg,
2917                 buttons: false,
2918                 closable:false,
2919                 progress:true,
2920                 modal:true,
2921                 width:300,
2922                 wait:true
2923             });
2924             waitTimer = Roo.TaskMgr.start({
2925                 run: function(i){
2926                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2927                 },
2928                 interval: 1000
2929             });
2930             return this;
2931         },
2932
2933         /**
2934          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2935          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2936          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2937          * @param {String} title The title bar text
2938          * @param {String} msg The message box body text
2939          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2940          * @param {Object} scope (optional) The scope of the callback function
2941          * @return {Roo.MessageBox} This message box
2942          */
2943         confirm : function(title, msg, fn, scope){
2944             this.show({
2945                 title : title,
2946                 msg : msg,
2947                 buttons: this.YESNO,
2948                 fn: fn,
2949                 scope : scope,
2950                 modal : true
2951             });
2952             return this;
2953         },
2954
2955         /**
2956          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2957          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2958          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2959          * (could also be the top-right close button) and the text that was entered will be passed as the two
2960          * parameters to the callback.
2961          * @param {String} title The title bar text
2962          * @param {String} msg The message box body text
2963          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2964          * @param {Object} scope (optional) The scope of the callback function
2965          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2966          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2967          * @return {Roo.MessageBox} This message box
2968          */
2969         prompt : function(title, msg, fn, scope, multiline){
2970             this.show({
2971                 title : title,
2972                 msg : msg,
2973                 buttons: this.OKCANCEL,
2974                 fn: fn,
2975                 minWidth:250,
2976                 scope : scope,
2977                 prompt:true,
2978                 multiline: multiline,
2979                 modal : true
2980             });
2981             return this;
2982         },
2983
2984         /**
2985          * Button config that displays a single OK button
2986          * @type Object
2987          */
2988         OK : {ok:true},
2989         /**
2990          * Button config that displays Yes and No buttons
2991          * @type Object
2992          */
2993         YESNO : {yes:true, no:true},
2994         /**
2995          * Button config that displays OK and Cancel buttons
2996          * @type Object
2997          */
2998         OKCANCEL : {ok:true, cancel:true},
2999         /**
3000          * Button config that displays Yes, No and Cancel buttons
3001          * @type Object
3002          */
3003         YESNOCANCEL : {yes:true, no:true, cancel:true},
3004
3005         /**
3006          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3007          * @type Number
3008          */
3009         defaultTextHeight : 75,
3010         /**
3011          * The maximum width in pixels of the message box (defaults to 600)
3012          * @type Number
3013          */
3014         maxWidth : 600,
3015         /**
3016          * The minimum width in pixels of the message box (defaults to 100)
3017          * @type Number
3018          */
3019         minWidth : 100,
3020         /**
3021          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3022          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3023          * @type Number
3024          */
3025         minProgressWidth : 250,
3026         /**
3027          * An object containing the default button text strings that can be overriden for localized language support.
3028          * Supported properties are: ok, cancel, yes and no.
3029          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3030          * @type Object
3031          */
3032         buttonText : {
3033             ok : "OK",
3034             cancel : "Cancel",
3035             yes : "Yes",
3036             no : "No"
3037         }
3038     };
3039 }();
3040
3041 /**
3042  * Shorthand for {@link Roo.MessageBox}
3043  */
3044 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3045 Roo.Msg = Roo.Msg || Roo.MessageBox;
3046 /*
3047  * - LGPL
3048  *
3049  * navbar
3050  * 
3051  */
3052
3053 /**
3054  * @class Roo.bootstrap.Navbar
3055  * @extends Roo.bootstrap.Component
3056  * Bootstrap Navbar class
3057
3058  * @constructor
3059  * Create a new Navbar
3060  * @param {Object} config The config object
3061  */
3062
3063
3064 Roo.bootstrap.Navbar = function(config){
3065     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3066     
3067 };
3068
3069 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3070     
3071     
3072    
3073     // private
3074     navItems : false,
3075     loadMask : false,
3076     
3077     
3078     getAutoCreate : function(){
3079         
3080         
3081         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3082         
3083     },
3084     
3085     initEvents :function ()
3086     {
3087         //Roo.log(this.el.select('.navbar-toggle',true));
3088         this.el.select('.navbar-toggle',true).on('click', function() {
3089            // Roo.log('click');
3090             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3091         }, this);
3092         
3093         var mark = {
3094             tag: "div",
3095             cls:"x-dlg-mask"
3096         }
3097         
3098         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3099         
3100         var size = this.el.getSize();
3101         this.maskEl.setSize(size.width, size.height);
3102         this.maskEl.enableDisplayMode("block");
3103         this.maskEl.hide();
3104         
3105         if(this.loadMask){
3106             this.maskEl.show();
3107         }
3108     },
3109     
3110     
3111     getChildContainer : function()
3112     {
3113         if (this.el.select('.collapse').getCount()) {
3114             return this.el.select('.collapse',true).first();
3115         }
3116         
3117         return this.el;
3118     },
3119     
3120     mask : function()
3121     {
3122         this.maskEl.show();
3123     },
3124     
3125     unmask : function()
3126     {
3127         this.maskEl.hide();
3128     } 
3129     
3130     
3131     
3132     
3133 });
3134
3135
3136
3137  
3138
3139  /*
3140  * - LGPL
3141  *
3142  * navbar
3143  * 
3144  */
3145
3146 /**
3147  * @class Roo.bootstrap.NavSimplebar
3148  * @extends Roo.bootstrap.Navbar
3149  * Bootstrap Sidebar class
3150  *
3151  * @cfg {Boolean} inverse is inverted color
3152  * 
3153  * @cfg {String} type (nav | pills | tabs)
3154  * @cfg {Boolean} arrangement stacked | justified
3155  * @cfg {String} align (left | right) alignment
3156  * 
3157  * @cfg {Boolean} main (true|false) main nav bar? default false
3158  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3159  * 
3160  * @cfg {String} tag (header|footer|nav|div) default is nav 
3161
3162  * 
3163  * 
3164  * 
3165  * @constructor
3166  * Create a new Sidebar
3167  * @param {Object} config The config object
3168  */
3169
3170
3171 Roo.bootstrap.NavSimplebar = function(config){
3172     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3173 };
3174
3175 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3176     
3177     inverse: false,
3178     
3179     type: false,
3180     arrangement: '',
3181     align : false,
3182     
3183     
3184     
3185     main : false,
3186     
3187     
3188     tag : false,
3189     
3190     
3191     getAutoCreate : function(){
3192         
3193         
3194         var cfg = {
3195             tag : this.tag || 'div',
3196             cls : 'navbar'
3197         };
3198           
3199         
3200         cfg.cn = [
3201             {
3202                 cls: 'nav',
3203                 tag : 'ul'
3204             }
3205         ];
3206         
3207          
3208         this.type = this.type || 'nav';
3209         if (['tabs','pills'].indexOf(this.type)!==-1) {
3210             cfg.cn[0].cls += ' nav-' + this.type
3211         
3212         
3213         } else {
3214             if (this.type!=='nav') {
3215                 Roo.log('nav type must be nav/tabs/pills')
3216             }
3217             cfg.cn[0].cls += ' navbar-nav'
3218         }
3219         
3220         
3221         
3222         
3223         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3224             cfg.cn[0].cls += ' nav-' + this.arrangement;
3225         }
3226         
3227         
3228         if (this.align === 'right') {
3229             cfg.cn[0].cls += ' navbar-right';
3230         }
3231         
3232         if (this.inverse) {
3233             cfg.cls += ' navbar-inverse';
3234             
3235         }
3236         
3237         
3238         return cfg;
3239     
3240         
3241     }
3242     
3243     
3244     
3245 });
3246
3247
3248
3249  
3250
3251  
3252        /*
3253  * - LGPL
3254  *
3255  * navbar
3256  * 
3257  */
3258
3259 /**
3260  * @class Roo.bootstrap.NavHeaderbar
3261  * @extends Roo.bootstrap.NavSimplebar
3262  * Bootstrap Sidebar class
3263  *
3264  * @cfg {String} brand what is brand
3265  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3266  * @cfg {String} brand_href href of the brand
3267  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3268  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3269  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3270  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3271  * 
3272  * @constructor
3273  * Create a new Sidebar
3274  * @param {Object} config The config object
3275  */
3276
3277
3278 Roo.bootstrap.NavHeaderbar = function(config){
3279     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3280       
3281 };
3282
3283 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3284     
3285     position: '',
3286     brand: '',
3287     brand_href: false,
3288     srButton : true,
3289     autohide : false,
3290     desktopCenter : false,
3291    
3292     
3293     getAutoCreate : function(){
3294         
3295         var   cfg = {
3296             tag: this.nav || 'nav',
3297             cls: 'navbar',
3298             role: 'navigation',
3299             cn: []
3300         };
3301         
3302         var cn = cfg.cn;
3303         if (this.desktopCenter) {
3304             cn.push({cls : 'container', cn : []});
3305             cn = cn[0].cn;
3306         }
3307         
3308         if(this.srButton){
3309             cn.push({
3310                 tag: 'div',
3311                 cls: 'navbar-header',
3312                 cn: [
3313                     {
3314                         tag: 'button',
3315                         type: 'button',
3316                         cls: 'navbar-toggle',
3317                         'data-toggle': 'collapse',
3318                         cn: [
3319                             {
3320                                 tag: 'span',
3321                                 cls: 'sr-only',
3322                                 html: 'Toggle navigation'
3323                             },
3324                             {
3325                                 tag: 'span',
3326                                 cls: 'icon-bar'
3327                             },
3328                             {
3329                                 tag: 'span',
3330                                 cls: 'icon-bar'
3331                             },
3332                             {
3333                                 tag: 'span',
3334                                 cls: 'icon-bar'
3335                             }
3336                         ]
3337                     }
3338                 ]
3339             });
3340         }
3341         
3342         cn.push({
3343             tag: 'div',
3344             cls: 'collapse navbar-collapse',
3345             cn : []
3346         });
3347         
3348         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3349         
3350         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3351             cfg.cls += ' navbar-' + this.position;
3352             
3353             // tag can override this..
3354             
3355             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3356         }
3357         
3358         if (this.brand !== '') {
3359             cn[0].cn.push({
3360                 tag: 'a',
3361                 href: this.brand_href ? this.brand_href : '#',
3362                 cls: 'navbar-brand',
3363                 cn: [
3364                 this.brand
3365                 ]
3366             });
3367         }
3368         
3369         if(this.main){
3370             cfg.cls += ' main-nav';
3371         }
3372         
3373         
3374         return cfg;
3375
3376         
3377     },
3378     getHeaderChildContainer : function()
3379     {
3380         if (this.el.select('.navbar-header').getCount()) {
3381             return this.el.select('.navbar-header',true).first();
3382         }
3383         
3384         return this.getChildContainer();
3385     },
3386     
3387     
3388     initEvents : function()
3389     {
3390         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3391         
3392         if (this.autohide) {
3393             
3394             var prevScroll = 0;
3395             var ft = this.el;
3396             
3397             Roo.get(document).on('scroll',function(e) {
3398                 var ns = Roo.get(document).getScroll().top;
3399                 var os = prevScroll;
3400                 prevScroll = ns;
3401                 
3402                 if(ns > os){
3403                     ft.removeClass('slideDown');
3404                     ft.addClass('slideUp');
3405                     return;
3406                 }
3407                 ft.removeClass('slideUp');
3408                 ft.addClass('slideDown');
3409                  
3410               
3411           },this);
3412         }
3413     }    
3414           
3415       
3416     
3417     
3418 });
3419
3420
3421
3422  
3423
3424  /*
3425  * - LGPL
3426  *
3427  * navbar
3428  * 
3429  */
3430
3431 /**
3432  * @class Roo.bootstrap.NavSidebar
3433  * @extends Roo.bootstrap.Navbar
3434  * Bootstrap Sidebar class
3435  * 
3436  * @constructor
3437  * Create a new Sidebar
3438  * @param {Object} config The config object
3439  */
3440
3441
3442 Roo.bootstrap.NavSidebar = function(config){
3443     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3444 };
3445
3446 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3447     
3448     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3449     
3450     getAutoCreate : function(){
3451         
3452         
3453         return  {
3454             tag: 'div',
3455             cls: 'sidebar sidebar-nav'
3456         };
3457     
3458         
3459     }
3460     
3461     
3462     
3463 });
3464
3465
3466
3467  
3468
3469  /*
3470  * - LGPL
3471  *
3472  * nav group
3473  * 
3474  */
3475
3476 /**
3477  * @class Roo.bootstrap.NavGroup
3478  * @extends Roo.bootstrap.Component
3479  * Bootstrap NavGroup class
3480  * @cfg {String} align left | right
3481  * @cfg {Boolean} inverse false | true
3482  * @cfg {String} type (nav|pills|tab) default nav
3483  * @cfg {String} navId - reference Id for navbar.
3484
3485  * 
3486  * @constructor
3487  * Create a new nav group
3488  * @param {Object} config The config object
3489  */
3490
3491 Roo.bootstrap.NavGroup = function(config){
3492     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3493     this.navItems = [];
3494    
3495     Roo.bootstrap.NavGroup.register(this);
3496      this.addEvents({
3497         /**
3498              * @event changed
3499              * Fires when the active item changes
3500              * @param {Roo.bootstrap.NavGroup} this
3501              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3502              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3503          */
3504         'changed': true
3505      });
3506     
3507 };
3508
3509 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3510     
3511     align: '',
3512     inverse: false,
3513     form: false,
3514     type: 'nav',
3515     navId : '',
3516     // private
3517     
3518     navItems : false, 
3519     
3520     getAutoCreate : function()
3521     {
3522         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3523         
3524         cfg = {
3525             tag : 'ul',
3526             cls: 'nav' 
3527         }
3528         
3529         if (['tabs','pills'].indexOf(this.type)!==-1) {
3530             cfg.cls += ' nav-' + this.type
3531         } else {
3532             if (this.type!=='nav') {
3533                 Roo.log('nav type must be nav/tabs/pills')
3534             }
3535             cfg.cls += ' navbar-nav'
3536         }
3537         
3538         if (this.parent().sidebar) {
3539             cfg = {
3540                 tag: 'ul',
3541                 cls: 'dashboard-menu sidebar-menu'
3542             }
3543             
3544             return cfg;
3545         }
3546         
3547         if (this.form === true) {
3548             cfg = {
3549                 tag: 'form',
3550                 cls: 'navbar-form'
3551             }
3552             
3553             if (this.align === 'right') {
3554                 cfg.cls += ' navbar-right';
3555             } else {
3556                 cfg.cls += ' navbar-left';
3557             }
3558         }
3559         
3560         if (this.align === 'right') {
3561             cfg.cls += ' navbar-right';
3562         }
3563         
3564         if (this.inverse) {
3565             cfg.cls += ' navbar-inverse';
3566             
3567         }
3568         
3569         
3570         return cfg;
3571     },
3572     /**
3573     * sets the active Navigation item
3574     * @param {Roo.bootstrap.NavItem} the new current navitem
3575     */
3576     setActiveItem : function(item)
3577     {
3578         var prev = false;
3579         Roo.each(this.navItems, function(v){
3580             if (v == item) {
3581                 return ;
3582             }
3583             if (v.isActive()) {
3584                 v.setActive(false, true);
3585                 prev = v;
3586                 
3587             }
3588             
3589         });
3590
3591         item.setActive(true, true);
3592         this.fireEvent('changed', this, item, prev);
3593         
3594         
3595     },
3596     /**
3597     * gets the active Navigation item
3598     * @return {Roo.bootstrap.NavItem} the current navitem
3599     */
3600     getActive : function()
3601     {
3602         
3603         var prev = false;
3604         Roo.each(this.navItems, function(v){
3605             
3606             if (v.isActive()) {
3607                 prev = v;
3608                 
3609             }
3610             
3611         });
3612         return prev;
3613     },
3614     
3615     indexOfNav : function()
3616     {
3617         
3618         var prev = false;
3619         Roo.each(this.navItems, function(v,i){
3620             
3621             if (v.isActive()) {
3622                 prev = i;
3623                 
3624             }
3625             
3626         });
3627         return prev;
3628     },
3629     /**
3630     * adds a Navigation item
3631     * @param {Roo.bootstrap.NavItem} the navitem to add
3632     */
3633     addItem : function(cfg)
3634     {
3635         var cn = new Roo.bootstrap.NavItem(cfg);
3636         this.register(cn);
3637         cn.parentId = this.id;
3638         cn.onRender(this.el, null);
3639         return cn;
3640     },
3641     /**
3642     * register a Navigation item
3643     * @param {Roo.bootstrap.NavItem} the navitem to add
3644     */
3645     register : function(item)
3646     {
3647         this.navItems.push( item);
3648         item.navId = this.navId;
3649     
3650     },
3651     
3652     /**
3653     * clear all the Navigation item
3654     */
3655    
3656     clearAll : function()
3657     {
3658         this.navItems = [];
3659         this.el.dom.innerHTML = '';
3660     },
3661     
3662     getNavItem: function(tabId)
3663     {
3664         var ret = false;
3665         Roo.each(this.navItems, function(e) {
3666             if (e.tabId == tabId) {
3667                ret =  e;
3668                return false;
3669             }
3670             return true;
3671             
3672         });
3673         return ret;
3674     },
3675     
3676     setActiveNext : function()
3677     {
3678         var i = this.indexOfNav(this.getActive());
3679         if (i > this.navItems.length) {
3680             return;
3681         }
3682         this.setActiveItem(this.navItems[i+1]);
3683     },
3684     setActivePrev : function()
3685     {
3686         var i = this.indexOfNav(this.getActive());
3687         if (i  < 1) {
3688             return;
3689         }
3690         this.setActiveItem(this.navItems[i-1]);
3691     },
3692     clearWasActive : function(except) {
3693         Roo.each(this.navItems, function(e) {
3694             if (e.tabId != except.tabId && e.was_active) {
3695                e.was_active = false;
3696                return false;
3697             }
3698             return true;
3699             
3700         });
3701     },
3702     getWasActive : function ()
3703     {
3704         var r = false;
3705         Roo.each(this.navItems, function(e) {
3706             if (e.was_active) {
3707                r = e;
3708                return false;
3709             }
3710             return true;
3711             
3712         });
3713         return r;
3714     }
3715     
3716     
3717 });
3718
3719  
3720 Roo.apply(Roo.bootstrap.NavGroup, {
3721     
3722     groups: {},
3723      /**
3724     * register a Navigation Group
3725     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3726     */
3727     register : function(navgrp)
3728     {
3729         this.groups[navgrp.navId] = navgrp;
3730         
3731     },
3732     /**
3733     * fetch a Navigation Group based on the navigation ID
3734     * @param {string} the navgroup to add
3735     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3736     */
3737     get: function(navId) {
3738         if (typeof(this.groups[navId]) == 'undefined') {
3739             return false;
3740             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3741         }
3742         return this.groups[navId] ;
3743     }
3744     
3745     
3746     
3747 });
3748
3749  /*
3750  * - LGPL
3751  *
3752  * row
3753  * 
3754  */
3755
3756 /**
3757  * @class Roo.bootstrap.NavItem
3758  * @extends Roo.bootstrap.Component
3759  * Bootstrap Navbar.NavItem class
3760  * @cfg {String} href  link to
3761  * @cfg {String} html content of button
3762  * @cfg {String} badge text inside badge
3763  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3764  * @cfg {String} glyphicon name of glyphicon
3765  * @cfg {String} icon name of font awesome icon
3766  * @cfg {Boolean} active Is item active
3767  * @cfg {Boolean} disabled Is item disabled
3768  
3769  * @cfg {Boolean} preventDefault (true | false) default false
3770  * @cfg {String} tabId the tab that this item activates.
3771  * @cfg {String} tagtype (a|span) render as a href or span?
3772  * @cfg {Boolean} animateRef (true|false) link to element default false  
3773   
3774  * @constructor
3775  * Create a new Navbar Item
3776  * @param {Object} config The config object
3777  */
3778 Roo.bootstrap.NavItem = function(config){
3779     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3780     this.addEvents({
3781         // raw events
3782         /**
3783          * @event click
3784          * The raw click event for the entire grid.
3785          * @param {Roo.EventObject} e
3786          */
3787         "click" : true,
3788          /**
3789             * @event changed
3790             * Fires when the active item active state changes
3791             * @param {Roo.bootstrap.NavItem} this
3792             * @param {boolean} state the new state
3793              
3794          */
3795         'changed': true,
3796         /**
3797             * @event scrollto
3798             * Fires when scroll to element
3799             * @param {Roo.bootstrap.NavItem} this
3800             * @param {Object} options
3801             * @param {Roo.EventObject} e
3802              
3803          */
3804         'scrollto': true
3805     });
3806    
3807 };
3808
3809 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3810     
3811     href: false,
3812     html: '',
3813     badge: '',
3814     icon: false,
3815     glyphicon: false,
3816     active: false,
3817     preventDefault : false,
3818     tabId : false,
3819     tagtype : 'a',
3820     disabled : false,
3821     animateRef : false,
3822     was_active : false,
3823     
3824     getAutoCreate : function(){
3825          
3826         var cfg = {
3827             tag: 'li',
3828             cls: 'nav-item'
3829             
3830         }
3831         if (this.active) {
3832             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3833         }
3834         if (this.disabled) {
3835             cfg.cls += ' disabled';
3836         }
3837         
3838         if (this.href || this.html || this.glyphicon || this.icon) {
3839             cfg.cn = [
3840                 {
3841                     tag: this.tagtype,
3842                     href : this.href || "#",
3843                     html: this.html || ''
3844                 }
3845             ];
3846             
3847             if (this.icon) {
3848                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3849             }
3850
3851             if(this.glyphicon) {
3852                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3853             }
3854             
3855             if (this.menu) {
3856                 
3857                 cfg.cn[0].html += " <span class='caret'></span>";
3858              
3859             }
3860             
3861             if (this.badge !== '') {
3862                  
3863                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3864             }
3865         }
3866         
3867         
3868         
3869         return cfg;
3870     },
3871     initEvents: function() 
3872     {
3873         if (typeof (this.menu) != 'undefined') {
3874             this.menu.parentType = this.xtype;
3875             this.menu.triggerEl = this.el;
3876             this.menu = this.addxtype(Roo.apply({}, this.menu));
3877         }
3878         
3879         this.el.select('a',true).on('click', this.onClick, this);
3880         
3881         if(this.tagtype == 'span'){
3882             this.el.select('span',true).on('click', this.onClick, this);
3883         }
3884        
3885         // at this point parent should be available..
3886         this.parent().register(this);
3887     },
3888     
3889     onClick : function(e)
3890     {
3891         if(
3892                 this.preventDefault || 
3893                 this.href == '#' 
3894         ){
3895             
3896             e.preventDefault();
3897         }
3898         
3899         if (this.disabled) {
3900             return;
3901         }
3902         
3903         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3904         if (tg && tg.transition) {
3905             Roo.log("waiting for the transitionend");
3906             return;
3907         }
3908         
3909         
3910         
3911         Roo.log("fire event clicked");
3912         if(this.fireEvent('click', this, e) === false){
3913             return;
3914         };
3915         
3916         if(this.tagtype == 'span'){
3917             return;
3918         }
3919         var p =  this.parent();
3920  
3921         Roo.log(this.href);
3922         var ael = this.el.select('a',true).first();
3923         Roo.log(ael);
3924         
3925         if(ael && this.animateRef && this.href.indexOf('#') > -1){
3926             Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
3927             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
3928                 return; // ignore... - it's a 'hash' to another page.
3929             }
3930             
3931             e.preventDefault();
3932             this.scrollToElement(e);
3933         }
3934         
3935         
3936         
3937         
3938         
3939         
3940         if (['tabs','pills'].indexOf(this.type)!==-1) {
3941             if (typeof(p.setActiveItem) !== 'undefined') {
3942                 p.setActiveItem(this);
3943             }
3944         }
3945     },
3946     
3947     isActive: function () {
3948         return this.active
3949     },
3950     setActive : function(state, fire, is_was_active)
3951     {
3952         if (this.active && !state & this.navId) {
3953             this.was_active = true;
3954             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3955             if (nv) {
3956                 nv.clearWasActive(this);
3957             }
3958             
3959         }
3960         this.active = state;
3961         
3962         if (!state ) {
3963             this.el.removeClass('active');
3964         } else if (!this.el.hasClass('active')) {
3965             this.el.addClass('active');
3966         }
3967         if (fire) {
3968             this.fireEvent('changed', this, state);
3969         }
3970         
3971         // show a panel if it's registered and related..
3972         
3973         if (!this.navId || !this.tabId || !state || is_was_active) {
3974             return;
3975         }
3976         
3977         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3978         if (!tg) {
3979             return;
3980         }
3981         var pan = tg.getPanelByName(this.tabId);
3982         if (!pan) {
3983             return;
3984         }
3985         // if we can not flip to new panel - go back to old nav highlight..
3986         if (false == tg.showPanel(pan)) {
3987             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3988             if (nv) {
3989                 var onav = nv.getWasActive();
3990                 if (onav) {
3991                     onav.setActive(true, false, true);
3992                 }
3993             }
3994             
3995         }
3996         
3997         
3998         
3999     },
4000      // this should not be here...
4001     setDisabled : function(state)
4002     {
4003         this.disabled = state;
4004         if (!state ) {
4005             this.el.removeClass('disabled');
4006         } else if (!this.el.hasClass('disabled')) {
4007             this.el.addClass('disabled');
4008         }
4009         
4010     },
4011     
4012     /**
4013      * Fetch the element to display the tooltip on.
4014      * @return {Roo.Element} defaults to this.el
4015      */
4016     tooltipEl : function()
4017     {
4018         return this.el.select('' + this.tagtype + '', true).first();
4019     },
4020     
4021     scrollToElement : function(e)
4022     {
4023         var c = document.body;
4024         
4025         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4026         
4027         if(!target){
4028             return;
4029         }
4030
4031         var o = target.calcOffsetsTo(c);
4032         
4033         var options = {
4034             target : target,
4035             value : o[1]
4036         }
4037         
4038         this.fireEvent('scrollto', this, options, e);
4039         
4040         Roo.get(c).scrollTo('top', options.value, true);
4041         
4042         return;
4043     }
4044 });
4045  
4046
4047  /*
4048  * - LGPL
4049  *
4050  * sidebar item
4051  *
4052  *  li
4053  *    <span> icon </span>
4054  *    <span> text </span>
4055  *    <span>badge </span>
4056  */
4057
4058 /**
4059  * @class Roo.bootstrap.NavSidebarItem
4060  * @extends Roo.bootstrap.NavItem
4061  * Bootstrap Navbar.NavSidebarItem class
4062  * @constructor
4063  * Create a new Navbar Button
4064  * @param {Object} config The config object
4065  */
4066 Roo.bootstrap.NavSidebarItem = function(config){
4067     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4068     this.addEvents({
4069         // raw events
4070         /**
4071          * @event click
4072          * The raw click event for the entire grid.
4073          * @param {Roo.EventObject} e
4074          */
4075         "click" : true,
4076          /**
4077             * @event changed
4078             * Fires when the active item active state changes
4079             * @param {Roo.bootstrap.NavSidebarItem} this
4080             * @param {boolean} state the new state
4081              
4082          */
4083         'changed': true
4084     });
4085    
4086 };
4087
4088 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4089     
4090     
4091     getAutoCreate : function(){
4092         
4093         
4094         var a = {
4095                 tag: 'a',
4096                 href : this.href || '#',
4097                 cls: '',
4098                 html : '',
4099                 cn : []
4100         };
4101         var cfg = {
4102             tag: 'li',
4103             cls: '',
4104             cn: [ a ]
4105         }
4106         var span = {
4107             tag: 'span',
4108             html : this.html || ''
4109         }
4110         
4111         
4112         if (this.active) {
4113             cfg.cls += ' active';
4114         }
4115         
4116         // left icon..
4117         if (this.glyphicon || this.icon) {
4118             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4119             a.cn.push({ tag : 'i', cls : c }) ;
4120         }
4121         // html..
4122         a.cn.push(span);
4123         // then badge..
4124         if (this.badge !== '') {
4125             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4126         }
4127         // fi
4128         if (this.menu) {
4129             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4130             a.cls += 'dropdown-toggle treeview' ;
4131             
4132         }
4133         
4134         
4135         
4136         return cfg;
4137          
4138            
4139     }
4140    
4141      
4142  
4143 });
4144  
4145
4146  /*
4147  * - LGPL
4148  *
4149  * row
4150  * 
4151  */
4152
4153 /**
4154  * @class Roo.bootstrap.Row
4155  * @extends Roo.bootstrap.Component
4156  * Bootstrap Row class (contains columns...)
4157  * 
4158  * @constructor
4159  * Create a new Row
4160  * @param {Object} config The config object
4161  */
4162
4163 Roo.bootstrap.Row = function(config){
4164     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4165 };
4166
4167 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4168     
4169     getAutoCreate : function(){
4170        return {
4171             cls: 'row clearfix'
4172        };
4173     }
4174     
4175     
4176 });
4177
4178  
4179
4180  /*
4181  * - LGPL
4182  *
4183  * element
4184  * 
4185  */
4186
4187 /**
4188  * @class Roo.bootstrap.Element
4189  * @extends Roo.bootstrap.Component
4190  * Bootstrap Element class
4191  * @cfg {String} html contents of the element
4192  * @cfg {String} tag tag of the element
4193  * @cfg {String} cls class of the element
4194  * @cfg {Boolean} preventDefault (true|false) default false
4195  * @cfg {Boolean} clickable (true|false) default false
4196  * 
4197  * @constructor
4198  * Create a new Element
4199  * @param {Object} config The config object
4200  */
4201
4202 Roo.bootstrap.Element = function(config){
4203     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4204     
4205     this.addEvents({
4206         // raw events
4207         /**
4208          * @event click
4209          * When a element is chick
4210          * @param {Roo.bootstrap.Element} this
4211          * @param {Roo.EventObject} e
4212          */
4213         "click" : true
4214     });
4215 };
4216
4217 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4218     
4219     tag: 'div',
4220     cls: '',
4221     html: '',
4222     preventDefault: false, 
4223     clickable: false,
4224     
4225     getAutoCreate : function(){
4226         
4227         var cfg = {
4228             tag: this.tag,
4229             cls: this.cls,
4230             html: this.html
4231         }
4232         
4233         return cfg;
4234     },
4235     
4236     initEvents: function() 
4237     {
4238         Roo.bootstrap.Element.superclass.initEvents.call(this);
4239         
4240         if(this.clickable){
4241             this.el.on('click', this.onClick, this);
4242         }
4243         
4244     },
4245     
4246     onClick : function(e)
4247     {
4248         if(this.preventDefault){
4249             e.preventDefault();
4250         }
4251         
4252         this.fireEvent('click', this, e);
4253     },
4254     
4255     getValue : function()
4256     {
4257         return this.el.dom.innerHTML;
4258     },
4259     
4260     setValue : function(value)
4261     {
4262         this.el.dom.innerHTML = value;
4263     }
4264    
4265 });
4266
4267  
4268
4269  /*
4270  * - LGPL
4271  *
4272  * pagination
4273  * 
4274  */
4275
4276 /**
4277  * @class Roo.bootstrap.Pagination
4278  * @extends Roo.bootstrap.Component
4279  * Bootstrap Pagination class
4280  * @cfg {String} size xs | sm | md | lg
4281  * @cfg {Boolean} inverse false | true
4282  * 
4283  * @constructor
4284  * Create a new Pagination
4285  * @param {Object} config The config object
4286  */
4287
4288 Roo.bootstrap.Pagination = function(config){
4289     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4290 };
4291
4292 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4293     
4294     cls: false,
4295     size: false,
4296     inverse: false,
4297     
4298     getAutoCreate : function(){
4299         var cfg = {
4300             tag: 'ul',
4301                 cls: 'pagination'
4302         };
4303         if (this.inverse) {
4304             cfg.cls += ' inverse';
4305         }
4306         if (this.html) {
4307             cfg.html=this.html;
4308         }
4309         if (this.cls) {
4310             cfg.cls += " " + this.cls;
4311         }
4312         return cfg;
4313     }
4314    
4315 });
4316
4317  
4318
4319  /*
4320  * - LGPL
4321  *
4322  * Pagination item
4323  * 
4324  */
4325
4326
4327 /**
4328  * @class Roo.bootstrap.PaginationItem
4329  * @extends Roo.bootstrap.Component
4330  * Bootstrap PaginationItem class
4331  * @cfg {String} html text
4332  * @cfg {String} href the link
4333  * @cfg {Boolean} preventDefault (true | false) default true
4334  * @cfg {Boolean} active (true | false) default false
4335  * @cfg {Boolean} disabled default false
4336  * 
4337  * 
4338  * @constructor
4339  * Create a new PaginationItem
4340  * @param {Object} config The config object
4341  */
4342
4343
4344 Roo.bootstrap.PaginationItem = function(config){
4345     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4346     this.addEvents({
4347         // raw events
4348         /**
4349          * @event click
4350          * The raw click event for the entire grid.
4351          * @param {Roo.EventObject} e
4352          */
4353         "click" : true
4354     });
4355 };
4356
4357 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4358     
4359     href : false,
4360     html : false,
4361     preventDefault: true,
4362     active : false,
4363     cls : false,
4364     disabled: false,
4365     
4366     getAutoCreate : function(){
4367         var cfg= {
4368             tag: 'li',
4369             cn: [
4370                 {
4371                     tag : 'a',
4372                     href : this.href ? this.href : '#',
4373                     html : this.html ? this.html : ''
4374                 }
4375             ]
4376         };
4377         
4378         if(this.cls){
4379             cfg.cls = this.cls;
4380         }
4381         
4382         if(this.disabled){
4383             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4384         }
4385         
4386         if(this.active){
4387             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4388         }
4389         
4390         return cfg;
4391     },
4392     
4393     initEvents: function() {
4394         
4395         this.el.on('click', this.onClick, this);
4396         
4397     },
4398     onClick : function(e)
4399     {
4400         Roo.log('PaginationItem on click ');
4401         if(this.preventDefault){
4402             e.preventDefault();
4403         }
4404         
4405         if(this.disabled){
4406             return;
4407         }
4408         
4409         this.fireEvent('click', this, e);
4410     }
4411    
4412 });
4413
4414  
4415
4416  /*
4417  * - LGPL
4418  *
4419  * slider
4420  * 
4421  */
4422
4423
4424 /**
4425  * @class Roo.bootstrap.Slider
4426  * @extends Roo.bootstrap.Component
4427  * Bootstrap Slider class
4428  *    
4429  * @constructor
4430  * Create a new Slider
4431  * @param {Object} config The config object
4432  */
4433
4434 Roo.bootstrap.Slider = function(config){
4435     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4436 };
4437
4438 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4439     
4440     getAutoCreate : function(){
4441         
4442         var cfg = {
4443             tag: 'div',
4444             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4445             cn: [
4446                 {
4447                     tag: 'a',
4448                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4449                 }
4450             ]
4451         }
4452         
4453         return cfg;
4454     }
4455    
4456 });
4457
4458  /*
4459  * Based on:
4460  * Ext JS Library 1.1.1
4461  * Copyright(c) 2006-2007, Ext JS, LLC.
4462  *
4463  * Originally Released Under LGPL - original licence link has changed is not relivant.
4464  *
4465  * Fork - LGPL
4466  * <script type="text/javascript">
4467  */
4468  
4469
4470 /**
4471  * @class Roo.grid.ColumnModel
4472  * @extends Roo.util.Observable
4473  * This is the default implementation of a ColumnModel used by the Grid. It defines
4474  * the columns in the grid.
4475  * <br>Usage:<br>
4476  <pre><code>
4477  var colModel = new Roo.grid.ColumnModel([
4478         {header: "Ticker", width: 60, sortable: true, locked: true},
4479         {header: "Company Name", width: 150, sortable: true},
4480         {header: "Market Cap.", width: 100, sortable: true},
4481         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4482         {header: "Employees", width: 100, sortable: true, resizable: false}
4483  ]);
4484  </code></pre>
4485  * <p>
4486  
4487  * The config options listed for this class are options which may appear in each
4488  * individual column definition.
4489  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4490  * @constructor
4491  * @param {Object} config An Array of column config objects. See this class's
4492  * config objects for details.
4493 */
4494 Roo.grid.ColumnModel = function(config){
4495         /**
4496      * The config passed into the constructor
4497      */
4498     this.config = config;
4499     this.lookup = {};
4500
4501     // if no id, create one
4502     // if the column does not have a dataIndex mapping,
4503     // map it to the order it is in the config
4504     for(var i = 0, len = config.length; i < len; i++){
4505         var c = config[i];
4506         if(typeof c.dataIndex == "undefined"){
4507             c.dataIndex = i;
4508         }
4509         if(typeof c.renderer == "string"){
4510             c.renderer = Roo.util.Format[c.renderer];
4511         }
4512         if(typeof c.id == "undefined"){
4513             c.id = Roo.id();
4514         }
4515         if(c.editor && c.editor.xtype){
4516             c.editor  = Roo.factory(c.editor, Roo.grid);
4517         }
4518         if(c.editor && c.editor.isFormField){
4519             c.editor = new Roo.grid.GridEditor(c.editor);
4520         }
4521         this.lookup[c.id] = c;
4522     }
4523
4524     /**
4525      * The width of columns which have no width specified (defaults to 100)
4526      * @type Number
4527      */
4528     this.defaultWidth = 100;
4529
4530     /**
4531      * Default sortable of columns which have no sortable specified (defaults to false)
4532      * @type Boolean
4533      */
4534     this.defaultSortable = false;
4535
4536     this.addEvents({
4537         /**
4538              * @event widthchange
4539              * Fires when the width of a column changes.
4540              * @param {ColumnModel} this
4541              * @param {Number} columnIndex The column index
4542              * @param {Number} newWidth The new width
4543              */
4544             "widthchange": true,
4545         /**
4546              * @event headerchange
4547              * Fires when the text of a header changes.
4548              * @param {ColumnModel} this
4549              * @param {Number} columnIndex The column index
4550              * @param {Number} newText The new header text
4551              */
4552             "headerchange": true,
4553         /**
4554              * @event hiddenchange
4555              * Fires when a column is hidden or "unhidden".
4556              * @param {ColumnModel} this
4557              * @param {Number} columnIndex The column index
4558              * @param {Boolean} hidden true if hidden, false otherwise
4559              */
4560             "hiddenchange": true,
4561             /**
4562          * @event columnmoved
4563          * Fires when a column is moved.
4564          * @param {ColumnModel} this
4565          * @param {Number} oldIndex
4566          * @param {Number} newIndex
4567          */
4568         "columnmoved" : true,
4569         /**
4570          * @event columlockchange
4571          * Fires when a column's locked state is changed
4572          * @param {ColumnModel} this
4573          * @param {Number} colIndex
4574          * @param {Boolean} locked true if locked
4575          */
4576         "columnlockchange" : true
4577     });
4578     Roo.grid.ColumnModel.superclass.constructor.call(this);
4579 };
4580 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4581     /**
4582      * @cfg {String} header The header text to display in the Grid view.
4583      */
4584     /**
4585      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4586      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4587      * specified, the column's index is used as an index into the Record's data Array.
4588      */
4589     /**
4590      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4591      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4592      */
4593     /**
4594      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4595      * Defaults to the value of the {@link #defaultSortable} property.
4596      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4597      */
4598     /**
4599      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4600      */
4601     /**
4602      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4603      */
4604     /**
4605      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4606      */
4607     /**
4608      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4609      */
4610     /**
4611      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4612      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4613      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4614      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4615      */
4616        /**
4617      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4618      */
4619     /**
4620      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4621      */
4622     /**
4623      * @cfg {String} cursor (Optional)
4624      */
4625     /**
4626      * @cfg {String} tooltip (Optional)
4627      */
4628     /**
4629      * Returns the id of the column at the specified index.
4630      * @param {Number} index The column index
4631      * @return {String} the id
4632      */
4633     getColumnId : function(index){
4634         return this.config[index].id;
4635     },
4636
4637     /**
4638      * Returns the column for a specified id.
4639      * @param {String} id The column id
4640      * @return {Object} the column
4641      */
4642     getColumnById : function(id){
4643         return this.lookup[id];
4644     },
4645
4646     
4647     /**
4648      * Returns the column for a specified dataIndex.
4649      * @param {String} dataIndex The column dataIndex
4650      * @return {Object|Boolean} the column or false if not found
4651      */
4652     getColumnByDataIndex: function(dataIndex){
4653         var index = this.findColumnIndex(dataIndex);
4654         return index > -1 ? this.config[index] : false;
4655     },
4656     
4657     /**
4658      * Returns the index for a specified column id.
4659      * @param {String} id The column id
4660      * @return {Number} the index, or -1 if not found
4661      */
4662     getIndexById : function(id){
4663         for(var i = 0, len = this.config.length; i < len; i++){
4664             if(this.config[i].id == id){
4665                 return i;
4666             }
4667         }
4668         return -1;
4669     },
4670     
4671     /**
4672      * Returns the index for a specified column dataIndex.
4673      * @param {String} dataIndex The column dataIndex
4674      * @return {Number} the index, or -1 if not found
4675      */
4676     
4677     findColumnIndex : function(dataIndex){
4678         for(var i = 0, len = this.config.length; i < len; i++){
4679             if(this.config[i].dataIndex == dataIndex){
4680                 return i;
4681             }
4682         }
4683         return -1;
4684     },
4685     
4686     
4687     moveColumn : function(oldIndex, newIndex){
4688         var c = this.config[oldIndex];
4689         this.config.splice(oldIndex, 1);
4690         this.config.splice(newIndex, 0, c);
4691         this.dataMap = null;
4692         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4693     },
4694
4695     isLocked : function(colIndex){
4696         return this.config[colIndex].locked === true;
4697     },
4698
4699     setLocked : function(colIndex, value, suppressEvent){
4700         if(this.isLocked(colIndex) == value){
4701             return;
4702         }
4703         this.config[colIndex].locked = value;
4704         if(!suppressEvent){
4705             this.fireEvent("columnlockchange", this, colIndex, value);
4706         }
4707     },
4708
4709     getTotalLockedWidth : function(){
4710         var totalWidth = 0;
4711         for(var i = 0; i < this.config.length; i++){
4712             if(this.isLocked(i) && !this.isHidden(i)){
4713                 this.totalWidth += this.getColumnWidth(i);
4714             }
4715         }
4716         return totalWidth;
4717     },
4718
4719     getLockedCount : function(){
4720         for(var i = 0, len = this.config.length; i < len; i++){
4721             if(!this.isLocked(i)){
4722                 return i;
4723             }
4724         }
4725     },
4726
4727     /**
4728      * Returns the number of columns.
4729      * @return {Number}
4730      */
4731     getColumnCount : function(visibleOnly){
4732         if(visibleOnly === true){
4733             var c = 0;
4734             for(var i = 0, len = this.config.length; i < len; i++){
4735                 if(!this.isHidden(i)){
4736                     c++;
4737                 }
4738             }
4739             return c;
4740         }
4741         return this.config.length;
4742     },
4743
4744     /**
4745      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4746      * @param {Function} fn
4747      * @param {Object} scope (optional)
4748      * @return {Array} result
4749      */
4750     getColumnsBy : function(fn, scope){
4751         var r = [];
4752         for(var i = 0, len = this.config.length; i < len; i++){
4753             var c = this.config[i];
4754             if(fn.call(scope||this, c, i) === true){
4755                 r[r.length] = c;
4756             }
4757         }
4758         return r;
4759     },
4760
4761     /**
4762      * Returns true if the specified column is sortable.
4763      * @param {Number} col The column index
4764      * @return {Boolean}
4765      */
4766     isSortable : function(col){
4767         if(typeof this.config[col].sortable == "undefined"){
4768             return this.defaultSortable;
4769         }
4770         return this.config[col].sortable;
4771     },
4772
4773     /**
4774      * Returns the rendering (formatting) function defined for the column.
4775      * @param {Number} col The column index.
4776      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4777      */
4778     getRenderer : function(col){
4779         if(!this.config[col].renderer){
4780             return Roo.grid.ColumnModel.defaultRenderer;
4781         }
4782         return this.config[col].renderer;
4783     },
4784
4785     /**
4786      * Sets the rendering (formatting) function for a column.
4787      * @param {Number} col The column index
4788      * @param {Function} fn The function to use to process the cell's raw data
4789      * to return HTML markup for the grid view. The render function is called with
4790      * the following parameters:<ul>
4791      * <li>Data value.</li>
4792      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4793      * <li>css A CSS style string to apply to the table cell.</li>
4794      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4795      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4796      * <li>Row index</li>
4797      * <li>Column index</li>
4798      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4799      */
4800     setRenderer : function(col, fn){
4801         this.config[col].renderer = fn;
4802     },
4803
4804     /**
4805      * Returns the width for the specified column.
4806      * @param {Number} col The column index
4807      * @return {Number}
4808      */
4809     getColumnWidth : function(col){
4810         return this.config[col].width * 1 || this.defaultWidth;
4811     },
4812
4813     /**
4814      * Sets the width for a column.
4815      * @param {Number} col The column index
4816      * @param {Number} width The new width
4817      */
4818     setColumnWidth : function(col, width, suppressEvent){
4819         this.config[col].width = width;
4820         this.totalWidth = null;
4821         if(!suppressEvent){
4822              this.fireEvent("widthchange", this, col, width);
4823         }
4824     },
4825
4826     /**
4827      * Returns the total width of all columns.
4828      * @param {Boolean} includeHidden True to include hidden column widths
4829      * @return {Number}
4830      */
4831     getTotalWidth : function(includeHidden){
4832         if(!this.totalWidth){
4833             this.totalWidth = 0;
4834             for(var i = 0, len = this.config.length; i < len; i++){
4835                 if(includeHidden || !this.isHidden(i)){
4836                     this.totalWidth += this.getColumnWidth(i);
4837                 }
4838             }
4839         }
4840         return this.totalWidth;
4841     },
4842
4843     /**
4844      * Returns the header for the specified column.
4845      * @param {Number} col The column index
4846      * @return {String}
4847      */
4848     getColumnHeader : function(col){
4849         return this.config[col].header;
4850     },
4851
4852     /**
4853      * Sets the header for a column.
4854      * @param {Number} col The column index
4855      * @param {String} header The new header
4856      */
4857     setColumnHeader : function(col, header){
4858         this.config[col].header = header;
4859         this.fireEvent("headerchange", this, col, header);
4860     },
4861
4862     /**
4863      * Returns the tooltip for the specified column.
4864      * @param {Number} col The column index
4865      * @return {String}
4866      */
4867     getColumnTooltip : function(col){
4868             return this.config[col].tooltip;
4869     },
4870     /**
4871      * Sets the tooltip for a column.
4872      * @param {Number} col The column index
4873      * @param {String} tooltip The new tooltip
4874      */
4875     setColumnTooltip : function(col, tooltip){
4876             this.config[col].tooltip = tooltip;
4877     },
4878
4879     /**
4880      * Returns the dataIndex for the specified column.
4881      * @param {Number} col The column index
4882      * @return {Number}
4883      */
4884     getDataIndex : function(col){
4885         return this.config[col].dataIndex;
4886     },
4887
4888     /**
4889      * Sets the dataIndex for a column.
4890      * @param {Number} col The column index
4891      * @param {Number} dataIndex The new dataIndex
4892      */
4893     setDataIndex : function(col, dataIndex){
4894         this.config[col].dataIndex = dataIndex;
4895     },
4896
4897     
4898     
4899     /**
4900      * Returns true if the cell is editable.
4901      * @param {Number} colIndex The column index
4902      * @param {Number} rowIndex The row index
4903      * @return {Boolean}
4904      */
4905     isCellEditable : function(colIndex, rowIndex){
4906         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4907     },
4908
4909     /**
4910      * Returns the editor defined for the cell/column.
4911      * return false or null to disable editing.
4912      * @param {Number} colIndex The column index
4913      * @param {Number} rowIndex The row index
4914      * @return {Object}
4915      */
4916     getCellEditor : function(colIndex, rowIndex){
4917         return this.config[colIndex].editor;
4918     },
4919
4920     /**
4921      * Sets if a column is editable.
4922      * @param {Number} col The column index
4923      * @param {Boolean} editable True if the column is editable
4924      */
4925     setEditable : function(col, editable){
4926         this.config[col].editable = editable;
4927     },
4928
4929
4930     /**
4931      * Returns true if the column is hidden.
4932      * @param {Number} colIndex The column index
4933      * @return {Boolean}
4934      */
4935     isHidden : function(colIndex){
4936         return this.config[colIndex].hidden;
4937     },
4938
4939
4940     /**
4941      * Returns true if the column width cannot be changed
4942      */
4943     isFixed : function(colIndex){
4944         return this.config[colIndex].fixed;
4945     },
4946
4947     /**
4948      * Returns true if the column can be resized
4949      * @return {Boolean}
4950      */
4951     isResizable : function(colIndex){
4952         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4953     },
4954     /**
4955      * Sets if a column is hidden.
4956      * @param {Number} colIndex The column index
4957      * @param {Boolean} hidden True if the column is hidden
4958      */
4959     setHidden : function(colIndex, hidden){
4960         this.config[colIndex].hidden = hidden;
4961         this.totalWidth = null;
4962         this.fireEvent("hiddenchange", this, colIndex, hidden);
4963     },
4964
4965     /**
4966      * Sets the editor for a column.
4967      * @param {Number} col The column index
4968      * @param {Object} editor The editor object
4969      */
4970     setEditor : function(col, editor){
4971         this.config[col].editor = editor;
4972     }
4973 });
4974
4975 Roo.grid.ColumnModel.defaultRenderer = function(value){
4976         if(typeof value == "string" && value.length < 1){
4977             return "&#160;";
4978         }
4979         return value;
4980 };
4981
4982 // Alias for backwards compatibility
4983 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4984 /*
4985  * Based on:
4986  * Ext JS Library 1.1.1
4987  * Copyright(c) 2006-2007, Ext JS, LLC.
4988  *
4989  * Originally Released Under LGPL - original licence link has changed is not relivant.
4990  *
4991  * Fork - LGPL
4992  * <script type="text/javascript">
4993  */
4994  
4995 /**
4996  * @class Roo.LoadMask
4997  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4998  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4999  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5000  * element's UpdateManager load indicator and will be destroyed after the initial load.
5001  * @constructor
5002  * Create a new LoadMask
5003  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5004  * @param {Object} config The config object
5005  */
5006 Roo.LoadMask = function(el, config){
5007     this.el = Roo.get(el);
5008     Roo.apply(this, config);
5009     if(this.store){
5010         this.store.on('beforeload', this.onBeforeLoad, this);
5011         this.store.on('load', this.onLoad, this);
5012         this.store.on('loadexception', this.onLoadException, this);
5013         this.removeMask = false;
5014     }else{
5015         var um = this.el.getUpdateManager();
5016         um.showLoadIndicator = false; // disable the default indicator
5017         um.on('beforeupdate', this.onBeforeLoad, this);
5018         um.on('update', this.onLoad, this);
5019         um.on('failure', this.onLoad, this);
5020         this.removeMask = true;
5021     }
5022 };
5023
5024 Roo.LoadMask.prototype = {
5025     /**
5026      * @cfg {Boolean} removeMask
5027      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5028      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5029      */
5030     /**
5031      * @cfg {String} msg
5032      * The text to display in a centered loading message box (defaults to 'Loading...')
5033      */
5034     msg : 'Loading...',
5035     /**
5036      * @cfg {String} msgCls
5037      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5038      */
5039     msgCls : 'x-mask-loading',
5040
5041     /**
5042      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5043      * @type Boolean
5044      */
5045     disabled: false,
5046
5047     /**
5048      * Disables the mask to prevent it from being displayed
5049      */
5050     disable : function(){
5051        this.disabled = true;
5052     },
5053
5054     /**
5055      * Enables the mask so that it can be displayed
5056      */
5057     enable : function(){
5058         this.disabled = false;
5059     },
5060     
5061     onLoadException : function()
5062     {
5063         Roo.log(arguments);
5064         
5065         if (typeof(arguments[3]) != 'undefined') {
5066             Roo.MessageBox.alert("Error loading",arguments[3]);
5067         } 
5068         /*
5069         try {
5070             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5071                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5072             }   
5073         } catch(e) {
5074             
5075         }
5076         */
5077     
5078         
5079         
5080         this.el.unmask(this.removeMask);
5081     },
5082     // private
5083     onLoad : function()
5084     {
5085         this.el.unmask(this.removeMask);
5086     },
5087
5088     // private
5089     onBeforeLoad : function(){
5090         if(!this.disabled){
5091             this.el.mask(this.msg, this.msgCls);
5092         }
5093     },
5094
5095     // private
5096     destroy : function(){
5097         if(this.store){
5098             this.store.un('beforeload', this.onBeforeLoad, this);
5099             this.store.un('load', this.onLoad, this);
5100             this.store.un('loadexception', this.onLoadException, this);
5101         }else{
5102             var um = this.el.getUpdateManager();
5103             um.un('beforeupdate', this.onBeforeLoad, this);
5104             um.un('update', this.onLoad, this);
5105             um.un('failure', this.onLoad, this);
5106         }
5107     }
5108 };/*
5109  * - LGPL
5110  *
5111  * table
5112  * 
5113  */
5114
5115 /**
5116  * @class Roo.bootstrap.Table
5117  * @extends Roo.bootstrap.Component
5118  * Bootstrap Table class
5119  * @cfg {String} cls table class
5120  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5121  * @cfg {String} bgcolor Specifies the background color for a table
5122  * @cfg {Number} border Specifies whether the table cells should have borders or not
5123  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5124  * @cfg {Number} cellspacing Specifies the space between cells
5125  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5126  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5127  * @cfg {String} sortable Specifies that the table should be sortable
5128  * @cfg {String} summary Specifies a summary of the content of a table
5129  * @cfg {Number} width Specifies the width of a table
5130  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5131  * 
5132  * @cfg {boolean} striped Should the rows be alternative striped
5133  * @cfg {boolean} bordered Add borders to the table
5134  * @cfg {boolean} hover Add hover highlighting
5135  * @cfg {boolean} condensed Format condensed
5136  * @cfg {boolean} responsive Format condensed
5137  * @cfg {Boolean} loadMask (true|false) default false
5138  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
5139  * @cfg {Boolean} thead (true|false) generate thead, default true
5140  * @cfg {Boolean} RowSelection (true|false) default false
5141  * @cfg {Boolean} CellSelection (true|false) default false
5142  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5143  
5144  * 
5145  * @constructor
5146  * Create a new Table
5147  * @param {Object} config The config object
5148  */
5149
5150 Roo.bootstrap.Table = function(config){
5151     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5152     
5153     if (this.sm) {
5154         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5155         this.sm = this.selModel;
5156         this.sm.xmodule = this.xmodule || false;
5157     }
5158     if (this.cm && typeof(this.cm.config) == 'undefined') {
5159         this.colModel = new Roo.grid.ColumnModel(this.cm);
5160         this.cm = this.colModel;
5161         this.cm.xmodule = this.xmodule || false;
5162     }
5163     if (this.store) {
5164         this.store= Roo.factory(this.store, Roo.data);
5165         this.ds = this.store;
5166         this.ds.xmodule = this.xmodule || false;
5167          
5168     }
5169     if (this.footer && this.store) {
5170         this.footer.dataSource = this.ds;
5171         this.footer = Roo.factory(this.footer);
5172     }
5173     
5174     /** @private */
5175     this.addEvents({
5176         /**
5177          * @event cellclick
5178          * Fires when a cell is clicked
5179          * @param {Roo.bootstrap.Table} this
5180          * @param {Roo.Element} el
5181          * @param {Number} rowIndex
5182          * @param {Number} columnIndex
5183          * @param {Roo.EventObject} e
5184          */
5185         "cellclick" : true,
5186         /**
5187          * @event celldblclick
5188          * Fires when a cell is double clicked
5189          * @param {Roo.bootstrap.Table} this
5190          * @param {Roo.Element} el
5191          * @param {Number} rowIndex
5192          * @param {Number} columnIndex
5193          * @param {Roo.EventObject} e
5194          */
5195         "celldblclick" : true,
5196         /**
5197          * @event rowclick
5198          * Fires when a row is clicked
5199          * @param {Roo.bootstrap.Table} this
5200          * @param {Roo.Element} el
5201          * @param {Number} rowIndex
5202          * @param {Roo.EventObject} e
5203          */
5204         "rowclick" : true,
5205         /**
5206          * @event rowdblclick
5207          * Fires when a row is double clicked
5208          * @param {Roo.bootstrap.Table} this
5209          * @param {Roo.Element} el
5210          * @param {Number} rowIndex
5211          * @param {Roo.EventObject} e
5212          */
5213         "rowdblclick" : true,
5214         /**
5215          * @event mouseover
5216          * Fires when a mouseover occur
5217          * @param {Roo.bootstrap.Table} this
5218          * @param {Roo.Element} el
5219          * @param {Number} rowIndex
5220          * @param {Number} columnIndex
5221          * @param {Roo.EventObject} e
5222          */
5223         "mouseover" : true,
5224         /**
5225          * @event mouseout
5226          * Fires when a mouseout occur
5227          * @param {Roo.bootstrap.Table} this
5228          * @param {Roo.Element} el
5229          * @param {Number} rowIndex
5230          * @param {Number} columnIndex
5231          * @param {Roo.EventObject} e
5232          */
5233         "mouseout" : true,
5234         /**
5235          * @event rowclass
5236          * Fires when a row is rendered, so you can change add a style to it.
5237          * @param {Roo.bootstrap.Table} this
5238          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5239          */
5240         'rowclass' : true,
5241           /**
5242          * @event rowsrendered
5243          * Fires when all the  rows have been rendered
5244          * @param {Roo.bootstrap.Table} this
5245          */
5246         'rowsrendered' : true
5247         
5248     });
5249 };
5250
5251 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5252     
5253     cls: false,
5254     align: false,
5255     bgcolor: false,
5256     border: false,
5257     cellpadding: false,
5258     cellspacing: false,
5259     frame: false,
5260     rules: false,
5261     sortable: false,
5262     summary: false,
5263     width: false,
5264     striped : false,
5265     bordered: false,
5266     hover:  false,
5267     condensed : false,
5268     responsive : false,
5269     sm : false,
5270     cm : false,
5271     store : false,
5272     loadMask : false,
5273     tfoot : true,
5274     thead : true,
5275     RowSelection : false,
5276     CellSelection : false,
5277     layout : false,
5278     
5279     // Roo.Element - the tbody
5280     mainBody: false, 
5281     
5282     getAutoCreate : function(){
5283         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5284         
5285         cfg = {
5286             tag: 'table',
5287             cls : 'table',
5288             cn : []
5289         }
5290             
5291         if (this.striped) {
5292             cfg.cls += ' table-striped';
5293         }
5294         
5295         if (this.hover) {
5296             cfg.cls += ' table-hover';
5297         }
5298         if (this.bordered) {
5299             cfg.cls += ' table-bordered';
5300         }
5301         if (this.condensed) {
5302             cfg.cls += ' table-condensed';
5303         }
5304         if (this.responsive) {
5305             cfg.cls += ' table-responsive';
5306         }
5307         
5308         if (this.cls) {
5309             cfg.cls+=  ' ' +this.cls;
5310         }
5311         
5312         // this lot should be simplifed...
5313         
5314         if (this.align) {
5315             cfg.align=this.align;
5316         }
5317         if (this.bgcolor) {
5318             cfg.bgcolor=this.bgcolor;
5319         }
5320         if (this.border) {
5321             cfg.border=this.border;
5322         }
5323         if (this.cellpadding) {
5324             cfg.cellpadding=this.cellpadding;
5325         }
5326         if (this.cellspacing) {
5327             cfg.cellspacing=this.cellspacing;
5328         }
5329         if (this.frame) {
5330             cfg.frame=this.frame;
5331         }
5332         if (this.rules) {
5333             cfg.rules=this.rules;
5334         }
5335         if (this.sortable) {
5336             cfg.sortable=this.sortable;
5337         }
5338         if (this.summary) {
5339             cfg.summary=this.summary;
5340         }
5341         if (this.width) {
5342             cfg.width=this.width;
5343         }
5344         if (this.layout) {
5345             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5346         }
5347         
5348         if(this.store || this.cm){
5349             if(this.thead){
5350                 cfg.cn.push(this.renderHeader());
5351             }
5352             
5353             cfg.cn.push(this.renderBody());
5354             
5355             if(this.tfoot){
5356                 cfg.cn.push(this.renderFooter());
5357             }
5358             
5359             cfg.cls+=  ' TableGrid';
5360         }
5361         
5362         return { cn : [ cfg ] };
5363     },
5364     
5365     initEvents : function()
5366     {   
5367         if(!this.store || !this.cm){
5368             return;
5369         }
5370         
5371         //Roo.log('initEvents with ds!!!!');
5372         
5373         this.mainBody = this.el.select('tbody', true).first();
5374         
5375         
5376         var _this = this;
5377         
5378         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5379             e.on('click', _this.sort, _this);
5380         });
5381         
5382         this.el.on("click", this.onClick, this);
5383         this.el.on("dblclick", this.onDblClick, this);
5384         
5385         // why is this done????? = it breaks dialogs??
5386         //this.parent().el.setStyle('position', 'relative');
5387         
5388         
5389         if (this.footer) {
5390             this.footer.parentId = this.id;
5391             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5392         }
5393         
5394         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5395         
5396         this.store.on('load', this.onLoad, this);
5397         this.store.on('beforeload', this.onBeforeLoad, this);
5398         this.store.on('update', this.onUpdate, this);
5399         this.store.on('add', this.onAdd, this);
5400         
5401     },
5402     
5403     onMouseover : function(e, el)
5404     {
5405         var cell = Roo.get(el);
5406         
5407         if(!cell){
5408             return;
5409         }
5410         
5411         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5412             cell = cell.findParent('td', false, true);
5413         }
5414         
5415         var row = cell.findParent('tr', false, true);
5416         var cellIndex = cell.dom.cellIndex;
5417         var rowIndex = row.dom.rowIndex - 1; // start from 0
5418         
5419         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5420         
5421     },
5422     
5423     onMouseout : function(e, el)
5424     {
5425         var cell = Roo.get(el);
5426         
5427         if(!cell){
5428             return;
5429         }
5430         
5431         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5432             cell = cell.findParent('td', false, true);
5433         }
5434         
5435         var row = cell.findParent('tr', false, true);
5436         var cellIndex = cell.dom.cellIndex;
5437         var rowIndex = row.dom.rowIndex - 1; // start from 0
5438         
5439         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5440         
5441     },
5442     
5443     onClick : function(e, el)
5444     {
5445         var cell = Roo.get(el);
5446         
5447         if(!cell || (!this.CellSelection && !this.RowSelection)){
5448             return;
5449         }
5450         
5451         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5452             cell = cell.findParent('td', false, true);
5453         }
5454         
5455         if(!cell || typeof(cell) == 'undefined'){
5456             return;
5457         }
5458         
5459         var row = cell.findParent('tr', false, true);
5460         
5461         if(!row || typeof(row) == 'undefined'){
5462             return;
5463         }
5464         
5465         var cellIndex = cell.dom.cellIndex;
5466         var rowIndex = this.getRowIndex(row);
5467         
5468         if(this.CellSelection){
5469             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5470         }
5471         
5472         if(this.RowSelection){
5473             this.fireEvent('rowclick', this, row, rowIndex, e);
5474         }
5475         
5476         
5477     },
5478     
5479     onDblClick : function(e,el)
5480     {
5481         var cell = Roo.get(el);
5482         
5483         if(!cell || (!this.CellSelection && !this.RowSelection)){
5484             return;
5485         }
5486         
5487         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5488             cell = cell.findParent('td', false, true);
5489         }
5490         
5491         if(!cell || typeof(cell) == 'undefined'){
5492             return;
5493         }
5494         
5495         var row = cell.findParent('tr', false, true);
5496         
5497         if(!row || typeof(row) == 'undefined'){
5498             return;
5499         }
5500         
5501         var cellIndex = cell.dom.cellIndex;
5502         var rowIndex = this.getRowIndex(row);
5503         
5504         if(this.CellSelection){
5505             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5506         }
5507         
5508         if(this.RowSelection){
5509             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5510         }
5511     },
5512     
5513     sort : function(e,el)
5514     {
5515         var col = Roo.get(el);
5516         
5517         if(!col.hasClass('sortable')){
5518             return;
5519         }
5520         
5521         var sort = col.attr('sort');
5522         var dir = 'ASC';
5523         
5524         if(col.hasClass('glyphicon-arrow-up')){
5525             dir = 'DESC';
5526         }
5527         
5528         this.store.sortInfo = {field : sort, direction : dir};
5529         
5530         if (this.footer) {
5531             Roo.log("calling footer first");
5532             this.footer.onClick('first');
5533         } else {
5534         
5535             this.store.load({ params : { start : 0 } });
5536         }
5537     },
5538     
5539     renderHeader : function()
5540     {
5541         var header = {
5542             tag: 'thead',
5543             cn : []
5544         };
5545         
5546         var cm = this.cm;
5547         
5548         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5549             
5550             var config = cm.config[i];
5551                     
5552             var c = {
5553                 tag: 'th',
5554                 style : '',
5555                 html: cm.getColumnHeader(i)
5556             };
5557             
5558             if(typeof(config.tooltip) != 'undefined'){
5559                 c.tooltip = config.tooltip;
5560             }
5561             
5562             if(typeof(config.colspan) != 'undefined'){
5563                 c.colspan = config.colspan;
5564             }
5565             
5566             if(typeof(config.hidden) != 'undefined' && config.hidden){
5567                 c.style += ' display:none;';
5568             }
5569             
5570             if(typeof(config.dataIndex) != 'undefined'){
5571                 c.sort = config.dataIndex;
5572             }
5573             
5574             if(typeof(config.sortable) != 'undefined' && config.sortable){
5575                 c.cls = 'sortable';
5576             }
5577             
5578             if(typeof(config.align) != 'undefined' && config.align.length){
5579                 c.style += ' text-align:' + config.align + ';';
5580             }
5581             
5582             if(typeof(config.width) != 'undefined'){
5583                 c.style += ' width:' + config.width + 'px;';
5584             }
5585             
5586             header.cn.push(c)
5587         }
5588         
5589         return header;
5590     },
5591     
5592     renderBody : function()
5593     {
5594         var body = {
5595             tag: 'tbody',
5596             cn : [
5597                 {
5598                     tag: 'tr',
5599                     cn : [
5600                         {
5601                             tag : 'td',
5602                             colspan :  this.cm.getColumnCount()
5603                         }
5604                     ]
5605                 }
5606             ]
5607         };
5608         
5609         return body;
5610     },
5611     
5612     renderFooter : function()
5613     {
5614         var footer = {
5615             tag: 'tfoot',
5616             cn : [
5617                 {
5618                     tag: 'tr',
5619                     cn : [
5620                         {
5621                             tag : 'td',
5622                             colspan :  this.cm.getColumnCount()
5623                         }
5624                     ]
5625                 }
5626             ]
5627         };
5628         
5629         return footer;
5630     },
5631     
5632     
5633     
5634     onLoad : function()
5635     {
5636         Roo.log('ds onload');
5637         this.clear();
5638         
5639         var _this = this;
5640         var cm = this.cm;
5641         var ds = this.store;
5642         
5643         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5644             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5645             
5646             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5647                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5648             }
5649             
5650             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5651                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5652             }
5653         });
5654         
5655         var tbody =  this.mainBody;
5656               
5657         if(ds.getCount() > 0){
5658             ds.data.each(function(d,rowIndex){
5659                 var row =  this.renderRow(cm, ds, rowIndex);
5660                 
5661                 tbody.createChild(row);
5662                 
5663                 var _this = this;
5664                 
5665                 if(row.cellObjects.length){
5666                     Roo.each(row.cellObjects, function(r){
5667                         _this.renderCellObject(r);
5668                     })
5669                 }
5670                 
5671             }, this);
5672         }
5673         
5674         Roo.each(this.el.select('tbody td', true).elements, function(e){
5675             e.on('mouseover', _this.onMouseover, _this);
5676         });
5677         
5678         Roo.each(this.el.select('tbody td', true).elements, function(e){
5679             e.on('mouseout', _this.onMouseout, _this);
5680         });
5681         this.fireEvent('rowsrendered', this);
5682         //if(this.loadMask){
5683         //    this.maskEl.hide();
5684         //}
5685     },
5686     
5687     
5688     onUpdate : function(ds,record)
5689     {
5690         this.refreshRow(record);
5691     },
5692     
5693     onRemove : function(ds, record, index, isUpdate){
5694         if(isUpdate !== true){
5695             this.fireEvent("beforerowremoved", this, index, record);
5696         }
5697         var bt = this.mainBody.dom;
5698         
5699         var rows = this.el.select('tbody > tr', true).elements;
5700         
5701         if(typeof(rows[index]) != 'undefined'){
5702             bt.removeChild(rows[index].dom);
5703         }
5704         
5705 //        if(bt.rows[index]){
5706 //            bt.removeChild(bt.rows[index]);
5707 //        }
5708         
5709         if(isUpdate !== true){
5710             //this.stripeRows(index);
5711             //this.syncRowHeights(index, index);
5712             //this.layout();
5713             this.fireEvent("rowremoved", this, index, record);
5714         }
5715     },
5716     
5717     onAdd : function(ds, records, rowIndex)
5718     {
5719         //Roo.log('on Add called');
5720         // - note this does not handle multiple adding very well..
5721         var bt = this.mainBody.dom;
5722         for (var i =0 ; i < records.length;i++) {
5723             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
5724             //Roo.log(records[i]);
5725             //Roo.log(this.store.getAt(rowIndex+i));
5726             this.insertRow(this.store, rowIndex + i, false);
5727             return;
5728         }
5729         
5730     },
5731     
5732     
5733     refreshRow : function(record){
5734         var ds = this.store, index;
5735         if(typeof record == 'number'){
5736             index = record;
5737             record = ds.getAt(index);
5738         }else{
5739             index = ds.indexOf(record);
5740         }
5741         this.insertRow(ds, index, true);
5742         this.onRemove(ds, record, index+1, true);
5743         //this.syncRowHeights(index, index);
5744         //this.layout();
5745         this.fireEvent("rowupdated", this, index, record);
5746     },
5747     
5748     insertRow : function(dm, rowIndex, isUpdate){
5749         
5750         if(!isUpdate){
5751             this.fireEvent("beforerowsinserted", this, rowIndex);
5752         }
5753             //var s = this.getScrollState();
5754         var row = this.renderRow(this.cm, this.store, rowIndex);
5755         // insert before rowIndex..
5756         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5757         
5758         var _this = this;
5759                 
5760         if(row.cellObjects.length){
5761             Roo.each(row.cellObjects, function(r){
5762                 _this.renderCellObject(r);
5763             })
5764         }
5765             
5766         if(!isUpdate){
5767             this.fireEvent("rowsinserted", this, rowIndex);
5768             //this.syncRowHeights(firstRow, lastRow);
5769             //this.stripeRows(firstRow);
5770             //this.layout();
5771         }
5772         
5773     },
5774     
5775     
5776     getRowDom : function(rowIndex)
5777     {
5778         var rows = this.el.select('tbody > tr', true).elements;
5779         
5780         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
5781         
5782     },
5783     // returns the object tree for a tr..
5784   
5785     
5786     renderRow : function(cm, ds, rowIndex) 
5787     {
5788         
5789         var d = ds.getAt(rowIndex);
5790         
5791         var row = {
5792             tag : 'tr',
5793             cn : []
5794         };
5795             
5796         var cellObjects = [];
5797         
5798         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5799             var config = cm.config[i];
5800             
5801             var renderer = cm.getRenderer(i);
5802             var value = '';
5803             var id = false;
5804             
5805             if(typeof(renderer) !== 'undefined'){
5806                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5807             }
5808             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5809             // and are rendered into the cells after the row is rendered - using the id for the element.
5810             
5811             if(typeof(value) === 'object'){
5812                 id = Roo.id();
5813                 cellObjects.push({
5814                     container : id,
5815                     cfg : value 
5816                 })
5817             }
5818             
5819             var rowcfg = {
5820                 record: d,
5821                 rowIndex : rowIndex,
5822                 colIndex : i,
5823                 rowClass : ''
5824             }
5825
5826             this.fireEvent('rowclass', this, rowcfg);
5827             
5828             var td = {
5829                 tag: 'td',
5830                 cls : rowcfg.rowClass,
5831                 style: '',
5832                 html: (typeof(value) === 'object') ? '' : value
5833             };
5834             
5835             if (id) {
5836                 td.id = id;
5837             }
5838             
5839             if(typeof(config.colspan) != 'undefined'){
5840                 td.colspan = config.colspan;
5841             }
5842             
5843             if(typeof(config.hidden) != 'undefined' && config.hidden){
5844                 td.style += ' display:none;';
5845             }
5846             
5847             if(typeof(config.align) != 'undefined' && config.align.length){
5848                 td.style += ' text-align:' + config.align + ';';
5849             }
5850             
5851             if(typeof(config.width) != 'undefined'){
5852                 td.style += ' width:' +  config.width + 'px;';
5853             }
5854             
5855             if(typeof(config.cursor) != 'undefined'){
5856                 td.style += ' cursor:' +  config.cursor + ';';
5857             }
5858              
5859             row.cn.push(td);
5860            
5861         }
5862         
5863         row.cellObjects = cellObjects;
5864         
5865         return row;
5866           
5867     },
5868     
5869     
5870     
5871     onBeforeLoad : function()
5872     {
5873         //Roo.log('ds onBeforeLoad');
5874         
5875         //this.clear();
5876         
5877         //if(this.loadMask){
5878         //    this.maskEl.show();
5879         //}
5880     },
5881      /**
5882      * Remove all rows
5883      */
5884     clear : function()
5885     {
5886         this.el.select('tbody', true).first().dom.innerHTML = '';
5887     },
5888     /**
5889      * Show or hide a row.
5890      * @param {Number} rowIndex to show or hide
5891      * @param {Boolean} state hide
5892      */
5893     setRowVisibility : function(rowIndex, state)
5894     {
5895         var bt = this.mainBody.dom;
5896         
5897         var rows = this.el.select('tbody > tr', true).elements;
5898         
5899         if(typeof(rows[rowIndex]) == 'undefined'){
5900             return;
5901         }
5902         rows[rowIndex].dom.style.display = state ? '' : 'none';
5903     },
5904     
5905     
5906     getSelectionModel : function(){
5907         if(!this.selModel){
5908             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5909         }
5910         return this.selModel;
5911     },
5912     /*
5913      * Render the Roo.bootstrap object from renderder
5914      */
5915     renderCellObject : function(r)
5916     {
5917         var _this = this;
5918         
5919         var t = r.cfg.render(r.container);
5920         
5921         if(r.cfg.cn){
5922             Roo.each(r.cfg.cn, function(c){
5923                 var child = {
5924                     container: t.getChildContainer(),
5925                     cfg: c
5926                 }
5927                 _this.renderCellObject(child);
5928             })
5929         }
5930     },
5931     
5932     getRowIndex : function(row)
5933     {
5934         var rowIndex = -1;
5935         
5936         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
5937             if(el != row){
5938                 return;
5939             }
5940             
5941             rowIndex = index;
5942         });
5943         
5944         return rowIndex;
5945     }
5946    
5947 });
5948
5949  
5950
5951  /*
5952  * - LGPL
5953  *
5954  * table cell
5955  * 
5956  */
5957
5958 /**
5959  * @class Roo.bootstrap.TableCell
5960  * @extends Roo.bootstrap.Component
5961  * Bootstrap TableCell class
5962  * @cfg {String} html cell contain text
5963  * @cfg {String} cls cell class
5964  * @cfg {String} tag cell tag (td|th) default td
5965  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5966  * @cfg {String} align Aligns the content in a cell
5967  * @cfg {String} axis Categorizes cells
5968  * @cfg {String} bgcolor Specifies the background color of a cell
5969  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5970  * @cfg {Number} colspan Specifies the number of columns a cell should span
5971  * @cfg {String} headers Specifies one or more header cells a cell is related to
5972  * @cfg {Number} height Sets the height of a cell
5973  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5974  * @cfg {Number} rowspan Sets the number of rows a cell should span
5975  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5976  * @cfg {String} valign Vertical aligns the content in a cell
5977  * @cfg {Number} width Specifies the width of a cell
5978  * 
5979  * @constructor
5980  * Create a new TableCell
5981  * @param {Object} config The config object
5982  */
5983
5984 Roo.bootstrap.TableCell = function(config){
5985     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5986 };
5987
5988 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5989     
5990     html: false,
5991     cls: false,
5992     tag: false,
5993     abbr: false,
5994     align: false,
5995     axis: false,
5996     bgcolor: false,
5997     charoff: false,
5998     colspan: false,
5999     headers: false,
6000     height: false,
6001     nowrap: false,
6002     rowspan: false,
6003     scope: false,
6004     valign: false,
6005     width: false,
6006     
6007     
6008     getAutoCreate : function(){
6009         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6010         
6011         cfg = {
6012             tag: 'td'
6013         }
6014         
6015         if(this.tag){
6016             cfg.tag = this.tag;
6017         }
6018         
6019         if (this.html) {
6020             cfg.html=this.html
6021         }
6022         if (this.cls) {
6023             cfg.cls=this.cls
6024         }
6025         if (this.abbr) {
6026             cfg.abbr=this.abbr
6027         }
6028         if (this.align) {
6029             cfg.align=this.align
6030         }
6031         if (this.axis) {
6032             cfg.axis=this.axis
6033         }
6034         if (this.bgcolor) {
6035             cfg.bgcolor=this.bgcolor
6036         }
6037         if (this.charoff) {
6038             cfg.charoff=this.charoff
6039         }
6040         if (this.colspan) {
6041             cfg.colspan=this.colspan
6042         }
6043         if (this.headers) {
6044             cfg.headers=this.headers
6045         }
6046         if (this.height) {
6047             cfg.height=this.height
6048         }
6049         if (this.nowrap) {
6050             cfg.nowrap=this.nowrap
6051         }
6052         if (this.rowspan) {
6053             cfg.rowspan=this.rowspan
6054         }
6055         if (this.scope) {
6056             cfg.scope=this.scope
6057         }
6058         if (this.valign) {
6059             cfg.valign=this.valign
6060         }
6061         if (this.width) {
6062             cfg.width=this.width
6063         }
6064         
6065         
6066         return cfg;
6067     }
6068    
6069 });
6070
6071  
6072
6073  /*
6074  * - LGPL
6075  *
6076  * table row
6077  * 
6078  */
6079
6080 /**
6081  * @class Roo.bootstrap.TableRow
6082  * @extends Roo.bootstrap.Component
6083  * Bootstrap TableRow class
6084  * @cfg {String} cls row class
6085  * @cfg {String} align Aligns the content in a table row
6086  * @cfg {String} bgcolor Specifies a background color for a table row
6087  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6088  * @cfg {String} valign Vertical aligns the content in a table row
6089  * 
6090  * @constructor
6091  * Create a new TableRow
6092  * @param {Object} config The config object
6093  */
6094
6095 Roo.bootstrap.TableRow = function(config){
6096     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6097 };
6098
6099 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6100     
6101     cls: false,
6102     align: false,
6103     bgcolor: false,
6104     charoff: false,
6105     valign: false,
6106     
6107     getAutoCreate : function(){
6108         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6109         
6110         cfg = {
6111             tag: 'tr'
6112         }
6113             
6114         if(this.cls){
6115             cfg.cls = this.cls;
6116         }
6117         if(this.align){
6118             cfg.align = this.align;
6119         }
6120         if(this.bgcolor){
6121             cfg.bgcolor = this.bgcolor;
6122         }
6123         if(this.charoff){
6124             cfg.charoff = this.charoff;
6125         }
6126         if(this.valign){
6127             cfg.valign = this.valign;
6128         }
6129         
6130         return cfg;
6131     }
6132    
6133 });
6134
6135  
6136
6137  /*
6138  * - LGPL
6139  *
6140  * table body
6141  * 
6142  */
6143
6144 /**
6145  * @class Roo.bootstrap.TableBody
6146  * @extends Roo.bootstrap.Component
6147  * Bootstrap TableBody class
6148  * @cfg {String} cls element class
6149  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6150  * @cfg {String} align Aligns the content inside the element
6151  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6152  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6153  * 
6154  * @constructor
6155  * Create a new TableBody
6156  * @param {Object} config The config object
6157  */
6158
6159 Roo.bootstrap.TableBody = function(config){
6160     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6161 };
6162
6163 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6164     
6165     cls: false,
6166     tag: false,
6167     align: false,
6168     charoff: false,
6169     valign: false,
6170     
6171     getAutoCreate : function(){
6172         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6173         
6174         cfg = {
6175             tag: 'tbody'
6176         }
6177             
6178         if (this.cls) {
6179             cfg.cls=this.cls
6180         }
6181         if(this.tag){
6182             cfg.tag = this.tag;
6183         }
6184         
6185         if(this.align){
6186             cfg.align = this.align;
6187         }
6188         if(this.charoff){
6189             cfg.charoff = this.charoff;
6190         }
6191         if(this.valign){
6192             cfg.valign = this.valign;
6193         }
6194         
6195         return cfg;
6196     }
6197     
6198     
6199 //    initEvents : function()
6200 //    {
6201 //        
6202 //        if(!this.store){
6203 //            return;
6204 //        }
6205 //        
6206 //        this.store = Roo.factory(this.store, Roo.data);
6207 //        this.store.on('load', this.onLoad, this);
6208 //        
6209 //        this.store.load();
6210 //        
6211 //    },
6212 //    
6213 //    onLoad: function () 
6214 //    {   
6215 //        this.fireEvent('load', this);
6216 //    }
6217 //    
6218 //   
6219 });
6220
6221  
6222
6223  /*
6224  * Based on:
6225  * Ext JS Library 1.1.1
6226  * Copyright(c) 2006-2007, Ext JS, LLC.
6227  *
6228  * Originally Released Under LGPL - original licence link has changed is not relivant.
6229  *
6230  * Fork - LGPL
6231  * <script type="text/javascript">
6232  */
6233
6234 // as we use this in bootstrap.
6235 Roo.namespace('Roo.form');
6236  /**
6237  * @class Roo.form.Action
6238  * Internal Class used to handle form actions
6239  * @constructor
6240  * @param {Roo.form.BasicForm} el The form element or its id
6241  * @param {Object} config Configuration options
6242  */
6243
6244  
6245  
6246 // define the action interface
6247 Roo.form.Action = function(form, options){
6248     this.form = form;
6249     this.options = options || {};
6250 };
6251 /**
6252  * Client Validation Failed
6253  * @const 
6254  */
6255 Roo.form.Action.CLIENT_INVALID = 'client';
6256 /**
6257  * Server Validation Failed
6258  * @const 
6259  */
6260 Roo.form.Action.SERVER_INVALID = 'server';
6261  /**
6262  * Connect to Server Failed
6263  * @const 
6264  */
6265 Roo.form.Action.CONNECT_FAILURE = 'connect';
6266 /**
6267  * Reading Data from Server Failed
6268  * @const 
6269  */
6270 Roo.form.Action.LOAD_FAILURE = 'load';
6271
6272 Roo.form.Action.prototype = {
6273     type : 'default',
6274     failureType : undefined,
6275     response : undefined,
6276     result : undefined,
6277
6278     // interface method
6279     run : function(options){
6280
6281     },
6282
6283     // interface method
6284     success : function(response){
6285
6286     },
6287
6288     // interface method
6289     handleResponse : function(response){
6290
6291     },
6292
6293     // default connection failure
6294     failure : function(response){
6295         
6296         this.response = response;
6297         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6298         this.form.afterAction(this, false);
6299     },
6300
6301     processResponse : function(response){
6302         this.response = response;
6303         if(!response.responseText){
6304             return true;
6305         }
6306         this.result = this.handleResponse(response);
6307         return this.result;
6308     },
6309
6310     // utility functions used internally
6311     getUrl : function(appendParams){
6312         var url = this.options.url || this.form.url || this.form.el.dom.action;
6313         if(appendParams){
6314             var p = this.getParams();
6315             if(p){
6316                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6317             }
6318         }
6319         return url;
6320     },
6321
6322     getMethod : function(){
6323         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6324     },
6325
6326     getParams : function(){
6327         var bp = this.form.baseParams;
6328         var p = this.options.params;
6329         if(p){
6330             if(typeof p == "object"){
6331                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6332             }else if(typeof p == 'string' && bp){
6333                 p += '&' + Roo.urlEncode(bp);
6334             }
6335         }else if(bp){
6336             p = Roo.urlEncode(bp);
6337         }
6338         return p;
6339     },
6340
6341     createCallback : function(){
6342         return {
6343             success: this.success,
6344             failure: this.failure,
6345             scope: this,
6346             timeout: (this.form.timeout*1000),
6347             upload: this.form.fileUpload ? this.success : undefined
6348         };
6349     }
6350 };
6351
6352 Roo.form.Action.Submit = function(form, options){
6353     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6354 };
6355
6356 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6357     type : 'submit',
6358
6359     haveProgress : false,
6360     uploadComplete : false,
6361     
6362     // uploadProgress indicator.
6363     uploadProgress : function()
6364     {
6365         if (!this.form.progressUrl) {
6366             return;
6367         }
6368         
6369         if (!this.haveProgress) {
6370             Roo.MessageBox.progress("Uploading", "Uploading");
6371         }
6372         if (this.uploadComplete) {
6373            Roo.MessageBox.hide();
6374            return;
6375         }
6376         
6377         this.haveProgress = true;
6378    
6379         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6380         
6381         var c = new Roo.data.Connection();
6382         c.request({
6383             url : this.form.progressUrl,
6384             params: {
6385                 id : uid
6386             },
6387             method: 'GET',
6388             success : function(req){
6389                //console.log(data);
6390                 var rdata = false;
6391                 var edata;
6392                 try  {
6393                    rdata = Roo.decode(req.responseText)
6394                 } catch (e) {
6395                     Roo.log("Invalid data from server..");
6396                     Roo.log(edata);
6397                     return;
6398                 }
6399                 if (!rdata || !rdata.success) {
6400                     Roo.log(rdata);
6401                     Roo.MessageBox.alert(Roo.encode(rdata));
6402                     return;
6403                 }
6404                 var data = rdata.data;
6405                 
6406                 if (this.uploadComplete) {
6407                    Roo.MessageBox.hide();
6408                    return;
6409                 }
6410                    
6411                 if (data){
6412                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6413                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6414                     );
6415                 }
6416                 this.uploadProgress.defer(2000,this);
6417             },
6418        
6419             failure: function(data) {
6420                 Roo.log('progress url failed ');
6421                 Roo.log(data);
6422             },
6423             scope : this
6424         });
6425            
6426     },
6427     
6428     
6429     run : function()
6430     {
6431         // run get Values on the form, so it syncs any secondary forms.
6432         this.form.getValues();
6433         
6434         var o = this.options;
6435         var method = this.getMethod();
6436         var isPost = method == 'POST';
6437         if(o.clientValidation === false || this.form.isValid()){
6438             
6439             if (this.form.progressUrl) {
6440                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6441                     (new Date() * 1) + '' + Math.random());
6442                     
6443             } 
6444             
6445             
6446             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6447                 form:this.form.el.dom,
6448                 url:this.getUrl(!isPost),
6449                 method: method,
6450                 params:isPost ? this.getParams() : null,
6451                 isUpload: this.form.fileUpload
6452             }));
6453             
6454             this.uploadProgress();
6455
6456         }else if (o.clientValidation !== false){ // client validation failed
6457             this.failureType = Roo.form.Action.CLIENT_INVALID;
6458             this.form.afterAction(this, false);
6459         }
6460     },
6461
6462     success : function(response)
6463     {
6464         this.uploadComplete= true;
6465         if (this.haveProgress) {
6466             Roo.MessageBox.hide();
6467         }
6468         
6469         
6470         var result = this.processResponse(response);
6471         if(result === true || result.success){
6472             this.form.afterAction(this, true);
6473             return;
6474         }
6475         if(result.errors){
6476             this.form.markInvalid(result.errors);
6477             this.failureType = Roo.form.Action.SERVER_INVALID;
6478         }
6479         this.form.afterAction(this, false);
6480     },
6481     failure : function(response)
6482     {
6483         this.uploadComplete= true;
6484         if (this.haveProgress) {
6485             Roo.MessageBox.hide();
6486         }
6487         
6488         this.response = response;
6489         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6490         this.form.afterAction(this, false);
6491     },
6492     
6493     handleResponse : function(response){
6494         if(this.form.errorReader){
6495             var rs = this.form.errorReader.read(response);
6496             var errors = [];
6497             if(rs.records){
6498                 for(var i = 0, len = rs.records.length; i < len; i++) {
6499                     var r = rs.records[i];
6500                     errors[i] = r.data;
6501                 }
6502             }
6503             if(errors.length < 1){
6504                 errors = null;
6505             }
6506             return {
6507                 success : rs.success,
6508                 errors : errors
6509             };
6510         }
6511         var ret = false;
6512         try {
6513             ret = Roo.decode(response.responseText);
6514         } catch (e) {
6515             ret = {
6516                 success: false,
6517                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6518                 errors : []
6519             };
6520         }
6521         return ret;
6522         
6523     }
6524 });
6525
6526
6527 Roo.form.Action.Load = function(form, options){
6528     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6529     this.reader = this.form.reader;
6530 };
6531
6532 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6533     type : 'load',
6534
6535     run : function(){
6536         
6537         Roo.Ajax.request(Roo.apply(
6538                 this.createCallback(), {
6539                     method:this.getMethod(),
6540                     url:this.getUrl(false),
6541                     params:this.getParams()
6542         }));
6543     },
6544
6545     success : function(response){
6546         
6547         var result = this.processResponse(response);
6548         if(result === true || !result.success || !result.data){
6549             this.failureType = Roo.form.Action.LOAD_FAILURE;
6550             this.form.afterAction(this, false);
6551             return;
6552         }
6553         this.form.clearInvalid();
6554         this.form.setValues(result.data);
6555         this.form.afterAction(this, true);
6556     },
6557
6558     handleResponse : function(response){
6559         if(this.form.reader){
6560             var rs = this.form.reader.read(response);
6561             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6562             return {
6563                 success : rs.success,
6564                 data : data
6565             };
6566         }
6567         return Roo.decode(response.responseText);
6568     }
6569 });
6570
6571 Roo.form.Action.ACTION_TYPES = {
6572     'load' : Roo.form.Action.Load,
6573     'submit' : Roo.form.Action.Submit
6574 };/*
6575  * - LGPL
6576  *
6577  * form
6578  * 
6579  */
6580
6581 /**
6582  * @class Roo.bootstrap.Form
6583  * @extends Roo.bootstrap.Component
6584  * Bootstrap Form class
6585  * @cfg {String} method  GET | POST (default POST)
6586  * @cfg {String} labelAlign top | left (default top)
6587  * @cfg {String} align left  | right - for navbars
6588  * @cfg {Boolean} loadMask load mask when submit (default true)
6589
6590  * 
6591  * @constructor
6592  * Create a new Form
6593  * @param {Object} config The config object
6594  */
6595
6596
6597 Roo.bootstrap.Form = function(config){
6598     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6599     this.addEvents({
6600         /**
6601          * @event clientvalidation
6602          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6603          * @param {Form} this
6604          * @param {Boolean} valid true if the form has passed client-side validation
6605          */
6606         clientvalidation: true,
6607         /**
6608          * @event beforeaction
6609          * Fires before any action is performed. Return false to cancel the action.
6610          * @param {Form} this
6611          * @param {Action} action The action to be performed
6612          */
6613         beforeaction: true,
6614         /**
6615          * @event actionfailed
6616          * Fires when an action fails.
6617          * @param {Form} this
6618          * @param {Action} action The action that failed
6619          */
6620         actionfailed : true,
6621         /**
6622          * @event actioncomplete
6623          * Fires when an action is completed.
6624          * @param {Form} this
6625          * @param {Action} action The action that completed
6626          */
6627         actioncomplete : true
6628     });
6629     
6630 };
6631
6632 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6633       
6634      /**
6635      * @cfg {String} method
6636      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6637      */
6638     method : 'POST',
6639     /**
6640      * @cfg {String} url
6641      * The URL to use for form actions if one isn't supplied in the action options.
6642      */
6643     /**
6644      * @cfg {Boolean} fileUpload
6645      * Set to true if this form is a file upload.
6646      */
6647      
6648     /**
6649      * @cfg {Object} baseParams
6650      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6651      */
6652       
6653     /**
6654      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6655      */
6656     timeout: 30,
6657     /**
6658      * @cfg {Sting} align (left|right) for navbar forms
6659      */
6660     align : 'left',
6661
6662     // private
6663     activeAction : null,
6664  
6665     /**
6666      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6667      * element by passing it or its id or mask the form itself by passing in true.
6668      * @type Mixed
6669      */
6670     waitMsgTarget : false,
6671     
6672     loadMask : true,
6673     
6674     getAutoCreate : function(){
6675         
6676         var cfg = {
6677             tag: 'form',
6678             method : this.method || 'POST',
6679             id : this.id || Roo.id(),
6680             cls : ''
6681         }
6682         if (this.parent().xtype.match(/^Nav/)) {
6683             cfg.cls = 'navbar-form navbar-' + this.align;
6684             
6685         }
6686         
6687         if (this.labelAlign == 'left' ) {
6688             cfg.cls += ' form-horizontal';
6689         }
6690         
6691         
6692         return cfg;
6693     },
6694     initEvents : function()
6695     {
6696         this.el.on('submit', this.onSubmit, this);
6697         // this was added as random key presses on the form where triggering form submit.
6698         this.el.on('keypress', function(e) {
6699             if (e.getCharCode() != 13) {
6700                 return true;
6701             }
6702             // we might need to allow it for textareas.. and some other items.
6703             // check e.getTarget().
6704             
6705             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6706                 return true;
6707             }
6708         
6709             Roo.log("keypress blocked");
6710             
6711             e.preventDefault();
6712             return false;
6713         });
6714         
6715     },
6716     // private
6717     onSubmit : function(e){
6718         e.stopEvent();
6719     },
6720     
6721      /**
6722      * Returns true if client-side validation on the form is successful.
6723      * @return Boolean
6724      */
6725     isValid : function(){
6726         var items = this.getItems();
6727         var valid = true;
6728         items.each(function(f){
6729            if(!f.validate()){
6730                valid = false;
6731                
6732            }
6733         });
6734         return valid;
6735     },
6736     /**
6737      * Returns true if any fields in this form have changed since their original load.
6738      * @return Boolean
6739      */
6740     isDirty : function(){
6741         var dirty = false;
6742         var items = this.getItems();
6743         items.each(function(f){
6744            if(f.isDirty()){
6745                dirty = true;
6746                return false;
6747            }
6748            return true;
6749         });
6750         return dirty;
6751     },
6752      /**
6753      * Performs a predefined action (submit or load) or custom actions you define on this form.
6754      * @param {String} actionName The name of the action type
6755      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6756      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6757      * accept other config options):
6758      * <pre>
6759 Property          Type             Description
6760 ----------------  ---------------  ----------------------------------------------------------------------------------
6761 url               String           The url for the action (defaults to the form's url)
6762 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6763 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6764 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6765                                    validate the form on the client (defaults to false)
6766      * </pre>
6767      * @return {BasicForm} this
6768      */
6769     doAction : function(action, options){
6770         if(typeof action == 'string'){
6771             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6772         }
6773         if(this.fireEvent('beforeaction', this, action) !== false){
6774             this.beforeAction(action);
6775             action.run.defer(100, action);
6776         }
6777         return this;
6778     },
6779     
6780     // private
6781     beforeAction : function(action){
6782         var o = action.options;
6783         
6784         if(this.loadMask){
6785             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6786         }
6787         // not really supported yet.. ??
6788         
6789         //if(this.waitMsgTarget === true){
6790         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6791         //}else if(this.waitMsgTarget){
6792         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6793         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6794         //}else {
6795         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6796        // }
6797          
6798     },
6799
6800     // private
6801     afterAction : function(action, success){
6802         this.activeAction = null;
6803         var o = action.options;
6804         
6805         //if(this.waitMsgTarget === true){
6806             this.el.unmask();
6807         //}else if(this.waitMsgTarget){
6808         //    this.waitMsgTarget.unmask();
6809         //}else{
6810         //    Roo.MessageBox.updateProgress(1);
6811         //    Roo.MessageBox.hide();
6812        // }
6813         // 
6814         if(success){
6815             if(o.reset){
6816                 this.reset();
6817             }
6818             Roo.callback(o.success, o.scope, [this, action]);
6819             this.fireEvent('actioncomplete', this, action);
6820             
6821         }else{
6822             
6823             // failure condition..
6824             // we have a scenario where updates need confirming.
6825             // eg. if a locking scenario exists..
6826             // we look for { errors : { needs_confirm : true }} in the response.
6827             if (
6828                 (typeof(action.result) != 'undefined')  &&
6829                 (typeof(action.result.errors) != 'undefined')  &&
6830                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6831            ){
6832                 var _t = this;
6833                 Roo.log("not supported yet");
6834                  /*
6835                 
6836                 Roo.MessageBox.confirm(
6837                     "Change requires confirmation",
6838                     action.result.errorMsg,
6839                     function(r) {
6840                         if (r != 'yes') {
6841                             return;
6842                         }
6843                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6844                     }
6845                     
6846                 );
6847                 */
6848                 
6849                 
6850                 return;
6851             }
6852             
6853             Roo.callback(o.failure, o.scope, [this, action]);
6854             // show an error message if no failed handler is set..
6855             if (!this.hasListener('actionfailed')) {
6856                 Roo.log("need to add dialog support");
6857                 /*
6858                 Roo.MessageBox.alert("Error",
6859                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6860                         action.result.errorMsg :
6861                         "Saving Failed, please check your entries or try again"
6862                 );
6863                 */
6864             }
6865             
6866             this.fireEvent('actionfailed', this, action);
6867         }
6868         
6869     },
6870     /**
6871      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6872      * @param {String} id The value to search for
6873      * @return Field
6874      */
6875     findField : function(id){
6876         var items = this.getItems();
6877         var field = items.get(id);
6878         if(!field){
6879              items.each(function(f){
6880                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6881                     field = f;
6882                     return false;
6883                 }
6884                 return true;
6885             });
6886         }
6887         return field || null;
6888     },
6889      /**
6890      * Mark fields in this form invalid in bulk.
6891      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6892      * @return {BasicForm} this
6893      */
6894     markInvalid : function(errors){
6895         if(errors instanceof Array){
6896             for(var i = 0, len = errors.length; i < len; i++){
6897                 var fieldError = errors[i];
6898                 var f = this.findField(fieldError.id);
6899                 if(f){
6900                     f.markInvalid(fieldError.msg);
6901                 }
6902             }
6903         }else{
6904             var field, id;
6905             for(id in errors){
6906                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6907                     field.markInvalid(errors[id]);
6908                 }
6909             }
6910         }
6911         //Roo.each(this.childForms || [], function (f) {
6912         //    f.markInvalid(errors);
6913         //});
6914         
6915         return this;
6916     },
6917
6918     /**
6919      * Set values for fields in this form in bulk.
6920      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6921      * @return {BasicForm} this
6922      */
6923     setValues : function(values){
6924         if(values instanceof Array){ // array of objects
6925             for(var i = 0, len = values.length; i < len; i++){
6926                 var v = values[i];
6927                 var f = this.findField(v.id);
6928                 if(f){
6929                     f.setValue(v.value);
6930                     if(this.trackResetOnLoad){
6931                         f.originalValue = f.getValue();
6932                     }
6933                 }
6934             }
6935         }else{ // object hash
6936             var field, id;
6937             for(id in values){
6938                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6939                     
6940                     if (field.setFromData && 
6941                         field.valueField && 
6942                         field.displayField &&
6943                         // combos' with local stores can 
6944                         // be queried via setValue()
6945                         // to set their value..
6946                         (field.store && !field.store.isLocal)
6947                         ) {
6948                         // it's a combo
6949                         var sd = { };
6950                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6951                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6952                         field.setFromData(sd);
6953                         
6954                     } else {
6955                         field.setValue(values[id]);
6956                     }
6957                     
6958                     
6959                     if(this.trackResetOnLoad){
6960                         field.originalValue = field.getValue();
6961                     }
6962                 }
6963             }
6964         }
6965          
6966         //Roo.each(this.childForms || [], function (f) {
6967         //    f.setValues(values);
6968         //});
6969                 
6970         return this;
6971     },
6972
6973     /**
6974      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6975      * they are returned as an array.
6976      * @param {Boolean} asString
6977      * @return {Object}
6978      */
6979     getValues : function(asString){
6980         //if (this.childForms) {
6981             // copy values from the child forms
6982         //    Roo.each(this.childForms, function (f) {
6983         //        this.setValues(f.getValues());
6984         //    }, this);
6985         //}
6986         
6987         
6988         
6989         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6990         if(asString === true){
6991             return fs;
6992         }
6993         return Roo.urlDecode(fs);
6994     },
6995     
6996     /**
6997      * Returns the fields in this form as an object with key/value pairs. 
6998      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6999      * @return {Object}
7000      */
7001     getFieldValues : function(with_hidden)
7002     {
7003         var items = this.getItems();
7004         var ret = {};
7005         items.each(function(f){
7006             if (!f.getName()) {
7007                 return;
7008             }
7009             var v = f.getValue();
7010             if (f.inputType =='radio') {
7011                 if (typeof(ret[f.getName()]) == 'undefined') {
7012                     ret[f.getName()] = ''; // empty..
7013                 }
7014                 
7015                 if (!f.el.dom.checked) {
7016                     return;
7017                     
7018                 }
7019                 v = f.el.dom.value;
7020                 
7021             }
7022             
7023             // not sure if this supported any more..
7024             if ((typeof(v) == 'object') && f.getRawValue) {
7025                 v = f.getRawValue() ; // dates..
7026             }
7027             // combo boxes where name != hiddenName...
7028             if (f.name != f.getName()) {
7029                 ret[f.name] = f.getRawValue();
7030             }
7031             ret[f.getName()] = v;
7032         });
7033         
7034         return ret;
7035     },
7036
7037     /**
7038      * Clears all invalid messages in this form.
7039      * @return {BasicForm} this
7040      */
7041     clearInvalid : function(){
7042         var items = this.getItems();
7043         
7044         items.each(function(f){
7045            f.clearInvalid();
7046         });
7047         
7048         
7049         
7050         return this;
7051     },
7052
7053     /**
7054      * Resets this form.
7055      * @return {BasicForm} this
7056      */
7057     reset : function(){
7058         var items = this.getItems();
7059         items.each(function(f){
7060             f.reset();
7061         });
7062         
7063         Roo.each(this.childForms || [], function (f) {
7064             f.reset();
7065         });
7066        
7067         
7068         return this;
7069     },
7070     getItems : function()
7071     {
7072         var r=new Roo.util.MixedCollection(false, function(o){
7073             return o.id || (o.id = Roo.id());
7074         });
7075         var iter = function(el) {
7076             if (el.inputEl) {
7077                 r.add(el);
7078             }
7079             if (!el.items) {
7080                 return;
7081             }
7082             Roo.each(el.items,function(e) {
7083                 iter(e);
7084             });
7085             
7086             
7087         };
7088         
7089         iter(this);
7090         return r;
7091         
7092         
7093         
7094         
7095     }
7096     
7097 });
7098
7099  
7100 /*
7101  * Based on:
7102  * Ext JS Library 1.1.1
7103  * Copyright(c) 2006-2007, Ext JS, LLC.
7104  *
7105  * Originally Released Under LGPL - original licence link has changed is not relivant.
7106  *
7107  * Fork - LGPL
7108  * <script type="text/javascript">
7109  */
7110 /**
7111  * @class Roo.form.VTypes
7112  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7113  * @singleton
7114  */
7115 Roo.form.VTypes = function(){
7116     // closure these in so they are only created once.
7117     var alpha = /^[a-zA-Z_]+$/;
7118     var alphanum = /^[a-zA-Z0-9_]+$/;
7119     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7120     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7121
7122     // All these messages and functions are configurable
7123     return {
7124         /**
7125          * The function used to validate email addresses
7126          * @param {String} value The email address
7127          */
7128         'email' : function(v){
7129             return email.test(v);
7130         },
7131         /**
7132          * The error text to display when the email validation function returns false
7133          * @type String
7134          */
7135         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7136         /**
7137          * The keystroke filter mask to be applied on email input
7138          * @type RegExp
7139          */
7140         'emailMask' : /[a-z0-9_\.\-@]/i,
7141
7142         /**
7143          * The function used to validate URLs
7144          * @param {String} value The URL
7145          */
7146         'url' : function(v){
7147             return url.test(v);
7148         },
7149         /**
7150          * The error text to display when the url validation function returns false
7151          * @type String
7152          */
7153         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7154         
7155         /**
7156          * The function used to validate alpha values
7157          * @param {String} value The value
7158          */
7159         'alpha' : function(v){
7160             return alpha.test(v);
7161         },
7162         /**
7163          * The error text to display when the alpha validation function returns false
7164          * @type String
7165          */
7166         'alphaText' : 'This field should only contain letters and _',
7167         /**
7168          * The keystroke filter mask to be applied on alpha input
7169          * @type RegExp
7170          */
7171         'alphaMask' : /[a-z_]/i,
7172
7173         /**
7174          * The function used to validate alphanumeric values
7175          * @param {String} value The value
7176          */
7177         'alphanum' : function(v){
7178             return alphanum.test(v);
7179         },
7180         /**
7181          * The error text to display when the alphanumeric validation function returns false
7182          * @type String
7183          */
7184         'alphanumText' : 'This field should only contain letters, numbers and _',
7185         /**
7186          * The keystroke filter mask to be applied on alphanumeric input
7187          * @type RegExp
7188          */
7189         'alphanumMask' : /[a-z0-9_]/i
7190     };
7191 }();/*
7192  * - LGPL
7193  *
7194  * Input
7195  * 
7196  */
7197
7198 /**
7199  * @class Roo.bootstrap.Input
7200  * @extends Roo.bootstrap.Component
7201  * Bootstrap Input class
7202  * @cfg {Boolean} disabled is it disabled
7203  * @cfg {String} fieldLabel - the label associated
7204  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7205  * @cfg {String} name name of the input
7206  * @cfg {string} fieldLabel - the label associated
7207  * @cfg {string}  inputType - input / file submit ...
7208  * @cfg {string} placeholder - placeholder to put in text.
7209  * @cfg {string}  before - input group add on before
7210  * @cfg {string} after - input group add on after
7211  * @cfg {string} size - (lg|sm) or leave empty..
7212  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7213  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7214  * @cfg {Number} md colspan out of 12 for computer-sized screens
7215  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7216  * @cfg {string} value default value of the input
7217  * @cfg {Number} labelWidth set the width of label (0-12)
7218  * @cfg {String} labelAlign (top|left)
7219  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7220  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7221
7222  * @cfg {String} align (left|center|right) Default left
7223  * 
7224  * 
7225  * 
7226  * @constructor
7227  * Create a new Input
7228  * @param {Object} config The config object
7229  */
7230
7231 Roo.bootstrap.Input = function(config){
7232     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7233    
7234         this.addEvents({
7235             /**
7236              * @event focus
7237              * Fires when this field receives input focus.
7238              * @param {Roo.form.Field} this
7239              */
7240             focus : true,
7241             /**
7242              * @event blur
7243              * Fires when this field loses input focus.
7244              * @param {Roo.form.Field} this
7245              */
7246             blur : true,
7247             /**
7248              * @event specialkey
7249              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7250              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7251              * @param {Roo.form.Field} this
7252              * @param {Roo.EventObject} e The event object
7253              */
7254             specialkey : true,
7255             /**
7256              * @event change
7257              * Fires just before the field blurs if the field value has changed.
7258              * @param {Roo.form.Field} this
7259              * @param {Mixed} newValue The new value
7260              * @param {Mixed} oldValue The original value
7261              */
7262             change : true,
7263             /**
7264              * @event invalid
7265              * Fires after the field has been marked as invalid.
7266              * @param {Roo.form.Field} this
7267              * @param {String} msg The validation message
7268              */
7269             invalid : true,
7270             /**
7271              * @event valid
7272              * Fires after the field has been validated with no errors.
7273              * @param {Roo.form.Field} this
7274              */
7275             valid : true,
7276              /**
7277              * @event keyup
7278              * Fires after the key up
7279              * @param {Roo.form.Field} this
7280              * @param {Roo.EventObject}  e The event Object
7281              */
7282             keyup : true
7283         });
7284 };
7285
7286 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7287      /**
7288      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7289       automatic validation (defaults to "keyup").
7290      */
7291     validationEvent : "keyup",
7292      /**
7293      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7294      */
7295     validateOnBlur : true,
7296     /**
7297      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7298      */
7299     validationDelay : 250,
7300      /**
7301      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7302      */
7303     focusClass : "x-form-focus",  // not needed???
7304     
7305        
7306     /**
7307      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7308      */
7309     invalidClass : "has-warning",
7310     
7311     /**
7312      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7313      */
7314     validClass : "has-success",
7315     
7316     /**
7317      * @cfg {Boolean} hasFeedback (true|false) default true
7318      */
7319     hasFeedback : true,
7320     
7321     /**
7322      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7323      */
7324     invalidFeedbackClass : "glyphicon-warning-sign",
7325     
7326     /**
7327      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7328      */
7329     validFeedbackClass : "glyphicon-ok",
7330     
7331     /**
7332      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7333      */
7334     selectOnFocus : false,
7335     
7336      /**
7337      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7338      */
7339     maskRe : null,
7340        /**
7341      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7342      */
7343     vtype : null,
7344     
7345       /**
7346      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7347      */
7348     disableKeyFilter : false,
7349     
7350        /**
7351      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7352      */
7353     disabled : false,
7354      /**
7355      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7356      */
7357     allowBlank : true,
7358     /**
7359      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7360      */
7361     blankText : "This field is required",
7362     
7363      /**
7364      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7365      */
7366     minLength : 0,
7367     /**
7368      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7369      */
7370     maxLength : Number.MAX_VALUE,
7371     /**
7372      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7373      */
7374     minLengthText : "The minimum length for this field is {0}",
7375     /**
7376      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7377      */
7378     maxLengthText : "The maximum length for this field is {0}",
7379   
7380     
7381     /**
7382      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7383      * If available, this function will be called only after the basic validators all return true, and will be passed the
7384      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7385      */
7386     validator : null,
7387     /**
7388      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7389      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7390      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7391      */
7392     regex : null,
7393     /**
7394      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7395      */
7396     regexText : "",
7397     
7398     autocomplete: false,
7399     
7400     
7401     fieldLabel : '',
7402     inputType : 'text',
7403     
7404     name : false,
7405     placeholder: false,
7406     before : false,
7407     after : false,
7408     size : false,
7409     hasFocus : false,
7410     preventMark: false,
7411     isFormField : true,
7412     value : '',
7413     labelWidth : 2,
7414     labelAlign : false,
7415     readOnly : false,
7416     align : false,
7417     formatedValue : false,
7418     
7419     parentLabelAlign : function()
7420     {
7421         var parent = this;
7422         while (parent.parent()) {
7423             parent = parent.parent();
7424             if (typeof(parent.labelAlign) !='undefined') {
7425                 return parent.labelAlign;
7426             }
7427         }
7428         return 'left';
7429         
7430     },
7431     
7432     getAutoCreate : function(){
7433         
7434         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7435         
7436         var id = Roo.id();
7437         
7438         var cfg = {};
7439         
7440         if(this.inputType != 'hidden'){
7441             cfg.cls = 'form-group' //input-group
7442         }
7443         
7444         var input =  {
7445             tag: 'input',
7446             id : id,
7447             type : this.inputType,
7448             value : this.value,
7449             cls : 'form-control',
7450             placeholder : this.placeholder || '',
7451             autocomplete : this.autocomplete || 'new-password'
7452         };
7453         
7454         
7455         if(this.align){
7456             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7457         }
7458         
7459         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7460             input.maxLength = this.maxLength;
7461         }
7462         
7463         if (this.disabled) {
7464             input.disabled=true;
7465         }
7466         
7467         if (this.readOnly) {
7468             input.readonly=true;
7469         }
7470         
7471         if (this.name) {
7472             input.name = this.name;
7473         }
7474         if (this.size) {
7475             input.cls += ' input-' + this.size;
7476         }
7477         var settings=this;
7478         ['xs','sm','md','lg'].map(function(size){
7479             if (settings[size]) {
7480                 cfg.cls += ' col-' + size + '-' + settings[size];
7481             }
7482         });
7483         
7484         var inputblock = input;
7485         
7486         var feedback = {
7487             tag: 'span',
7488             cls: 'glyphicon form-control-feedback'
7489         };
7490             
7491         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7492             
7493             inputblock = {
7494                 cls : 'has-feedback',
7495                 cn :  [
7496                     input,
7497                     feedback
7498                 ] 
7499             };  
7500         }
7501         
7502         if (this.before || this.after) {
7503             
7504             inputblock = {
7505                 cls : 'input-group',
7506                 cn :  [] 
7507             };
7508             
7509             if (this.before && typeof(this.before) == 'string') {
7510                 
7511                 inputblock.cn.push({
7512                     tag :'span',
7513                     cls : 'roo-input-before input-group-addon',
7514                     html : this.before
7515                 });
7516             }
7517             if (this.before && typeof(this.before) == 'object') {
7518                 this.before = Roo.factory(this.before);
7519                 Roo.log(this.before);
7520                 inputblock.cn.push({
7521                     tag :'span',
7522                     cls : 'roo-input-before input-group-' +
7523                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7524                 });
7525             }
7526             
7527             inputblock.cn.push(input);
7528             
7529             if (this.after && typeof(this.after) == 'string') {
7530                 inputblock.cn.push({
7531                     tag :'span',
7532                     cls : 'roo-input-after input-group-addon',
7533                     html : this.after
7534                 });
7535             }
7536             if (this.after && typeof(this.after) == 'object') {
7537                 this.after = Roo.factory(this.after);
7538                 Roo.log(this.after);
7539                 inputblock.cn.push({
7540                     tag :'span',
7541                     cls : 'roo-input-after input-group-' +
7542                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7543                 });
7544             }
7545             
7546             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7547                 inputblock.cls += ' has-feedback';
7548                 inputblock.cn.push(feedback);
7549             }
7550         };
7551         
7552         if (align ==='left' && this.fieldLabel.length) {
7553                 Roo.log("left and has label");
7554                 cfg.cn = [
7555                     
7556                     {
7557                         tag: 'label',
7558                         'for' :  id,
7559                         cls : 'control-label col-sm-' + this.labelWidth,
7560                         html : this.fieldLabel
7561                         
7562                     },
7563                     {
7564                         cls : "col-sm-" + (12 - this.labelWidth), 
7565                         cn: [
7566                             inputblock
7567                         ]
7568                     }
7569                     
7570                 ];
7571         } else if ( this.fieldLabel.length) {
7572                 Roo.log(" label");
7573                  cfg.cn = [
7574                    
7575                     {
7576                         tag: 'label',
7577                         //cls : 'input-group-addon',
7578                         html : this.fieldLabel
7579                         
7580                     },
7581                     
7582                     inputblock
7583                     
7584                 ];
7585
7586         } else {
7587             
7588                 Roo.log(" no label && no align");
7589                 cfg.cn = [
7590                     
7591                         inputblock
7592                     
7593                 ];
7594                 
7595                 
7596         };
7597         Roo.log('input-parentType: ' + this.parentType);
7598         
7599         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7600            cfg.cls += ' navbar-form';
7601            Roo.log(cfg);
7602         }
7603         
7604         return cfg;
7605         
7606     },
7607     /**
7608      * return the real input element.
7609      */
7610     inputEl: function ()
7611     {
7612         return this.el.select('input.form-control',true).first();
7613     },
7614     
7615     tooltipEl : function()
7616     {
7617         return this.inputEl();
7618     },
7619     
7620     setDisabled : function(v)
7621     {
7622         var i  = this.inputEl().dom;
7623         if (!v) {
7624             i.removeAttribute('disabled');
7625             return;
7626             
7627         }
7628         i.setAttribute('disabled','true');
7629     },
7630     initEvents : function()
7631     {
7632           
7633         this.inputEl().on("keydown" , this.fireKey,  this);
7634         this.inputEl().on("focus", this.onFocus,  this);
7635         this.inputEl().on("blur", this.onBlur,  this);
7636         
7637         this.inputEl().relayEvent('keyup', this);
7638
7639         // reference to original value for reset
7640         this.originalValue = this.getValue();
7641         //Roo.form.TextField.superclass.initEvents.call(this);
7642         if(this.validationEvent == 'keyup'){
7643             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7644             this.inputEl().on('keyup', this.filterValidation, this);
7645         }
7646         else if(this.validationEvent !== false){
7647             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7648         }
7649         
7650         if(this.selectOnFocus){
7651             this.on("focus", this.preFocus, this);
7652             
7653         }
7654         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7655             this.inputEl().on("keypress", this.filterKeys, this);
7656         }
7657        /* if(this.grow){
7658             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7659             this.el.on("click", this.autoSize,  this);
7660         }
7661         */
7662         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7663             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7664         }
7665         
7666         if (typeof(this.before) == 'object') {
7667             this.before.render(this.el.select('.roo-input-before',true).first());
7668         }
7669         if (typeof(this.after) == 'object') {
7670             this.after.render(this.el.select('.roo-input-after',true).first());
7671         }
7672         
7673         
7674     },
7675     filterValidation : function(e){
7676         if(!e.isNavKeyPress()){
7677             this.validationTask.delay(this.validationDelay);
7678         }
7679     },
7680      /**
7681      * Validates the field value
7682      * @return {Boolean} True if the value is valid, else false
7683      */
7684     validate : function(){
7685         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7686         if(this.disabled || this.validateValue(this.getRawValue())){
7687             this.markValid();
7688             return true;
7689         }
7690         
7691         this.markInvalid();
7692         return false;
7693     },
7694     
7695     
7696     /**
7697      * Validates a value according to the field's validation rules and marks the field as invalid
7698      * if the validation fails
7699      * @param {Mixed} value The value to validate
7700      * @return {Boolean} True if the value is valid, else false
7701      */
7702     validateValue : function(value){
7703         if(value.length < 1)  { // if it's blank
7704             if(this.allowBlank){
7705                 return true;
7706             }
7707             return false;
7708         }
7709         
7710         if(value.length < this.minLength){
7711             return false;
7712         }
7713         if(value.length > this.maxLength){
7714             return false;
7715         }
7716         if(this.vtype){
7717             var vt = Roo.form.VTypes;
7718             if(!vt[this.vtype](value, this)){
7719                 return false;
7720             }
7721         }
7722         if(typeof this.validator == "function"){
7723             var msg = this.validator(value);
7724             if(msg !== true){
7725                 return false;
7726             }
7727         }
7728         
7729         if(this.regex && !this.regex.test(value)){
7730             return false;
7731         }
7732         
7733         return true;
7734     },
7735
7736     
7737     
7738      // private
7739     fireKey : function(e){
7740         //Roo.log('field ' + e.getKey());
7741         if(e.isNavKeyPress()){
7742             this.fireEvent("specialkey", this, e);
7743         }
7744     },
7745     focus : function (selectText){
7746         if(this.rendered){
7747             this.inputEl().focus();
7748             if(selectText === true){
7749                 this.inputEl().dom.select();
7750             }
7751         }
7752         return this;
7753     } ,
7754     
7755     onFocus : function(){
7756         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7757            // this.el.addClass(this.focusClass);
7758         }
7759         if(!this.hasFocus){
7760             this.hasFocus = true;
7761             this.startValue = this.getValue();
7762             this.fireEvent("focus", this);
7763         }
7764     },
7765     
7766     beforeBlur : Roo.emptyFn,
7767
7768     
7769     // private
7770     onBlur : function(){
7771         this.beforeBlur();
7772         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7773             //this.el.removeClass(this.focusClass);
7774         }
7775         this.hasFocus = false;
7776         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7777             this.validate();
7778         }
7779         var v = this.getValue();
7780         if(String(v) !== String(this.startValue)){
7781             this.fireEvent('change', this, v, this.startValue);
7782         }
7783         this.fireEvent("blur", this);
7784     },
7785     
7786     /**
7787      * Resets the current field value to the originally loaded value and clears any validation messages
7788      */
7789     reset : function(){
7790         this.setValue(this.originalValue);
7791         this.validate();
7792     },
7793      /**
7794      * Returns the name of the field
7795      * @return {Mixed} name The name field
7796      */
7797     getName: function(){
7798         return this.name;
7799     },
7800      /**
7801      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7802      * @return {Mixed} value The field value
7803      */
7804     getValue : function(){
7805         
7806         var v = this.inputEl().getValue();
7807         
7808         return v;
7809     },
7810     /**
7811      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7812      * @return {Mixed} value The field value
7813      */
7814     getRawValue : function(){
7815         var v = this.inputEl().getValue();
7816         
7817         return v;
7818     },
7819     
7820     /**
7821      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7822      * @param {Mixed} value The value to set
7823      */
7824     setRawValue : function(v){
7825         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7826     },
7827     
7828     selectText : function(start, end){
7829         var v = this.getRawValue();
7830         if(v.length > 0){
7831             start = start === undefined ? 0 : start;
7832             end = end === undefined ? v.length : end;
7833             var d = this.inputEl().dom;
7834             if(d.setSelectionRange){
7835                 d.setSelectionRange(start, end);
7836             }else if(d.createTextRange){
7837                 var range = d.createTextRange();
7838                 range.moveStart("character", start);
7839                 range.moveEnd("character", v.length-end);
7840                 range.select();
7841             }
7842         }
7843     },
7844     
7845     /**
7846      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7847      * @param {Mixed} value The value to set
7848      */
7849     setValue : function(v){
7850         this.value = v;
7851         if(this.rendered){
7852             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7853             this.validate();
7854         }
7855     },
7856     
7857     /*
7858     processValue : function(value){
7859         if(this.stripCharsRe){
7860             var newValue = value.replace(this.stripCharsRe, '');
7861             if(newValue !== value){
7862                 this.setRawValue(newValue);
7863                 return newValue;
7864             }
7865         }
7866         return value;
7867     },
7868   */
7869     preFocus : function(){
7870         
7871         if(this.selectOnFocus){
7872             this.inputEl().dom.select();
7873         }
7874     },
7875     filterKeys : function(e){
7876         var k = e.getKey();
7877         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7878             return;
7879         }
7880         var c = e.getCharCode(), cc = String.fromCharCode(c);
7881         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7882             return;
7883         }
7884         if(!this.maskRe.test(cc)){
7885             e.stopEvent();
7886         }
7887     },
7888      /**
7889      * Clear any invalid styles/messages for this field
7890      */
7891     clearInvalid : function(){
7892         
7893         if(!this.el || this.preventMark){ // not rendered
7894             return;
7895         }
7896         this.el.removeClass(this.invalidClass);
7897         
7898         this.fireEvent('valid', this);
7899     },
7900     
7901      /**
7902      * Mark this field as valid
7903      */
7904     markValid : function(){
7905         if(!this.el  || this.preventMark){ // not rendered
7906             return;
7907         }
7908         
7909         this.el.removeClass([this.invalidClass, this.validClass]);
7910         
7911         if(this.disabled || this.allowBlank){
7912             return;
7913         }
7914         
7915         this.el.addClass(this.validClass);
7916         
7917         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
7918             
7919             var feedback = this.el.select('.form-control-feedback', true).first();
7920             
7921             if(feedback){
7922                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
7923                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
7924             }
7925             
7926         }
7927         
7928         this.fireEvent('valid', this);
7929     },
7930     
7931      /**
7932      * Mark this field as invalid
7933      * @param {String} msg The validation message
7934      */
7935     markInvalid : function(msg){
7936         if(!this.el  || this.preventMark){ // not rendered
7937             return;
7938         }
7939         
7940         this.el.removeClass([this.invalidClass, this.validClass]);
7941         
7942         if(this.disabled || this.allowBlank){
7943             return;
7944         }
7945         
7946         this.el.addClass(this.invalidClass);
7947         
7948         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7949             
7950             var feedback = this.el.select('.form-control-feedback', true).first();
7951             
7952             if(feedback){
7953                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
7954                 
7955                 if(this.getValue().length){
7956                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
7957                 }
7958                 
7959             }
7960             
7961         }
7962         
7963         this.fireEvent('invalid', this, msg);
7964     },
7965     // private
7966     SafariOnKeyDown : function(event)
7967     {
7968         // this is a workaround for a password hang bug on chrome/ webkit.
7969         
7970         var isSelectAll = false;
7971         
7972         if(this.inputEl().dom.selectionEnd > 0){
7973             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7974         }
7975         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7976             event.preventDefault();
7977             this.setValue('');
7978             return;
7979         }
7980         
7981         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
7982             
7983             event.preventDefault();
7984             // this is very hacky as keydown always get's upper case.
7985             //
7986             var cc = String.fromCharCode(event.getCharCode());
7987             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7988             
7989         }
7990     },
7991     adjustWidth : function(tag, w){
7992         tag = tag.toLowerCase();
7993         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7994             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7995                 if(tag == 'input'){
7996                     return w + 2;
7997                 }
7998                 if(tag == 'textarea'){
7999                     return w-2;
8000                 }
8001             }else if(Roo.isOpera){
8002                 if(tag == 'input'){
8003                     return w + 2;
8004                 }
8005                 if(tag == 'textarea'){
8006                     return w-2;
8007                 }
8008             }
8009         }
8010         return w;
8011     }
8012     
8013 });
8014
8015  
8016 /*
8017  * - LGPL
8018  *
8019  * Input
8020  * 
8021  */
8022
8023 /**
8024  * @class Roo.bootstrap.TextArea
8025  * @extends Roo.bootstrap.Input
8026  * Bootstrap TextArea class
8027  * @cfg {Number} cols Specifies the visible width of a text area
8028  * @cfg {Number} rows Specifies the visible number of lines in a text area
8029  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8030  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8031  * @cfg {string} html text
8032  * 
8033  * @constructor
8034  * Create a new TextArea
8035  * @param {Object} config The config object
8036  */
8037
8038 Roo.bootstrap.TextArea = function(config){
8039     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8040    
8041 };
8042
8043 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8044      
8045     cols : false,
8046     rows : 5,
8047     readOnly : false,
8048     warp : 'soft',
8049     resize : false,
8050     value: false,
8051     html: false,
8052     
8053     getAutoCreate : function(){
8054         
8055         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8056         
8057         var id = Roo.id();
8058         
8059         var cfg = {};
8060         
8061         var input =  {
8062             tag: 'textarea',
8063             id : id,
8064             warp : this.warp,
8065             rows : this.rows,
8066             value : this.value || '',
8067             html: this.html || '',
8068             cls : 'form-control',
8069             placeholder : this.placeholder || '' 
8070             
8071         };
8072         
8073         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8074             input.maxLength = this.maxLength;
8075         }
8076         
8077         if(this.resize){
8078             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8079         }
8080         
8081         if(this.cols){
8082             input.cols = this.cols;
8083         }
8084         
8085         if (this.readOnly) {
8086             input.readonly = true;
8087         }
8088         
8089         if (this.name) {
8090             input.name = this.name;
8091         }
8092         
8093         if (this.size) {
8094             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8095         }
8096         
8097         var settings=this;
8098         ['xs','sm','md','lg'].map(function(size){
8099             if (settings[size]) {
8100                 cfg.cls += ' col-' + size + '-' + settings[size];
8101             }
8102         });
8103         
8104         var inputblock = input;
8105         
8106         if(this.hasFeedback && !this.allowBlank){
8107             
8108             var feedback = {
8109                 tag: 'span',
8110                 cls: 'glyphicon form-control-feedback'
8111             };
8112
8113             inputblock = {
8114                 cls : 'has-feedback',
8115                 cn :  [
8116                     input,
8117                     feedback
8118                 ] 
8119             };  
8120         }
8121         
8122         
8123         if (this.before || this.after) {
8124             
8125             inputblock = {
8126                 cls : 'input-group',
8127                 cn :  [] 
8128             };
8129             if (this.before) {
8130                 inputblock.cn.push({
8131                     tag :'span',
8132                     cls : 'input-group-addon',
8133                     html : this.before
8134                 });
8135             }
8136             
8137             inputblock.cn.push(input);
8138             
8139             if(this.hasFeedback && !this.allowBlank){
8140                 inputblock.cls += ' has-feedback';
8141                 inputblock.cn.push(feedback);
8142             }
8143             
8144             if (this.after) {
8145                 inputblock.cn.push({
8146                     tag :'span',
8147                     cls : 'input-group-addon',
8148                     html : this.after
8149                 });
8150             }
8151             
8152         }
8153         
8154         if (align ==='left' && this.fieldLabel.length) {
8155                 Roo.log("left and has label");
8156                 cfg.cn = [
8157                     
8158                     {
8159                         tag: 'label',
8160                         'for' :  id,
8161                         cls : 'control-label col-sm-' + this.labelWidth,
8162                         html : this.fieldLabel
8163                         
8164                     },
8165                     {
8166                         cls : "col-sm-" + (12 - this.labelWidth), 
8167                         cn: [
8168                             inputblock
8169                         ]
8170                     }
8171                     
8172                 ];
8173         } else if ( this.fieldLabel.length) {
8174                 Roo.log(" label");
8175                  cfg.cn = [
8176                    
8177                     {
8178                         tag: 'label',
8179                         //cls : 'input-group-addon',
8180                         html : this.fieldLabel
8181                         
8182                     },
8183                     
8184                     inputblock
8185                     
8186                 ];
8187
8188         } else {
8189             
8190                    Roo.log(" no label && no align");
8191                 cfg.cn = [
8192                     
8193                         inputblock
8194                     
8195                 ];
8196                 
8197                 
8198         }
8199         
8200         if (this.disabled) {
8201             input.disabled=true;
8202         }
8203         
8204         return cfg;
8205         
8206     },
8207     /**
8208      * return the real textarea element.
8209      */
8210     inputEl: function ()
8211     {
8212         return this.el.select('textarea.form-control',true).first();
8213     }
8214 });
8215
8216  
8217 /*
8218  * - LGPL
8219  *
8220  * trigger field - base class for combo..
8221  * 
8222  */
8223  
8224 /**
8225  * @class Roo.bootstrap.TriggerField
8226  * @extends Roo.bootstrap.Input
8227  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8228  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8229  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8230  * for which you can provide a custom implementation.  For example:
8231  * <pre><code>
8232 var trigger = new Roo.bootstrap.TriggerField();
8233 trigger.onTriggerClick = myTriggerFn;
8234 trigger.applyTo('my-field');
8235 </code></pre>
8236  *
8237  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8238  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8239  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8240  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8241  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8242
8243  * @constructor
8244  * Create a new TriggerField.
8245  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8246  * to the base TextField)
8247  */
8248 Roo.bootstrap.TriggerField = function(config){
8249     this.mimicing = false;
8250     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8251 };
8252
8253 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8254     /**
8255      * @cfg {String} triggerClass A CSS class to apply to the trigger
8256      */
8257      /**
8258      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8259      */
8260     hideTrigger:false,
8261
8262     /** @cfg {Boolean} grow @hide */
8263     /** @cfg {Number} growMin @hide */
8264     /** @cfg {Number} growMax @hide */
8265
8266     /**
8267      * @hide 
8268      * @method
8269      */
8270     autoSize: Roo.emptyFn,
8271     // private
8272     monitorTab : true,
8273     // private
8274     deferHeight : true,
8275
8276     
8277     actionMode : 'wrap',
8278     
8279     caret : false,
8280     
8281     
8282     getAutoCreate : function(){
8283        
8284         var align = this.labelAlign || this.parentLabelAlign();
8285         
8286         var id = Roo.id();
8287         
8288         var cfg = {
8289             cls: 'form-group' //input-group
8290         };
8291         
8292         
8293         var input =  {
8294             tag: 'input',
8295             id : id,
8296             type : this.inputType,
8297             cls : 'form-control',
8298             autocomplete: 'new-password',
8299             placeholder : this.placeholder || '' 
8300             
8301         };
8302         if (this.name) {
8303             input.name = this.name;
8304         }
8305         if (this.size) {
8306             input.cls += ' input-' + this.size;
8307         }
8308         
8309         if (this.disabled) {
8310             input.disabled=true;
8311         }
8312         
8313         var inputblock = input;
8314         
8315         if(this.hasFeedback && !this.allowBlank){
8316             
8317             var feedback = {
8318                 tag: 'span',
8319                 cls: 'glyphicon form-control-feedback'
8320             };
8321
8322             inputblock = {
8323                 cls : 'has-feedback',
8324                 cn :  [
8325                     input,
8326                     feedback
8327                 ] 
8328             };  
8329         }
8330         
8331         if (this.before || this.after) {
8332             
8333             inputblock = {
8334                 cls : 'input-group',
8335                 cn :  [] 
8336             };
8337             if (this.before) {
8338                 inputblock.cn.push({
8339                     tag :'span',
8340                     cls : 'input-group-addon',
8341                     html : this.before
8342                 });
8343             }
8344             
8345             inputblock.cn.push(input);
8346             
8347             if(this.hasFeedback && !this.allowBlank){
8348                 inputblock.cls += ' has-feedback';
8349                 inputblock.cn.push(feedback);
8350             }
8351             
8352             if (this.after) {
8353                 inputblock.cn.push({
8354                     tag :'span',
8355                     cls : 'input-group-addon',
8356                     html : this.after
8357                 });
8358             }
8359             
8360         };
8361         
8362         var box = {
8363             tag: 'div',
8364             cn: [
8365                 {
8366                     tag: 'input',
8367                     type : 'hidden',
8368                     cls: 'form-hidden-field'
8369                 },
8370                 inputblock
8371             ]
8372             
8373         };
8374         
8375         if(this.multiple){
8376             Roo.log('multiple');
8377             
8378             box = {
8379                 tag: 'div',
8380                 cn: [
8381                     {
8382                         tag: 'input',
8383                         type : 'hidden',
8384                         cls: 'form-hidden-field'
8385                     },
8386                     {
8387                         tag: 'ul',
8388                         cls: 'select2-choices',
8389                         cn:[
8390                             {
8391                                 tag: 'li',
8392                                 cls: 'select2-search-field',
8393                                 cn: [
8394
8395                                     inputblock
8396                                 ]
8397                             }
8398                         ]
8399                     }
8400                 ]
8401             }
8402         };
8403         
8404         var combobox = {
8405             cls: 'select2-container input-group',
8406             cn: [
8407                 box
8408 //                {
8409 //                    tag: 'ul',
8410 //                    cls: 'typeahead typeahead-long dropdown-menu',
8411 //                    style: 'display:none'
8412 //                }
8413             ]
8414         };
8415         
8416         if(!this.multiple && this.showToggleBtn){
8417             
8418             var caret = {
8419                         tag: 'span',
8420                         cls: 'caret'
8421              };
8422             if (this.caret != false) {
8423                 caret = {
8424                      tag: 'i',
8425                      cls: 'fa fa-' + this.caret
8426                 };
8427                 
8428             }
8429             
8430             combobox.cn.push({
8431                 tag :'span',
8432                 cls : 'input-group-addon btn dropdown-toggle',
8433                 cn : [
8434                     caret,
8435                     {
8436                         tag: 'span',
8437                         cls: 'combobox-clear',
8438                         cn  : [
8439                             {
8440                                 tag : 'i',
8441                                 cls: 'icon-remove'
8442                             }
8443                         ]
8444                     }
8445                 ]
8446
8447             })
8448         }
8449         
8450         if(this.multiple){
8451             combobox.cls += ' select2-container-multi';
8452         }
8453         
8454         if (align ==='left' && this.fieldLabel.length) {
8455             
8456                 Roo.log("left and has label");
8457                 cfg.cn = [
8458                     
8459                     {
8460                         tag: 'label',
8461                         'for' :  id,
8462                         cls : 'control-label col-sm-' + this.labelWidth,
8463                         html : this.fieldLabel
8464                         
8465                     },
8466                     {
8467                         cls : "col-sm-" + (12 - this.labelWidth), 
8468                         cn: [
8469                             combobox
8470                         ]
8471                     }
8472                     
8473                 ];
8474         } else if ( this.fieldLabel.length) {
8475                 Roo.log(" label");
8476                  cfg.cn = [
8477                    
8478                     {
8479                         tag: 'label',
8480                         //cls : 'input-group-addon',
8481                         html : this.fieldLabel
8482                         
8483                     },
8484                     
8485                     combobox
8486                     
8487                 ];
8488
8489         } else {
8490             
8491                 Roo.log(" no label && no align");
8492                 cfg = combobox
8493                      
8494                 
8495         }
8496          
8497         var settings=this;
8498         ['xs','sm','md','lg'].map(function(size){
8499             if (settings[size]) {
8500                 cfg.cls += ' col-' + size + '-' + settings[size];
8501             }
8502         });
8503         
8504         return cfg;
8505         
8506     },
8507     
8508     
8509     
8510     // private
8511     onResize : function(w, h){
8512 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8513 //        if(typeof w == 'number'){
8514 //            var x = w - this.trigger.getWidth();
8515 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8516 //            this.trigger.setStyle('left', x+'px');
8517 //        }
8518     },
8519
8520     // private
8521     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8522
8523     // private
8524     getResizeEl : function(){
8525         return this.inputEl();
8526     },
8527
8528     // private
8529     getPositionEl : function(){
8530         return this.inputEl();
8531     },
8532
8533     // private
8534     alignErrorIcon : function(){
8535         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8536     },
8537
8538     // private
8539     initEvents : function(){
8540         
8541         this.createList();
8542         
8543         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8544         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8545         if(!this.multiple && this.showToggleBtn){
8546             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8547             if(this.hideTrigger){
8548                 this.trigger.setDisplayed(false);
8549             }
8550             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8551         }
8552         
8553         if(this.multiple){
8554             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8555         }
8556         
8557         //this.trigger.addClassOnOver('x-form-trigger-over');
8558         //this.trigger.addClassOnClick('x-form-trigger-click');
8559         
8560         //if(!this.width){
8561         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8562         //}
8563     },
8564     
8565     createList : function()
8566     {
8567         this.list = Roo.get(document.body).createChild({
8568             tag: 'ul',
8569             cls: 'typeahead typeahead-long dropdown-menu',
8570             style: 'display:none'
8571         });
8572         
8573         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8574         
8575     },
8576
8577     // private
8578     initTrigger : function(){
8579        
8580     },
8581
8582     // private
8583     onDestroy : function(){
8584         if(this.trigger){
8585             this.trigger.removeAllListeners();
8586           //  this.trigger.remove();
8587         }
8588         //if(this.wrap){
8589         //    this.wrap.remove();
8590         //}
8591         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8592     },
8593
8594     // private
8595     onFocus : function(){
8596         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8597         /*
8598         if(!this.mimicing){
8599             this.wrap.addClass('x-trigger-wrap-focus');
8600             this.mimicing = true;
8601             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8602             if(this.monitorTab){
8603                 this.el.on("keydown", this.checkTab, this);
8604             }
8605         }
8606         */
8607     },
8608
8609     // private
8610     checkTab : function(e){
8611         if(e.getKey() == e.TAB){
8612             this.triggerBlur();
8613         }
8614     },
8615
8616     // private
8617     onBlur : function(){
8618         // do nothing
8619     },
8620
8621     // private
8622     mimicBlur : function(e, t){
8623         /*
8624         if(!this.wrap.contains(t) && this.validateBlur()){
8625             this.triggerBlur();
8626         }
8627         */
8628     },
8629
8630     // private
8631     triggerBlur : function(){
8632         this.mimicing = false;
8633         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8634         if(this.monitorTab){
8635             this.el.un("keydown", this.checkTab, this);
8636         }
8637         //this.wrap.removeClass('x-trigger-wrap-focus');
8638         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8639     },
8640
8641     // private
8642     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8643     validateBlur : function(e, t){
8644         return true;
8645     },
8646
8647     // private
8648     onDisable : function(){
8649         this.inputEl().dom.disabled = true;
8650         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8651         //if(this.wrap){
8652         //    this.wrap.addClass('x-item-disabled');
8653         //}
8654     },
8655
8656     // private
8657     onEnable : function(){
8658         this.inputEl().dom.disabled = false;
8659         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8660         //if(this.wrap){
8661         //    this.el.removeClass('x-item-disabled');
8662         //}
8663     },
8664
8665     // private
8666     onShow : function(){
8667         var ae = this.getActionEl();
8668         
8669         if(ae){
8670             ae.dom.style.display = '';
8671             ae.dom.style.visibility = 'visible';
8672         }
8673     },
8674
8675     // private
8676     
8677     onHide : function(){
8678         var ae = this.getActionEl();
8679         ae.dom.style.display = 'none';
8680     },
8681
8682     /**
8683      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8684      * by an implementing function.
8685      * @method
8686      * @param {EventObject} e
8687      */
8688     onTriggerClick : Roo.emptyFn
8689 });
8690  /*
8691  * Based on:
8692  * Ext JS Library 1.1.1
8693  * Copyright(c) 2006-2007, Ext JS, LLC.
8694  *
8695  * Originally Released Under LGPL - original licence link has changed is not relivant.
8696  *
8697  * Fork - LGPL
8698  * <script type="text/javascript">
8699  */
8700
8701
8702 /**
8703  * @class Roo.data.SortTypes
8704  * @singleton
8705  * Defines the default sorting (casting?) comparison functions used when sorting data.
8706  */
8707 Roo.data.SortTypes = {
8708     /**
8709      * Default sort that does nothing
8710      * @param {Mixed} s The value being converted
8711      * @return {Mixed} The comparison value
8712      */
8713     none : function(s){
8714         return s;
8715     },
8716     
8717     /**
8718      * The regular expression used to strip tags
8719      * @type {RegExp}
8720      * @property
8721      */
8722     stripTagsRE : /<\/?[^>]+>/gi,
8723     
8724     /**
8725      * Strips all HTML tags to sort on text only
8726      * @param {Mixed} s The value being converted
8727      * @return {String} The comparison value
8728      */
8729     asText : function(s){
8730         return String(s).replace(this.stripTagsRE, "");
8731     },
8732     
8733     /**
8734      * Strips all HTML tags to sort on text only - Case insensitive
8735      * @param {Mixed} s The value being converted
8736      * @return {String} The comparison value
8737      */
8738     asUCText : function(s){
8739         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8740     },
8741     
8742     /**
8743      * Case insensitive string
8744      * @param {Mixed} s The value being converted
8745      * @return {String} The comparison value
8746      */
8747     asUCString : function(s) {
8748         return String(s).toUpperCase();
8749     },
8750     
8751     /**
8752      * Date sorting
8753      * @param {Mixed} s The value being converted
8754      * @return {Number} The comparison value
8755      */
8756     asDate : function(s) {
8757         if(!s){
8758             return 0;
8759         }
8760         if(s instanceof Date){
8761             return s.getTime();
8762         }
8763         return Date.parse(String(s));
8764     },
8765     
8766     /**
8767      * Float sorting
8768      * @param {Mixed} s The value being converted
8769      * @return {Float} The comparison value
8770      */
8771     asFloat : function(s) {
8772         var val = parseFloat(String(s).replace(/,/g, ""));
8773         if(isNaN(val)) val = 0;
8774         return val;
8775     },
8776     
8777     /**
8778      * Integer sorting
8779      * @param {Mixed} s The value being converted
8780      * @return {Number} The comparison value
8781      */
8782     asInt : function(s) {
8783         var val = parseInt(String(s).replace(/,/g, ""));
8784         if(isNaN(val)) val = 0;
8785         return val;
8786     }
8787 };/*
8788  * Based on:
8789  * Ext JS Library 1.1.1
8790  * Copyright(c) 2006-2007, Ext JS, LLC.
8791  *
8792  * Originally Released Under LGPL - original licence link has changed is not relivant.
8793  *
8794  * Fork - LGPL
8795  * <script type="text/javascript">
8796  */
8797
8798 /**
8799 * @class Roo.data.Record
8800  * Instances of this class encapsulate both record <em>definition</em> information, and record
8801  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8802  * to access Records cached in an {@link Roo.data.Store} object.<br>
8803  * <p>
8804  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8805  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8806  * objects.<br>
8807  * <p>
8808  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8809  * @constructor
8810  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8811  * {@link #create}. The parameters are the same.
8812  * @param {Array} data An associative Array of data values keyed by the field name.
8813  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8814  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8815  * not specified an integer id is generated.
8816  */
8817 Roo.data.Record = function(data, id){
8818     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8819     this.data = data;
8820 };
8821
8822 /**
8823  * Generate a constructor for a specific record layout.
8824  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8825  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8826  * Each field definition object may contain the following properties: <ul>
8827  * <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,
8828  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8829  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8830  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8831  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8832  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8833  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8834  * this may be omitted.</p></li>
8835  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8836  * <ul><li>auto (Default, implies no conversion)</li>
8837  * <li>string</li>
8838  * <li>int</li>
8839  * <li>float</li>
8840  * <li>boolean</li>
8841  * <li>date</li></ul></p></li>
8842  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8843  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8844  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8845  * by the Reader into an object that will be stored in the Record. It is passed the
8846  * following parameters:<ul>
8847  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8848  * </ul></p></li>
8849  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8850  * </ul>
8851  * <br>usage:<br><pre><code>
8852 var TopicRecord = Roo.data.Record.create(
8853     {name: 'title', mapping: 'topic_title'},
8854     {name: 'author', mapping: 'username'},
8855     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8856     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8857     {name: 'lastPoster', mapping: 'user2'},
8858     {name: 'excerpt', mapping: 'post_text'}
8859 );
8860
8861 var myNewRecord = new TopicRecord({
8862     title: 'Do my job please',
8863     author: 'noobie',
8864     totalPosts: 1,
8865     lastPost: new Date(),
8866     lastPoster: 'Animal',
8867     excerpt: 'No way dude!'
8868 });
8869 myStore.add(myNewRecord);
8870 </code></pre>
8871  * @method create
8872  * @static
8873  */
8874 Roo.data.Record.create = function(o){
8875     var f = function(){
8876         f.superclass.constructor.apply(this, arguments);
8877     };
8878     Roo.extend(f, Roo.data.Record);
8879     var p = f.prototype;
8880     p.fields = new Roo.util.MixedCollection(false, function(field){
8881         return field.name;
8882     });
8883     for(var i = 0, len = o.length; i < len; i++){
8884         p.fields.add(new Roo.data.Field(o[i]));
8885     }
8886     f.getField = function(name){
8887         return p.fields.get(name);  
8888     };
8889     return f;
8890 };
8891
8892 Roo.data.Record.AUTO_ID = 1000;
8893 Roo.data.Record.EDIT = 'edit';
8894 Roo.data.Record.REJECT = 'reject';
8895 Roo.data.Record.COMMIT = 'commit';
8896
8897 Roo.data.Record.prototype = {
8898     /**
8899      * Readonly flag - true if this record has been modified.
8900      * @type Boolean
8901      */
8902     dirty : false,
8903     editing : false,
8904     error: null,
8905     modified: null,
8906
8907     // private
8908     join : function(store){
8909         this.store = store;
8910     },
8911
8912     /**
8913      * Set the named field to the specified value.
8914      * @param {String} name The name of the field to set.
8915      * @param {Object} value The value to set the field to.
8916      */
8917     set : function(name, value){
8918         if(this.data[name] == value){
8919             return;
8920         }
8921         this.dirty = true;
8922         if(!this.modified){
8923             this.modified = {};
8924         }
8925         if(typeof this.modified[name] == 'undefined'){
8926             this.modified[name] = this.data[name];
8927         }
8928         this.data[name] = value;
8929         if(!this.editing && this.store){
8930             this.store.afterEdit(this);
8931         }       
8932     },
8933
8934     /**
8935      * Get the value of the named field.
8936      * @param {String} name The name of the field to get the value of.
8937      * @return {Object} The value of the field.
8938      */
8939     get : function(name){
8940         return this.data[name]; 
8941     },
8942
8943     // private
8944     beginEdit : function(){
8945         this.editing = true;
8946         this.modified = {}; 
8947     },
8948
8949     // private
8950     cancelEdit : function(){
8951         this.editing = false;
8952         delete this.modified;
8953     },
8954
8955     // private
8956     endEdit : function(){
8957         this.editing = false;
8958         if(this.dirty && this.store){
8959             this.store.afterEdit(this);
8960         }
8961     },
8962
8963     /**
8964      * Usually called by the {@link Roo.data.Store} which owns the Record.
8965      * Rejects all changes made to the Record since either creation, or the last commit operation.
8966      * Modified fields are reverted to their original values.
8967      * <p>
8968      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8969      * of reject operations.
8970      */
8971     reject : function(){
8972         var m = this.modified;
8973         for(var n in m){
8974             if(typeof m[n] != "function"){
8975                 this.data[n] = m[n];
8976             }
8977         }
8978         this.dirty = false;
8979         delete this.modified;
8980         this.editing = false;
8981         if(this.store){
8982             this.store.afterReject(this);
8983         }
8984     },
8985
8986     /**
8987      * Usually called by the {@link Roo.data.Store} which owns the Record.
8988      * Commits all changes made to the Record since either creation, or the last commit operation.
8989      * <p>
8990      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8991      * of commit operations.
8992      */
8993     commit : function(){
8994         this.dirty = false;
8995         delete this.modified;
8996         this.editing = false;
8997         if(this.store){
8998             this.store.afterCommit(this);
8999         }
9000     },
9001
9002     // private
9003     hasError : function(){
9004         return this.error != null;
9005     },
9006
9007     // private
9008     clearError : function(){
9009         this.error = null;
9010     },
9011
9012     /**
9013      * Creates a copy of this record.
9014      * @param {String} id (optional) A new record id if you don't want to use this record's id
9015      * @return {Record}
9016      */
9017     copy : function(newId) {
9018         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9019     }
9020 };/*
9021  * Based on:
9022  * Ext JS Library 1.1.1
9023  * Copyright(c) 2006-2007, Ext JS, LLC.
9024  *
9025  * Originally Released Under LGPL - original licence link has changed is not relivant.
9026  *
9027  * Fork - LGPL
9028  * <script type="text/javascript">
9029  */
9030
9031
9032
9033 /**
9034  * @class Roo.data.Store
9035  * @extends Roo.util.Observable
9036  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9037  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9038  * <p>
9039  * 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
9040  * has no knowledge of the format of the data returned by the Proxy.<br>
9041  * <p>
9042  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9043  * instances from the data object. These records are cached and made available through accessor functions.
9044  * @constructor
9045  * Creates a new Store.
9046  * @param {Object} config A config object containing the objects needed for the Store to access data,
9047  * and read the data into Records.
9048  */
9049 Roo.data.Store = function(config){
9050     this.data = new Roo.util.MixedCollection(false);
9051     this.data.getKey = function(o){
9052         return o.id;
9053     };
9054     this.baseParams = {};
9055     // private
9056     this.paramNames = {
9057         "start" : "start",
9058         "limit" : "limit",
9059         "sort" : "sort",
9060         "dir" : "dir",
9061         "multisort" : "_multisort"
9062     };
9063
9064     if(config && config.data){
9065         this.inlineData = config.data;
9066         delete config.data;
9067     }
9068
9069     Roo.apply(this, config);
9070     
9071     if(this.reader){ // reader passed
9072         this.reader = Roo.factory(this.reader, Roo.data);
9073         this.reader.xmodule = this.xmodule || false;
9074         if(!this.recordType){
9075             this.recordType = this.reader.recordType;
9076         }
9077         if(this.reader.onMetaChange){
9078             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9079         }
9080     }
9081
9082     if(this.recordType){
9083         this.fields = this.recordType.prototype.fields;
9084     }
9085     this.modified = [];
9086
9087     this.addEvents({
9088         /**
9089          * @event datachanged
9090          * Fires when the data cache has changed, and a widget which is using this Store
9091          * as a Record cache should refresh its view.
9092          * @param {Store} this
9093          */
9094         datachanged : true,
9095         /**
9096          * @event metachange
9097          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9098          * @param {Store} this
9099          * @param {Object} meta The JSON metadata
9100          */
9101         metachange : true,
9102         /**
9103          * @event add
9104          * Fires when Records have been added to the Store
9105          * @param {Store} this
9106          * @param {Roo.data.Record[]} records The array of Records added
9107          * @param {Number} index The index at which the record(s) were added
9108          */
9109         add : true,
9110         /**
9111          * @event remove
9112          * Fires when a Record has been removed from the Store
9113          * @param {Store} this
9114          * @param {Roo.data.Record} record The Record that was removed
9115          * @param {Number} index The index at which the record was removed
9116          */
9117         remove : true,
9118         /**
9119          * @event update
9120          * Fires when a Record has been updated
9121          * @param {Store} this
9122          * @param {Roo.data.Record} record The Record that was updated
9123          * @param {String} operation The update operation being performed.  Value may be one of:
9124          * <pre><code>
9125  Roo.data.Record.EDIT
9126  Roo.data.Record.REJECT
9127  Roo.data.Record.COMMIT
9128          * </code></pre>
9129          */
9130         update : true,
9131         /**
9132          * @event clear
9133          * Fires when the data cache has been cleared.
9134          * @param {Store} this
9135          */
9136         clear : true,
9137         /**
9138          * @event beforeload
9139          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9140          * the load action will be canceled.
9141          * @param {Store} this
9142          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9143          */
9144         beforeload : true,
9145         /**
9146          * @event beforeloadadd
9147          * Fires after a new set of Records has been loaded.
9148          * @param {Store} this
9149          * @param {Roo.data.Record[]} records The Records that were loaded
9150          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9151          */
9152         beforeloadadd : true,
9153         /**
9154          * @event load
9155          * Fires after a new set of Records has been loaded, before they are added to the store.
9156          * @param {Store} this
9157          * @param {Roo.data.Record[]} records The Records that were loaded
9158          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9159          * @params {Object} return from reader
9160          */
9161         load : true,
9162         /**
9163          * @event loadexception
9164          * Fires if an exception occurs in the Proxy during loading.
9165          * Called with the signature of the Proxy's "loadexception" event.
9166          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9167          * 
9168          * @param {Proxy} 
9169          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9170          * @param {Object} load options 
9171          * @param {Object} jsonData from your request (normally this contains the Exception)
9172          */
9173         loadexception : true
9174     });
9175     
9176     if(this.proxy){
9177         this.proxy = Roo.factory(this.proxy, Roo.data);
9178         this.proxy.xmodule = this.xmodule || false;
9179         this.relayEvents(this.proxy,  ["loadexception"]);
9180     }
9181     this.sortToggle = {};
9182     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9183
9184     Roo.data.Store.superclass.constructor.call(this);
9185
9186     if(this.inlineData){
9187         this.loadData(this.inlineData);
9188         delete this.inlineData;
9189     }
9190 };
9191
9192 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9193      /**
9194     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9195     * without a remote query - used by combo/forms at present.
9196     */
9197     
9198     /**
9199     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9200     */
9201     /**
9202     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9203     */
9204     /**
9205     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9206     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9207     */
9208     /**
9209     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9210     * on any HTTP request
9211     */
9212     /**
9213     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9214     */
9215     /**
9216     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9217     */
9218     multiSort: false,
9219     /**
9220     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9221     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9222     */
9223     remoteSort : false,
9224
9225     /**
9226     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9227      * loaded or when a record is removed. (defaults to false).
9228     */
9229     pruneModifiedRecords : false,
9230
9231     // private
9232     lastOptions : null,
9233
9234     /**
9235      * Add Records to the Store and fires the add event.
9236      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9237      */
9238     add : function(records){
9239         records = [].concat(records);
9240         for(var i = 0, len = records.length; i < len; i++){
9241             records[i].join(this);
9242         }
9243         var index = this.data.length;
9244         this.data.addAll(records);
9245         this.fireEvent("add", this, records, index);
9246     },
9247
9248     /**
9249      * Remove a Record from the Store and fires the remove event.
9250      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9251      */
9252     remove : function(record){
9253         var index = this.data.indexOf(record);
9254         this.data.removeAt(index);
9255         if(this.pruneModifiedRecords){
9256             this.modified.remove(record);
9257         }
9258         this.fireEvent("remove", this, record, index);
9259     },
9260
9261     /**
9262      * Remove all Records from the Store and fires the clear event.
9263      */
9264     removeAll : function(){
9265         this.data.clear();
9266         if(this.pruneModifiedRecords){
9267             this.modified = [];
9268         }
9269         this.fireEvent("clear", this);
9270     },
9271
9272     /**
9273      * Inserts Records to the Store at the given index and fires the add event.
9274      * @param {Number} index The start index at which to insert the passed Records.
9275      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9276      */
9277     insert : function(index, records){
9278         records = [].concat(records);
9279         for(var i = 0, len = records.length; i < len; i++){
9280             this.data.insert(index, records[i]);
9281             records[i].join(this);
9282         }
9283         this.fireEvent("add", this, records, index);
9284     },
9285
9286     /**
9287      * Get the index within the cache of the passed Record.
9288      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9289      * @return {Number} The index of the passed Record. Returns -1 if not found.
9290      */
9291     indexOf : function(record){
9292         return this.data.indexOf(record);
9293     },
9294
9295     /**
9296      * Get the index within the cache of the Record with the passed id.
9297      * @param {String} id The id of the Record to find.
9298      * @return {Number} The index of the Record. Returns -1 if not found.
9299      */
9300     indexOfId : function(id){
9301         return this.data.indexOfKey(id);
9302     },
9303
9304     /**
9305      * Get the Record with the specified id.
9306      * @param {String} id The id of the Record to find.
9307      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9308      */
9309     getById : function(id){
9310         return this.data.key(id);
9311     },
9312
9313     /**
9314      * Get the Record at the specified index.
9315      * @param {Number} index The index of the Record to find.
9316      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9317      */
9318     getAt : function(index){
9319         return this.data.itemAt(index);
9320     },
9321
9322     /**
9323      * Returns a range of Records between specified indices.
9324      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9325      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9326      * @return {Roo.data.Record[]} An array of Records
9327      */
9328     getRange : function(start, end){
9329         return this.data.getRange(start, end);
9330     },
9331
9332     // private
9333     storeOptions : function(o){
9334         o = Roo.apply({}, o);
9335         delete o.callback;
9336         delete o.scope;
9337         this.lastOptions = o;
9338     },
9339
9340     /**
9341      * Loads the Record cache from the configured Proxy using the configured Reader.
9342      * <p>
9343      * If using remote paging, then the first load call must specify the <em>start</em>
9344      * and <em>limit</em> properties in the options.params property to establish the initial
9345      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9346      * <p>
9347      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9348      * and this call will return before the new data has been loaded. Perform any post-processing
9349      * in a callback function, or in a "load" event handler.</strong>
9350      * <p>
9351      * @param {Object} options An object containing properties which control loading options:<ul>
9352      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9353      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9354      * passed the following arguments:<ul>
9355      * <li>r : Roo.data.Record[]</li>
9356      * <li>options: Options object from the load call</li>
9357      * <li>success: Boolean success indicator</li></ul></li>
9358      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9359      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9360      * </ul>
9361      */
9362     load : function(options){
9363         options = options || {};
9364         if(this.fireEvent("beforeload", this, options) !== false){
9365             this.storeOptions(options);
9366             var p = Roo.apply(options.params || {}, this.baseParams);
9367             // if meta was not loaded from remote source.. try requesting it.
9368             if (!this.reader.metaFromRemote) {
9369                 p._requestMeta = 1;
9370             }
9371             if(this.sortInfo && this.remoteSort){
9372                 var pn = this.paramNames;
9373                 p[pn["sort"]] = this.sortInfo.field;
9374                 p[pn["dir"]] = this.sortInfo.direction;
9375             }
9376             if (this.multiSort) {
9377                 var pn = this.paramNames;
9378                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9379             }
9380             
9381             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9382         }
9383     },
9384
9385     /**
9386      * Reloads the Record cache from the configured Proxy using the configured Reader and
9387      * the options from the last load operation performed.
9388      * @param {Object} options (optional) An object containing properties which may override the options
9389      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9390      * the most recently used options are reused).
9391      */
9392     reload : function(options){
9393         this.load(Roo.applyIf(options||{}, this.lastOptions));
9394     },
9395
9396     // private
9397     // Called as a callback by the Reader during a load operation.
9398     loadRecords : function(o, options, success){
9399         if(!o || success === false){
9400             if(success !== false){
9401                 this.fireEvent("load", this, [], options, o);
9402             }
9403             if(options.callback){
9404                 options.callback.call(options.scope || this, [], options, false);
9405             }
9406             return;
9407         }
9408         // if data returned failure - throw an exception.
9409         if (o.success === false) {
9410             // show a message if no listener is registered.
9411             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9412                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9413             }
9414             // loadmask wil be hooked into this..
9415             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9416             return;
9417         }
9418         var r = o.records, t = o.totalRecords || r.length;
9419         
9420         this.fireEvent("beforeloadadd", this, r, options, o);
9421         
9422         if(!options || options.add !== true){
9423             if(this.pruneModifiedRecords){
9424                 this.modified = [];
9425             }
9426             for(var i = 0, len = r.length; i < len; i++){
9427                 r[i].join(this);
9428             }
9429             if(this.snapshot){
9430                 this.data = this.snapshot;
9431                 delete this.snapshot;
9432             }
9433             this.data.clear();
9434             this.data.addAll(r);
9435             this.totalLength = t;
9436             this.applySort();
9437             this.fireEvent("datachanged", this);
9438         }else{
9439             this.totalLength = Math.max(t, this.data.length+r.length);
9440             this.add(r);
9441         }
9442         this.fireEvent("load", this, r, options, o);
9443         if(options.callback){
9444             options.callback.call(options.scope || this, r, options, true);
9445         }
9446     },
9447
9448
9449     /**
9450      * Loads data from a passed data block. A Reader which understands the format of the data
9451      * must have been configured in the constructor.
9452      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9453      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9454      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9455      */
9456     loadData : function(o, append){
9457         var r = this.reader.readRecords(o);
9458         this.loadRecords(r, {add: append}, true);
9459     },
9460
9461     /**
9462      * Gets the number of cached records.
9463      * <p>
9464      * <em>If using paging, this may not be the total size of the dataset. If the data object
9465      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9466      * the data set size</em>
9467      */
9468     getCount : function(){
9469         return this.data.length || 0;
9470     },
9471
9472     /**
9473      * Gets the total number of records in the dataset as returned by the server.
9474      * <p>
9475      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9476      * the dataset size</em>
9477      */
9478     getTotalCount : function(){
9479         return this.totalLength || 0;
9480     },
9481
9482     /**
9483      * Returns the sort state of the Store as an object with two properties:
9484      * <pre><code>
9485  field {String} The name of the field by which the Records are sorted
9486  direction {String} The sort order, "ASC" or "DESC"
9487      * </code></pre>
9488      */
9489     getSortState : function(){
9490         return this.sortInfo;
9491     },
9492
9493     // private
9494     applySort : function(){
9495         if(this.sortInfo && !this.remoteSort){
9496             var s = this.sortInfo, f = s.field;
9497             var st = this.fields.get(f).sortType;
9498             var fn = function(r1, r2){
9499                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9500                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9501             };
9502             this.data.sort(s.direction, fn);
9503             if(this.snapshot && this.snapshot != this.data){
9504                 this.snapshot.sort(s.direction, fn);
9505             }
9506         }
9507     },
9508
9509     /**
9510      * Sets the default sort column and order to be used by the next load operation.
9511      * @param {String} fieldName The name of the field to sort by.
9512      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9513      */
9514     setDefaultSort : function(field, dir){
9515         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9516     },
9517
9518     /**
9519      * Sort the Records.
9520      * If remote sorting is used, the sort is performed on the server, and the cache is
9521      * reloaded. If local sorting is used, the cache is sorted internally.
9522      * @param {String} fieldName The name of the field to sort by.
9523      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9524      */
9525     sort : function(fieldName, dir){
9526         var f = this.fields.get(fieldName);
9527         if(!dir){
9528             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9529             
9530             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9531                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9532             }else{
9533                 dir = f.sortDir;
9534             }
9535         }
9536         this.sortToggle[f.name] = dir;
9537         this.sortInfo = {field: f.name, direction: dir};
9538         if(!this.remoteSort){
9539             this.applySort();
9540             this.fireEvent("datachanged", this);
9541         }else{
9542             this.load(this.lastOptions);
9543         }
9544     },
9545
9546     /**
9547      * Calls the specified function for each of the Records in the cache.
9548      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9549      * Returning <em>false</em> aborts and exits the iteration.
9550      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9551      */
9552     each : function(fn, scope){
9553         this.data.each(fn, scope);
9554     },
9555
9556     /**
9557      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9558      * (e.g., during paging).
9559      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9560      */
9561     getModifiedRecords : function(){
9562         return this.modified;
9563     },
9564
9565     // private
9566     createFilterFn : function(property, value, anyMatch){
9567         if(!value.exec){ // not a regex
9568             value = String(value);
9569             if(value.length == 0){
9570                 return false;
9571             }
9572             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9573         }
9574         return function(r){
9575             return value.test(r.data[property]);
9576         };
9577     },
9578
9579     /**
9580      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9581      * @param {String} property A field on your records
9582      * @param {Number} start The record index to start at (defaults to 0)
9583      * @param {Number} end The last record index to include (defaults to length - 1)
9584      * @return {Number} The sum
9585      */
9586     sum : function(property, start, end){
9587         var rs = this.data.items, v = 0;
9588         start = start || 0;
9589         end = (end || end === 0) ? end : rs.length-1;
9590
9591         for(var i = start; i <= end; i++){
9592             v += (rs[i].data[property] || 0);
9593         }
9594         return v;
9595     },
9596
9597     /**
9598      * Filter the records by a specified property.
9599      * @param {String} field A field on your records
9600      * @param {String/RegExp} value Either a string that the field
9601      * should start with or a RegExp to test against the field
9602      * @param {Boolean} anyMatch True to match any part not just the beginning
9603      */
9604     filter : function(property, value, anyMatch){
9605         var fn = this.createFilterFn(property, value, anyMatch);
9606         return fn ? this.filterBy(fn) : this.clearFilter();
9607     },
9608
9609     /**
9610      * Filter by a function. The specified function will be called with each
9611      * record in this data source. If the function returns true the record is included,
9612      * otherwise it is filtered.
9613      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9614      * @param {Object} scope (optional) The scope of the function (defaults to this)
9615      */
9616     filterBy : function(fn, scope){
9617         this.snapshot = this.snapshot || this.data;
9618         this.data = this.queryBy(fn, scope||this);
9619         this.fireEvent("datachanged", this);
9620     },
9621
9622     /**
9623      * Query the records by a specified property.
9624      * @param {String} field A field on your records
9625      * @param {String/RegExp} value Either a string that the field
9626      * should start with or a RegExp to test against the field
9627      * @param {Boolean} anyMatch True to match any part not just the beginning
9628      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9629      */
9630     query : function(property, value, anyMatch){
9631         var fn = this.createFilterFn(property, value, anyMatch);
9632         return fn ? this.queryBy(fn) : this.data.clone();
9633     },
9634
9635     /**
9636      * Query by a function. The specified function will be called with each
9637      * record in this data source. If the function returns true the record is included
9638      * in the results.
9639      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9640      * @param {Object} scope (optional) The scope of the function (defaults to this)
9641       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9642      **/
9643     queryBy : function(fn, scope){
9644         var data = this.snapshot || this.data;
9645         return data.filterBy(fn, scope||this);
9646     },
9647
9648     /**
9649      * Collects unique values for a particular dataIndex from this store.
9650      * @param {String} dataIndex The property to collect
9651      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9652      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9653      * @return {Array} An array of the unique values
9654      **/
9655     collect : function(dataIndex, allowNull, bypassFilter){
9656         var d = (bypassFilter === true && this.snapshot) ?
9657                 this.snapshot.items : this.data.items;
9658         var v, sv, r = [], l = {};
9659         for(var i = 0, len = d.length; i < len; i++){
9660             v = d[i].data[dataIndex];
9661             sv = String(v);
9662             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9663                 l[sv] = true;
9664                 r[r.length] = v;
9665             }
9666         }
9667         return r;
9668     },
9669
9670     /**
9671      * Revert to a view of the Record cache with no filtering applied.
9672      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9673      */
9674     clearFilter : function(suppressEvent){
9675         if(this.snapshot && this.snapshot != this.data){
9676             this.data = this.snapshot;
9677             delete this.snapshot;
9678             if(suppressEvent !== true){
9679                 this.fireEvent("datachanged", this);
9680             }
9681         }
9682     },
9683
9684     // private
9685     afterEdit : function(record){
9686         if(this.modified.indexOf(record) == -1){
9687             this.modified.push(record);
9688         }
9689         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9690     },
9691     
9692     // private
9693     afterReject : function(record){
9694         this.modified.remove(record);
9695         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9696     },
9697
9698     // private
9699     afterCommit : function(record){
9700         this.modified.remove(record);
9701         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9702     },
9703
9704     /**
9705      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9706      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9707      */
9708     commitChanges : function(){
9709         var m = this.modified.slice(0);
9710         this.modified = [];
9711         for(var i = 0, len = m.length; i < len; i++){
9712             m[i].commit();
9713         }
9714     },
9715
9716     /**
9717      * Cancel outstanding changes on all changed records.
9718      */
9719     rejectChanges : function(){
9720         var m = this.modified.slice(0);
9721         this.modified = [];
9722         for(var i = 0, len = m.length; i < len; i++){
9723             m[i].reject();
9724         }
9725     },
9726
9727     onMetaChange : function(meta, rtype, o){
9728         this.recordType = rtype;
9729         this.fields = rtype.prototype.fields;
9730         delete this.snapshot;
9731         this.sortInfo = meta.sortInfo || this.sortInfo;
9732         this.modified = [];
9733         this.fireEvent('metachange', this, this.reader.meta);
9734     },
9735     
9736     moveIndex : function(data, type)
9737     {
9738         var index = this.indexOf(data);
9739         
9740         var newIndex = index + type;
9741         
9742         this.remove(data);
9743         
9744         this.insert(newIndex, data);
9745         
9746     }
9747 });/*
9748  * Based on:
9749  * Ext JS Library 1.1.1
9750  * Copyright(c) 2006-2007, Ext JS, LLC.
9751  *
9752  * Originally Released Under LGPL - original licence link has changed is not relivant.
9753  *
9754  * Fork - LGPL
9755  * <script type="text/javascript">
9756  */
9757
9758 /**
9759  * @class Roo.data.SimpleStore
9760  * @extends Roo.data.Store
9761  * Small helper class to make creating Stores from Array data easier.
9762  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9763  * @cfg {Array} fields An array of field definition objects, or field name strings.
9764  * @cfg {Array} data The multi-dimensional array of data
9765  * @constructor
9766  * @param {Object} config
9767  */
9768 Roo.data.SimpleStore = function(config){
9769     Roo.data.SimpleStore.superclass.constructor.call(this, {
9770         isLocal : true,
9771         reader: new Roo.data.ArrayReader({
9772                 id: config.id
9773             },
9774             Roo.data.Record.create(config.fields)
9775         ),
9776         proxy : new Roo.data.MemoryProxy(config.data)
9777     });
9778     this.load();
9779 };
9780 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9781  * Based on:
9782  * Ext JS Library 1.1.1
9783  * Copyright(c) 2006-2007, Ext JS, LLC.
9784  *
9785  * Originally Released Under LGPL - original licence link has changed is not relivant.
9786  *
9787  * Fork - LGPL
9788  * <script type="text/javascript">
9789  */
9790
9791 /**
9792 /**
9793  * @extends Roo.data.Store
9794  * @class Roo.data.JsonStore
9795  * Small helper class to make creating Stores for JSON data easier. <br/>
9796 <pre><code>
9797 var store = new Roo.data.JsonStore({
9798     url: 'get-images.php',
9799     root: 'images',
9800     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9801 });
9802 </code></pre>
9803  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9804  * JsonReader and HttpProxy (unless inline data is provided).</b>
9805  * @cfg {Array} fields An array of field definition objects, or field name strings.
9806  * @constructor
9807  * @param {Object} config
9808  */
9809 Roo.data.JsonStore = function(c){
9810     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9811         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9812         reader: new Roo.data.JsonReader(c, c.fields)
9813     }));
9814 };
9815 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9816  * Based on:
9817  * Ext JS Library 1.1.1
9818  * Copyright(c) 2006-2007, Ext JS, LLC.
9819  *
9820  * Originally Released Under LGPL - original licence link has changed is not relivant.
9821  *
9822  * Fork - LGPL
9823  * <script type="text/javascript">
9824  */
9825
9826  
9827 Roo.data.Field = function(config){
9828     if(typeof config == "string"){
9829         config = {name: config};
9830     }
9831     Roo.apply(this, config);
9832     
9833     if(!this.type){
9834         this.type = "auto";
9835     }
9836     
9837     var st = Roo.data.SortTypes;
9838     // named sortTypes are supported, here we look them up
9839     if(typeof this.sortType == "string"){
9840         this.sortType = st[this.sortType];
9841     }
9842     
9843     // set default sortType for strings and dates
9844     if(!this.sortType){
9845         switch(this.type){
9846             case "string":
9847                 this.sortType = st.asUCString;
9848                 break;
9849             case "date":
9850                 this.sortType = st.asDate;
9851                 break;
9852             default:
9853                 this.sortType = st.none;
9854         }
9855     }
9856
9857     // define once
9858     var stripRe = /[\$,%]/g;
9859
9860     // prebuilt conversion function for this field, instead of
9861     // switching every time we're reading a value
9862     if(!this.convert){
9863         var cv, dateFormat = this.dateFormat;
9864         switch(this.type){
9865             case "":
9866             case "auto":
9867             case undefined:
9868                 cv = function(v){ return v; };
9869                 break;
9870             case "string":
9871                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9872                 break;
9873             case "int":
9874                 cv = function(v){
9875                     return v !== undefined && v !== null && v !== '' ?
9876                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9877                     };
9878                 break;
9879             case "float":
9880                 cv = function(v){
9881                     return v !== undefined && v !== null && v !== '' ?
9882                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9883                     };
9884                 break;
9885             case "bool":
9886             case "boolean":
9887                 cv = function(v){ return v === true || v === "true" || v == 1; };
9888                 break;
9889             case "date":
9890                 cv = function(v){
9891                     if(!v){
9892                         return '';
9893                     }
9894                     if(v instanceof Date){
9895                         return v;
9896                     }
9897                     if(dateFormat){
9898                         if(dateFormat == "timestamp"){
9899                             return new Date(v*1000);
9900                         }
9901                         return Date.parseDate(v, dateFormat);
9902                     }
9903                     var parsed = Date.parse(v);
9904                     return parsed ? new Date(parsed) : null;
9905                 };
9906              break;
9907             
9908         }
9909         this.convert = cv;
9910     }
9911 };
9912
9913 Roo.data.Field.prototype = {
9914     dateFormat: null,
9915     defaultValue: "",
9916     mapping: null,
9917     sortType : null,
9918     sortDir : "ASC"
9919 };/*
9920  * Based on:
9921  * Ext JS Library 1.1.1
9922  * Copyright(c) 2006-2007, Ext JS, LLC.
9923  *
9924  * Originally Released Under LGPL - original licence link has changed is not relivant.
9925  *
9926  * Fork - LGPL
9927  * <script type="text/javascript">
9928  */
9929  
9930 // Base class for reading structured data from a data source.  This class is intended to be
9931 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9932
9933 /**
9934  * @class Roo.data.DataReader
9935  * Base class for reading structured data from a data source.  This class is intended to be
9936  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9937  */
9938
9939 Roo.data.DataReader = function(meta, recordType){
9940     
9941     this.meta = meta;
9942     
9943     this.recordType = recordType instanceof Array ? 
9944         Roo.data.Record.create(recordType) : recordType;
9945 };
9946
9947 Roo.data.DataReader.prototype = {
9948      /**
9949      * Create an empty record
9950      * @param {Object} data (optional) - overlay some values
9951      * @return {Roo.data.Record} record created.
9952      */
9953     newRow :  function(d) {
9954         var da =  {};
9955         this.recordType.prototype.fields.each(function(c) {
9956             switch( c.type) {
9957                 case 'int' : da[c.name] = 0; break;
9958                 case 'date' : da[c.name] = new Date(); break;
9959                 case 'float' : da[c.name] = 0.0; break;
9960                 case 'boolean' : da[c.name] = false; break;
9961                 default : da[c.name] = ""; break;
9962             }
9963             
9964         });
9965         return new this.recordType(Roo.apply(da, d));
9966     }
9967     
9968 };/*
9969  * Based on:
9970  * Ext JS Library 1.1.1
9971  * Copyright(c) 2006-2007, Ext JS, LLC.
9972  *
9973  * Originally Released Under LGPL - original licence link has changed is not relivant.
9974  *
9975  * Fork - LGPL
9976  * <script type="text/javascript">
9977  */
9978
9979 /**
9980  * @class Roo.data.DataProxy
9981  * @extends Roo.data.Observable
9982  * This class is an abstract base class for implementations which provide retrieval of
9983  * unformatted data objects.<br>
9984  * <p>
9985  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9986  * (of the appropriate type which knows how to parse the data object) to provide a block of
9987  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9988  * <p>
9989  * Custom implementations must implement the load method as described in
9990  * {@link Roo.data.HttpProxy#load}.
9991  */
9992 Roo.data.DataProxy = function(){
9993     this.addEvents({
9994         /**
9995          * @event beforeload
9996          * Fires before a network request is made to retrieve a data object.
9997          * @param {Object} This DataProxy object.
9998          * @param {Object} params The params parameter to the load function.
9999          */
10000         beforeload : true,
10001         /**
10002          * @event load
10003          * Fires before the load method's callback is called.
10004          * @param {Object} This DataProxy object.
10005          * @param {Object} o The data object.
10006          * @param {Object} arg The callback argument object passed to the load function.
10007          */
10008         load : true,
10009         /**
10010          * @event loadexception
10011          * Fires if an Exception occurs during data retrieval.
10012          * @param {Object} This DataProxy object.
10013          * @param {Object} o The data object.
10014          * @param {Object} arg The callback argument object passed to the load function.
10015          * @param {Object} e The Exception.
10016          */
10017         loadexception : true
10018     });
10019     Roo.data.DataProxy.superclass.constructor.call(this);
10020 };
10021
10022 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10023
10024     /**
10025      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10026      */
10027 /*
10028  * Based on:
10029  * Ext JS Library 1.1.1
10030  * Copyright(c) 2006-2007, Ext JS, LLC.
10031  *
10032  * Originally Released Under LGPL - original licence link has changed is not relivant.
10033  *
10034  * Fork - LGPL
10035  * <script type="text/javascript">
10036  */
10037 /**
10038  * @class Roo.data.MemoryProxy
10039  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10040  * to the Reader when its load method is called.
10041  * @constructor
10042  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10043  */
10044 Roo.data.MemoryProxy = function(data){
10045     if (data.data) {
10046         data = data.data;
10047     }
10048     Roo.data.MemoryProxy.superclass.constructor.call(this);
10049     this.data = data;
10050 };
10051
10052 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10053     /**
10054      * Load data from the requested source (in this case an in-memory
10055      * data object passed to the constructor), read the data object into
10056      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10057      * process that block using the passed callback.
10058      * @param {Object} params This parameter is not used by the MemoryProxy class.
10059      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10060      * object into a block of Roo.data.Records.
10061      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10062      * The function must be passed <ul>
10063      * <li>The Record block object</li>
10064      * <li>The "arg" argument from the load function</li>
10065      * <li>A boolean success indicator</li>
10066      * </ul>
10067      * @param {Object} scope The scope in which to call the callback
10068      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10069      */
10070     load : function(params, reader, callback, scope, arg){
10071         params = params || {};
10072         var result;
10073         try {
10074             result = reader.readRecords(this.data);
10075         }catch(e){
10076             this.fireEvent("loadexception", this, arg, null, e);
10077             callback.call(scope, null, arg, false);
10078             return;
10079         }
10080         callback.call(scope, result, arg, true);
10081     },
10082     
10083     // private
10084     update : function(params, records){
10085         
10086     }
10087 });/*
10088  * Based on:
10089  * Ext JS Library 1.1.1
10090  * Copyright(c) 2006-2007, Ext JS, LLC.
10091  *
10092  * Originally Released Under LGPL - original licence link has changed is not relivant.
10093  *
10094  * Fork - LGPL
10095  * <script type="text/javascript">
10096  */
10097 /**
10098  * @class Roo.data.HttpProxy
10099  * @extends Roo.data.DataProxy
10100  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10101  * configured to reference a certain URL.<br><br>
10102  * <p>
10103  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10104  * from which the running page was served.<br><br>
10105  * <p>
10106  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10107  * <p>
10108  * Be aware that to enable the browser to parse an XML document, the server must set
10109  * the Content-Type header in the HTTP response to "text/xml".
10110  * @constructor
10111  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10112  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10113  * will be used to make the request.
10114  */
10115 Roo.data.HttpProxy = function(conn){
10116     Roo.data.HttpProxy.superclass.constructor.call(this);
10117     // is conn a conn config or a real conn?
10118     this.conn = conn;
10119     this.useAjax = !conn || !conn.events;
10120   
10121 };
10122
10123 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10124     // thse are take from connection...
10125     
10126     /**
10127      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10128      */
10129     /**
10130      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10131      * extra parameters to each request made by this object. (defaults to undefined)
10132      */
10133     /**
10134      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10135      *  to each request made by this object. (defaults to undefined)
10136      */
10137     /**
10138      * @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)
10139      */
10140     /**
10141      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10142      */
10143      /**
10144      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10145      * @type Boolean
10146      */
10147   
10148
10149     /**
10150      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10151      * @type Boolean
10152      */
10153     /**
10154      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10155      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10156      * a finer-grained basis than the DataProxy events.
10157      */
10158     getConnection : function(){
10159         return this.useAjax ? Roo.Ajax : this.conn;
10160     },
10161
10162     /**
10163      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10164      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10165      * process that block using the passed callback.
10166      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10167      * for the request to the remote server.
10168      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10169      * object into a block of Roo.data.Records.
10170      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10171      * The function must be passed <ul>
10172      * <li>The Record block object</li>
10173      * <li>The "arg" argument from the load function</li>
10174      * <li>A boolean success indicator</li>
10175      * </ul>
10176      * @param {Object} scope The scope in which to call the callback
10177      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10178      */
10179     load : function(params, reader, callback, scope, arg){
10180         if(this.fireEvent("beforeload", this, params) !== false){
10181             var  o = {
10182                 params : params || {},
10183                 request: {
10184                     callback : callback,
10185                     scope : scope,
10186                     arg : arg
10187                 },
10188                 reader: reader,
10189                 callback : this.loadResponse,
10190                 scope: this
10191             };
10192             if(this.useAjax){
10193                 Roo.applyIf(o, this.conn);
10194                 if(this.activeRequest){
10195                     Roo.Ajax.abort(this.activeRequest);
10196                 }
10197                 this.activeRequest = Roo.Ajax.request(o);
10198             }else{
10199                 this.conn.request(o);
10200             }
10201         }else{
10202             callback.call(scope||this, null, arg, false);
10203         }
10204     },
10205
10206     // private
10207     loadResponse : function(o, success, response){
10208         delete this.activeRequest;
10209         if(!success){
10210             this.fireEvent("loadexception", this, o, response);
10211             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10212             return;
10213         }
10214         var result;
10215         try {
10216             result = o.reader.read(response);
10217         }catch(e){
10218             this.fireEvent("loadexception", this, o, response, e);
10219             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10220             return;
10221         }
10222         
10223         this.fireEvent("load", this, o, o.request.arg);
10224         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10225     },
10226
10227     // private
10228     update : function(dataSet){
10229
10230     },
10231
10232     // private
10233     updateResponse : function(dataSet){
10234
10235     }
10236 });/*
10237  * Based on:
10238  * Ext JS Library 1.1.1
10239  * Copyright(c) 2006-2007, Ext JS, LLC.
10240  *
10241  * Originally Released Under LGPL - original licence link has changed is not relivant.
10242  *
10243  * Fork - LGPL
10244  * <script type="text/javascript">
10245  */
10246
10247 /**
10248  * @class Roo.data.ScriptTagProxy
10249  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10250  * other than the originating domain of the running page.<br><br>
10251  * <p>
10252  * <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
10253  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10254  * <p>
10255  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10256  * source code that is used as the source inside a &lt;script> tag.<br><br>
10257  * <p>
10258  * In order for the browser to process the returned data, the server must wrap the data object
10259  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10260  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10261  * depending on whether the callback name was passed:
10262  * <p>
10263  * <pre><code>
10264 boolean scriptTag = false;
10265 String cb = request.getParameter("callback");
10266 if (cb != null) {
10267     scriptTag = true;
10268     response.setContentType("text/javascript");
10269 } else {
10270     response.setContentType("application/x-json");
10271 }
10272 Writer out = response.getWriter();
10273 if (scriptTag) {
10274     out.write(cb + "(");
10275 }
10276 out.print(dataBlock.toJsonString());
10277 if (scriptTag) {
10278     out.write(");");
10279 }
10280 </pre></code>
10281  *
10282  * @constructor
10283  * @param {Object} config A configuration object.
10284  */
10285 Roo.data.ScriptTagProxy = function(config){
10286     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10287     Roo.apply(this, config);
10288     this.head = document.getElementsByTagName("head")[0];
10289 };
10290
10291 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10292
10293 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10294     /**
10295      * @cfg {String} url The URL from which to request the data object.
10296      */
10297     /**
10298      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10299      */
10300     timeout : 30000,
10301     /**
10302      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10303      * the server the name of the callback function set up by the load call to process the returned data object.
10304      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10305      * javascript output which calls this named function passing the data object as its only parameter.
10306      */
10307     callbackParam : "callback",
10308     /**
10309      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10310      * name to the request.
10311      */
10312     nocache : true,
10313
10314     /**
10315      * Load data from the configured URL, read the data object into
10316      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10317      * process that block using the passed callback.
10318      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10319      * for the request to the remote server.
10320      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10321      * object into a block of Roo.data.Records.
10322      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10323      * The function must be passed <ul>
10324      * <li>The Record block object</li>
10325      * <li>The "arg" argument from the load function</li>
10326      * <li>A boolean success indicator</li>
10327      * </ul>
10328      * @param {Object} scope The scope in which to call the callback
10329      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10330      */
10331     load : function(params, reader, callback, scope, arg){
10332         if(this.fireEvent("beforeload", this, params) !== false){
10333
10334             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10335
10336             var url = this.url;
10337             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10338             if(this.nocache){
10339                 url += "&_dc=" + (new Date().getTime());
10340             }
10341             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10342             var trans = {
10343                 id : transId,
10344                 cb : "stcCallback"+transId,
10345                 scriptId : "stcScript"+transId,
10346                 params : params,
10347                 arg : arg,
10348                 url : url,
10349                 callback : callback,
10350                 scope : scope,
10351                 reader : reader
10352             };
10353             var conn = this;
10354
10355             window[trans.cb] = function(o){
10356                 conn.handleResponse(o, trans);
10357             };
10358
10359             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10360
10361             if(this.autoAbort !== false){
10362                 this.abort();
10363             }
10364
10365             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10366
10367             var script = document.createElement("script");
10368             script.setAttribute("src", url);
10369             script.setAttribute("type", "text/javascript");
10370             script.setAttribute("id", trans.scriptId);
10371             this.head.appendChild(script);
10372
10373             this.trans = trans;
10374         }else{
10375             callback.call(scope||this, null, arg, false);
10376         }
10377     },
10378
10379     // private
10380     isLoading : function(){
10381         return this.trans ? true : false;
10382     },
10383
10384     /**
10385      * Abort the current server request.
10386      */
10387     abort : function(){
10388         if(this.isLoading()){
10389             this.destroyTrans(this.trans);
10390         }
10391     },
10392
10393     // private
10394     destroyTrans : function(trans, isLoaded){
10395         this.head.removeChild(document.getElementById(trans.scriptId));
10396         clearTimeout(trans.timeoutId);
10397         if(isLoaded){
10398             window[trans.cb] = undefined;
10399             try{
10400                 delete window[trans.cb];
10401             }catch(e){}
10402         }else{
10403             // if hasn't been loaded, wait for load to remove it to prevent script error
10404             window[trans.cb] = function(){
10405                 window[trans.cb] = undefined;
10406                 try{
10407                     delete window[trans.cb];
10408                 }catch(e){}
10409             };
10410         }
10411     },
10412
10413     // private
10414     handleResponse : function(o, trans){
10415         this.trans = false;
10416         this.destroyTrans(trans, true);
10417         var result;
10418         try {
10419             result = trans.reader.readRecords(o);
10420         }catch(e){
10421             this.fireEvent("loadexception", this, o, trans.arg, e);
10422             trans.callback.call(trans.scope||window, null, trans.arg, false);
10423             return;
10424         }
10425         this.fireEvent("load", this, o, trans.arg);
10426         trans.callback.call(trans.scope||window, result, trans.arg, true);
10427     },
10428
10429     // private
10430     handleFailure : function(trans){
10431         this.trans = false;
10432         this.destroyTrans(trans, false);
10433         this.fireEvent("loadexception", this, null, trans.arg);
10434         trans.callback.call(trans.scope||window, null, trans.arg, false);
10435     }
10436 });/*
10437  * Based on:
10438  * Ext JS Library 1.1.1
10439  * Copyright(c) 2006-2007, Ext JS, LLC.
10440  *
10441  * Originally Released Under LGPL - original licence link has changed is not relivant.
10442  *
10443  * Fork - LGPL
10444  * <script type="text/javascript">
10445  */
10446
10447 /**
10448  * @class Roo.data.JsonReader
10449  * @extends Roo.data.DataReader
10450  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10451  * based on mappings in a provided Roo.data.Record constructor.
10452  * 
10453  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10454  * in the reply previously. 
10455  * 
10456  * <p>
10457  * Example code:
10458  * <pre><code>
10459 var RecordDef = Roo.data.Record.create([
10460     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10461     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10462 ]);
10463 var myReader = new Roo.data.JsonReader({
10464     totalProperty: "results",    // The property which contains the total dataset size (optional)
10465     root: "rows",                // The property which contains an Array of row objects
10466     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10467 }, RecordDef);
10468 </code></pre>
10469  * <p>
10470  * This would consume a JSON file like this:
10471  * <pre><code>
10472 { 'results': 2, 'rows': [
10473     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10474     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10475 }
10476 </code></pre>
10477  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10478  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10479  * paged from the remote server.
10480  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10481  * @cfg {String} root name of the property which contains the Array of row objects.
10482  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10483  * @constructor
10484  * Create a new JsonReader
10485  * @param {Object} meta Metadata configuration options
10486  * @param {Object} recordType Either an Array of field definition objects,
10487  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10488  */
10489 Roo.data.JsonReader = function(meta, recordType){
10490     
10491     meta = meta || {};
10492     // set some defaults:
10493     Roo.applyIf(meta, {
10494         totalProperty: 'total',
10495         successProperty : 'success',
10496         root : 'data',
10497         id : 'id'
10498     });
10499     
10500     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10501 };
10502 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10503     
10504     /**
10505      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10506      * Used by Store query builder to append _requestMeta to params.
10507      * 
10508      */
10509     metaFromRemote : false,
10510     /**
10511      * This method is only used by a DataProxy which has retrieved data from a remote server.
10512      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10513      * @return {Object} data A data block which is used by an Roo.data.Store object as
10514      * a cache of Roo.data.Records.
10515      */
10516     read : function(response){
10517         var json = response.responseText;
10518        
10519         var o = /* eval:var:o */ eval("("+json+")");
10520         if(!o) {
10521             throw {message: "JsonReader.read: Json object not found"};
10522         }
10523         
10524         if(o.metaData){
10525             
10526             delete this.ef;
10527             this.metaFromRemote = true;
10528             this.meta = o.metaData;
10529             this.recordType = Roo.data.Record.create(o.metaData.fields);
10530             this.onMetaChange(this.meta, this.recordType, o);
10531         }
10532         return this.readRecords(o);
10533     },
10534
10535     // private function a store will implement
10536     onMetaChange : function(meta, recordType, o){
10537
10538     },
10539
10540     /**
10541          * @ignore
10542          */
10543     simpleAccess: function(obj, subsc) {
10544         return obj[subsc];
10545     },
10546
10547         /**
10548          * @ignore
10549          */
10550     getJsonAccessor: function(){
10551         var re = /[\[\.]/;
10552         return function(expr) {
10553             try {
10554                 return(re.test(expr))
10555                     ? new Function("obj", "return obj." + expr)
10556                     : function(obj){
10557                         return obj[expr];
10558                     };
10559             } catch(e){}
10560             return Roo.emptyFn;
10561         };
10562     }(),
10563
10564     /**
10565      * Create a data block containing Roo.data.Records from an XML document.
10566      * @param {Object} o An object which contains an Array of row objects in the property specified
10567      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10568      * which contains the total size of the dataset.
10569      * @return {Object} data A data block which is used by an Roo.data.Store object as
10570      * a cache of Roo.data.Records.
10571      */
10572     readRecords : function(o){
10573         /**
10574          * After any data loads, the raw JSON data is available for further custom processing.
10575          * @type Object
10576          */
10577         this.o = o;
10578         var s = this.meta, Record = this.recordType,
10579             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10580
10581 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10582         if (!this.ef) {
10583             if(s.totalProperty) {
10584                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10585                 }
10586                 if(s.successProperty) {
10587                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10588                 }
10589                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10590                 if (s.id) {
10591                         var g = this.getJsonAccessor(s.id);
10592                         this.getId = function(rec) {
10593                                 var r = g(rec);  
10594                                 return (r === undefined || r === "") ? null : r;
10595                         };
10596                 } else {
10597                         this.getId = function(){return null;};
10598                 }
10599             this.ef = [];
10600             for(var jj = 0; jj < fl; jj++){
10601                 f = fi[jj];
10602                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10603                 this.ef[jj] = this.getJsonAccessor(map);
10604             }
10605         }
10606
10607         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10608         if(s.totalProperty){
10609             var vt = parseInt(this.getTotal(o), 10);
10610             if(!isNaN(vt)){
10611                 totalRecords = vt;
10612             }
10613         }
10614         if(s.successProperty){
10615             var vs = this.getSuccess(o);
10616             if(vs === false || vs === 'false'){
10617                 success = false;
10618             }
10619         }
10620         var records = [];
10621         for(var i = 0; i < c; i++){
10622                 var n = root[i];
10623             var values = {};
10624             var id = this.getId(n);
10625             for(var j = 0; j < fl; j++){
10626                 f = fi[j];
10627             var v = this.ef[j](n);
10628             if (!f.convert) {
10629                 Roo.log('missing convert for ' + f.name);
10630                 Roo.log(f);
10631                 continue;
10632             }
10633             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10634             }
10635             var record = new Record(values, id);
10636             record.json = n;
10637             records[i] = record;
10638         }
10639         return {
10640             raw : o,
10641             success : success,
10642             records : records,
10643             totalRecords : totalRecords
10644         };
10645     }
10646 });/*
10647  * Based on:
10648  * Ext JS Library 1.1.1
10649  * Copyright(c) 2006-2007, Ext JS, LLC.
10650  *
10651  * Originally Released Under LGPL - original licence link has changed is not relivant.
10652  *
10653  * Fork - LGPL
10654  * <script type="text/javascript">
10655  */
10656
10657 /**
10658  * @class Roo.data.ArrayReader
10659  * @extends Roo.data.DataReader
10660  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10661  * Each element of that Array represents a row of data fields. The
10662  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10663  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10664  * <p>
10665  * Example code:.
10666  * <pre><code>
10667 var RecordDef = Roo.data.Record.create([
10668     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10669     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10670 ]);
10671 var myReader = new Roo.data.ArrayReader({
10672     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10673 }, RecordDef);
10674 </code></pre>
10675  * <p>
10676  * This would consume an Array like this:
10677  * <pre><code>
10678 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10679   </code></pre>
10680  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10681  * @constructor
10682  * Create a new JsonReader
10683  * @param {Object} meta Metadata configuration options.
10684  * @param {Object} recordType Either an Array of field definition objects
10685  * as specified to {@link Roo.data.Record#create},
10686  * or an {@link Roo.data.Record} object
10687  * created using {@link Roo.data.Record#create}.
10688  */
10689 Roo.data.ArrayReader = function(meta, recordType){
10690     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10691 };
10692
10693 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10694     /**
10695      * Create a data block containing Roo.data.Records from an XML document.
10696      * @param {Object} o An Array of row objects which represents the dataset.
10697      * @return {Object} data A data block which is used by an Roo.data.Store object as
10698      * a cache of Roo.data.Records.
10699      */
10700     readRecords : function(o){
10701         var sid = this.meta ? this.meta.id : null;
10702         var recordType = this.recordType, fields = recordType.prototype.fields;
10703         var records = [];
10704         var root = o;
10705             for(var i = 0; i < root.length; i++){
10706                     var n = root[i];
10707                 var values = {};
10708                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10709                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10710                 var f = fields.items[j];
10711                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10712                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10713                 v = f.convert(v);
10714                 values[f.name] = v;
10715             }
10716                 var record = new recordType(values, id);
10717                 record.json = n;
10718                 records[records.length] = record;
10719             }
10720             return {
10721                 records : records,
10722                 totalRecords : records.length
10723             };
10724     }
10725 });/*
10726  * - LGPL
10727  * * 
10728  */
10729
10730 /**
10731  * @class Roo.bootstrap.ComboBox
10732  * @extends Roo.bootstrap.TriggerField
10733  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10734  * @cfg {Boolean} append (true|false) default false
10735  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10736  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10737  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10738  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10739  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10740  * @constructor
10741  * Create a new ComboBox.
10742  * @param {Object} config Configuration options
10743  */
10744 Roo.bootstrap.ComboBox = function(config){
10745     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10746     this.addEvents({
10747         /**
10748          * @event expand
10749          * Fires when the dropdown list is expanded
10750              * @param {Roo.bootstrap.ComboBox} combo This combo box
10751              */
10752         'expand' : true,
10753         /**
10754          * @event collapse
10755          * Fires when the dropdown list is collapsed
10756              * @param {Roo.bootstrap.ComboBox} combo This combo box
10757              */
10758         'collapse' : true,
10759         /**
10760          * @event beforeselect
10761          * Fires before a list item is selected. Return false to cancel the selection.
10762              * @param {Roo.bootstrap.ComboBox} combo This combo box
10763              * @param {Roo.data.Record} record The data record returned from the underlying store
10764              * @param {Number} index The index of the selected item in the dropdown list
10765              */
10766         'beforeselect' : true,
10767         /**
10768          * @event select
10769          * Fires when a list item is selected
10770              * @param {Roo.bootstrap.ComboBox} combo This combo box
10771              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10772              * @param {Number} index The index of the selected item in the dropdown list
10773              */
10774         'select' : true,
10775         /**
10776          * @event beforequery
10777          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10778          * The event object passed has these properties:
10779              * @param {Roo.bootstrap.ComboBox} combo This combo box
10780              * @param {String} query The query
10781              * @param {Boolean} forceAll true to force "all" query
10782              * @param {Boolean} cancel true to cancel the query
10783              * @param {Object} e The query event object
10784              */
10785         'beforequery': true,
10786          /**
10787          * @event add
10788          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10789              * @param {Roo.bootstrap.ComboBox} combo This combo box
10790              */
10791         'add' : true,
10792         /**
10793          * @event edit
10794          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10795              * @param {Roo.bootstrap.ComboBox} combo This combo box
10796              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10797              */
10798         'edit' : true,
10799         /**
10800          * @event remove
10801          * Fires when the remove value from the combobox array
10802              * @param {Roo.bootstrap.ComboBox} combo This combo box
10803              */
10804         'remove' : true,
10805         /**
10806          * @event specialfilter
10807          * Fires when specialfilter
10808             * @param {Roo.bootstrap.ComboBox} combo This combo box
10809             */
10810         'specialfilter' : true
10811         
10812     });
10813     
10814     this.item = [];
10815     this.tickItems = [];
10816     
10817     this.selectedIndex = -1;
10818     if(this.mode == 'local'){
10819         if(config.queryDelay === undefined){
10820             this.queryDelay = 10;
10821         }
10822         if(config.minChars === undefined){
10823             this.minChars = 0;
10824         }
10825     }
10826 };
10827
10828 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10829      
10830     /**
10831      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10832      * rendering into an Roo.Editor, defaults to false)
10833      */
10834     /**
10835      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10836      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10837      */
10838     /**
10839      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10840      */
10841     /**
10842      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10843      * the dropdown list (defaults to undefined, with no header element)
10844      */
10845
10846      /**
10847      * @cfg {String/Roo.Template} tpl The template to use to render the output
10848      */
10849      
10850      /**
10851      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10852      */
10853     listWidth: undefined,
10854     /**
10855      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10856      * mode = 'remote' or 'text' if mode = 'local')
10857      */
10858     displayField: undefined,
10859     
10860     /**
10861      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10862      * mode = 'remote' or 'value' if mode = 'local'). 
10863      * Note: use of a valueField requires the user make a selection
10864      * in order for a value to be mapped.
10865      */
10866     valueField: undefined,
10867     
10868     
10869     /**
10870      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10871      * field's data value (defaults to the underlying DOM element's name)
10872      */
10873     hiddenName: undefined,
10874     /**
10875      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10876      */
10877     listClass: '',
10878     /**
10879      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10880      */
10881     selectedClass: 'active',
10882     
10883     /**
10884      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10885      */
10886     shadow:'sides',
10887     /**
10888      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10889      * anchor positions (defaults to 'tl-bl')
10890      */
10891     listAlign: 'tl-bl?',
10892     /**
10893      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10894      */
10895     maxHeight: 300,
10896     /**
10897      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10898      * query specified by the allQuery config option (defaults to 'query')
10899      */
10900     triggerAction: 'query',
10901     /**
10902      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10903      * (defaults to 4, does not apply if editable = false)
10904      */
10905     minChars : 4,
10906     /**
10907      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10908      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10909      */
10910     typeAhead: false,
10911     /**
10912      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10913      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10914      */
10915     queryDelay: 500,
10916     /**
10917      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10918      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10919      */
10920     pageSize: 0,
10921     /**
10922      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10923      * when editable = true (defaults to false)
10924      */
10925     selectOnFocus:false,
10926     /**
10927      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10928      */
10929     queryParam: 'query',
10930     /**
10931      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10932      * when mode = 'remote' (defaults to 'Loading...')
10933      */
10934     loadingText: 'Loading...',
10935     /**
10936      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10937      */
10938     resizable: false,
10939     /**
10940      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10941      */
10942     handleHeight : 8,
10943     /**
10944      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10945      * traditional select (defaults to true)
10946      */
10947     editable: true,
10948     /**
10949      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10950      */
10951     allQuery: '',
10952     /**
10953      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10954      */
10955     mode: 'remote',
10956     /**
10957      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10958      * listWidth has a higher value)
10959      */
10960     minListWidth : 70,
10961     /**
10962      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10963      * allow the user to set arbitrary text into the field (defaults to false)
10964      */
10965     forceSelection:false,
10966     /**
10967      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10968      * if typeAhead = true (defaults to 250)
10969      */
10970     typeAheadDelay : 250,
10971     /**
10972      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10973      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10974      */
10975     valueNotFoundText : undefined,
10976     /**
10977      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10978      */
10979     blockFocus : false,
10980     
10981     /**
10982      * @cfg {Boolean} disableClear Disable showing of clear button.
10983      */
10984     disableClear : false,
10985     /**
10986      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10987      */
10988     alwaysQuery : false,
10989     
10990     /**
10991      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10992      */
10993     multiple : false,
10994     
10995     /**
10996      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
10997      */
10998     invalidClass : "has-warning",
10999     
11000     /**
11001      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11002      */
11003     validClass : "has-success",
11004     
11005     /**
11006      * @cfg {Boolean} specialFilter (true|false) special filter default false
11007      */
11008     specialFilter : false,
11009     
11010     //private
11011     addicon : false,
11012     editicon: false,
11013     
11014     page: 0,
11015     hasQuery: false,
11016     append: false,
11017     loadNext: false,
11018     autoFocus : true,
11019     tickable : false,
11020     btnPosition : 'right',
11021     triggerList : true,
11022     showToggleBtn : true,
11023     // element that contains real text value.. (when hidden is used..)
11024     
11025     getAutoCreate : function()
11026     {
11027         var cfg = false;
11028         
11029         /*
11030          *  Normal ComboBox
11031          */
11032         if(!this.tickable){
11033             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11034             return cfg;
11035         }
11036         
11037         /*
11038          *  ComboBox with tickable selections
11039          */
11040              
11041         var align = this.labelAlign || this.parentLabelAlign();
11042         
11043         cfg = {
11044             cls : 'form-group roo-combobox-tickable' //input-group
11045         };
11046         
11047         var buttons = {
11048             tag : 'div',
11049             cls : 'tickable-buttons',
11050             cn : [
11051                 {
11052                     tag : 'button',
11053                     type : 'button',
11054                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11055                     html : 'Edit'
11056                 },
11057                 {
11058                     tag : 'button',
11059                     type : 'button',
11060                     name : 'ok',
11061                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11062                     html : 'Done'
11063                 },
11064                 {
11065                     tag : 'button',
11066                     type : 'button',
11067                     name : 'cancel',
11068                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11069                     html : 'Cancel'
11070                 }
11071             ]
11072         };
11073         
11074         if(this.editable){
11075             buttons.cn.unshift({
11076                 tag: 'input',
11077                 cls: 'select2-search-field-input'
11078             });
11079         }
11080         
11081         var _this = this;
11082         
11083         Roo.each(buttons.cn, function(c){
11084             if (_this.size) {
11085                 c.cls += ' btn-' + _this.size;
11086             }
11087
11088             if (_this.disabled) {
11089                 c.disabled = true;
11090             }
11091         });
11092         
11093         var box = {
11094             tag: 'div',
11095             cn: [
11096                 {
11097                     tag: 'input',
11098                     type : 'hidden',
11099                     cls: 'form-hidden-field'
11100                 },
11101                 {
11102                     tag: 'ul',
11103                     cls: 'select2-choices',
11104                     cn:[
11105                         {
11106                             tag: 'li',
11107                             cls: 'select2-search-field',
11108                             cn: [
11109
11110                                 buttons
11111                             ]
11112                         }
11113                     ]
11114                 }
11115             ]
11116         }
11117         
11118         var combobox = {
11119             cls: 'select2-container input-group select2-container-multi',
11120             cn: [
11121                 box
11122 //                {
11123 //                    tag: 'ul',
11124 //                    cls: 'typeahead typeahead-long dropdown-menu',
11125 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11126 //                }
11127             ]
11128         };
11129         
11130         if(this.hasFeedback && !this.allowBlank){
11131             
11132             var feedback = {
11133                 tag: 'span',
11134                 cls: 'glyphicon form-control-feedback'
11135             };
11136
11137             combobox.cn.push(feedback);
11138         }
11139         
11140         if (align ==='left' && this.fieldLabel.length) {
11141             
11142                 Roo.log("left and has label");
11143                 cfg.cn = [
11144                     
11145                     {
11146                         tag: 'label',
11147                         'for' :  id,
11148                         cls : 'control-label col-sm-' + this.labelWidth,
11149                         html : this.fieldLabel
11150                         
11151                     },
11152                     {
11153                         cls : "col-sm-" + (12 - this.labelWidth), 
11154                         cn: [
11155                             combobox
11156                         ]
11157                     }
11158                     
11159                 ];
11160         } else if ( this.fieldLabel.length) {
11161                 Roo.log(" label");
11162                  cfg.cn = [
11163                    
11164                     {
11165                         tag: 'label',
11166                         //cls : 'input-group-addon',
11167                         html : this.fieldLabel
11168                         
11169                     },
11170                     
11171                     combobox
11172                     
11173                 ];
11174
11175         } else {
11176             
11177                 Roo.log(" no label && no align");
11178                 cfg = combobox
11179                      
11180                 
11181         }
11182          
11183         var settings=this;
11184         ['xs','sm','md','lg'].map(function(size){
11185             if (settings[size]) {
11186                 cfg.cls += ' col-' + size + '-' + settings[size];
11187             }
11188         });
11189         
11190         return cfg;
11191         
11192     },
11193     
11194     // private
11195     initEvents: function()
11196     {
11197         
11198         if (!this.store) {
11199             throw "can not find store for combo";
11200         }
11201         this.store = Roo.factory(this.store, Roo.data);
11202         
11203         if(this.tickable){
11204             this.initTickableEvents();
11205             return;
11206         }
11207         
11208         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11209         
11210         if(this.hiddenName){
11211             
11212             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11213             
11214             this.hiddenField.dom.value =
11215                 this.hiddenValue !== undefined ? this.hiddenValue :
11216                 this.value !== undefined ? this.value : '';
11217
11218             // prevent input submission
11219             this.el.dom.removeAttribute('name');
11220             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11221              
11222              
11223         }
11224         //if(Roo.isGecko){
11225         //    this.el.dom.setAttribute('autocomplete', 'off');
11226         //}
11227         
11228         var cls = 'x-combo-list';
11229         
11230         //this.list = new Roo.Layer({
11231         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11232         //});
11233         
11234         var _this = this;
11235         
11236         (function(){
11237             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11238             _this.list.setWidth(lw);
11239         }).defer(100);
11240         
11241         this.list.on('mouseover', this.onViewOver, this);
11242         this.list.on('mousemove', this.onViewMove, this);
11243         
11244         this.list.on('scroll', this.onViewScroll, this);
11245         
11246         /*
11247         this.list.swallowEvent('mousewheel');
11248         this.assetHeight = 0;
11249
11250         if(this.title){
11251             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11252             this.assetHeight += this.header.getHeight();
11253         }
11254
11255         this.innerList = this.list.createChild({cls:cls+'-inner'});
11256         this.innerList.on('mouseover', this.onViewOver, this);
11257         this.innerList.on('mousemove', this.onViewMove, this);
11258         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11259         
11260         if(this.allowBlank && !this.pageSize && !this.disableClear){
11261             this.footer = this.list.createChild({cls:cls+'-ft'});
11262             this.pageTb = new Roo.Toolbar(this.footer);
11263            
11264         }
11265         if(this.pageSize){
11266             this.footer = this.list.createChild({cls:cls+'-ft'});
11267             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11268                     {pageSize: this.pageSize});
11269             
11270         }
11271         
11272         if (this.pageTb && this.allowBlank && !this.disableClear) {
11273             var _this = this;
11274             this.pageTb.add(new Roo.Toolbar.Fill(), {
11275                 cls: 'x-btn-icon x-btn-clear',
11276                 text: '&#160;',
11277                 handler: function()
11278                 {
11279                     _this.collapse();
11280                     _this.clearValue();
11281                     _this.onSelect(false, -1);
11282                 }
11283             });
11284         }
11285         if (this.footer) {
11286             this.assetHeight += this.footer.getHeight();
11287         }
11288         */
11289             
11290         if(!this.tpl){
11291             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11292         }
11293
11294         this.view = new Roo.View(this.list, this.tpl, {
11295             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11296         });
11297         //this.view.wrapEl.setDisplayed(false);
11298         this.view.on('click', this.onViewClick, this);
11299         
11300         
11301         
11302         this.store.on('beforeload', this.onBeforeLoad, this);
11303         this.store.on('load', this.onLoad, this);
11304         this.store.on('loadexception', this.onLoadException, this);
11305         /*
11306         if(this.resizable){
11307             this.resizer = new Roo.Resizable(this.list,  {
11308                pinned:true, handles:'se'
11309             });
11310             this.resizer.on('resize', function(r, w, h){
11311                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11312                 this.listWidth = w;
11313                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11314                 this.restrictHeight();
11315             }, this);
11316             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11317         }
11318         */
11319         if(!this.editable){
11320             this.editable = true;
11321             this.setEditable(false);
11322         }
11323         
11324         /*
11325         
11326         if (typeof(this.events.add.listeners) != 'undefined') {
11327             
11328             this.addicon = this.wrap.createChild(
11329                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11330        
11331             this.addicon.on('click', function(e) {
11332                 this.fireEvent('add', this);
11333             }, this);
11334         }
11335         if (typeof(this.events.edit.listeners) != 'undefined') {
11336             
11337             this.editicon = this.wrap.createChild(
11338                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11339             if (this.addicon) {
11340                 this.editicon.setStyle('margin-left', '40px');
11341             }
11342             this.editicon.on('click', function(e) {
11343                 
11344                 // we fire even  if inothing is selected..
11345                 this.fireEvent('edit', this, this.lastData );
11346                 
11347             }, this);
11348         }
11349         */
11350         
11351         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11352             "up" : function(e){
11353                 this.inKeyMode = true;
11354                 this.selectPrev();
11355             },
11356
11357             "down" : function(e){
11358                 if(!this.isExpanded()){
11359                     this.onTriggerClick();
11360                 }else{
11361                     this.inKeyMode = true;
11362                     this.selectNext();
11363                 }
11364             },
11365
11366             "enter" : function(e){
11367 //                this.onViewClick();
11368                 //return true;
11369                 this.collapse();
11370                 
11371                 if(this.fireEvent("specialkey", this, e)){
11372                     this.onViewClick(false);
11373                 }
11374                 
11375                 return true;
11376             },
11377
11378             "esc" : function(e){
11379                 this.collapse();
11380             },
11381
11382             "tab" : function(e){
11383                 this.collapse();
11384                 
11385                 if(this.fireEvent("specialkey", this, e)){
11386                     this.onViewClick(false);
11387                 }
11388                 
11389                 return true;
11390             },
11391
11392             scope : this,
11393
11394             doRelay : function(foo, bar, hname){
11395                 if(hname == 'down' || this.scope.isExpanded()){
11396                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11397                 }
11398                 return true;
11399             },
11400
11401             forceKeyDown: true
11402         });
11403         
11404         
11405         this.queryDelay = Math.max(this.queryDelay || 10,
11406                 this.mode == 'local' ? 10 : 250);
11407         
11408         
11409         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11410         
11411         if(this.typeAhead){
11412             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11413         }
11414         if(this.editable !== false){
11415             this.inputEl().on("keyup", this.onKeyUp, this);
11416         }
11417         if(this.forceSelection){
11418             this.inputEl().on('blur', this.doForce, this);
11419         }
11420         
11421         if(this.multiple){
11422             this.choices = this.el.select('ul.select2-choices', true).first();
11423             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11424         }
11425     },
11426     
11427     initTickableEvents: function()
11428     {   
11429         this.createList();
11430         
11431         if(this.hiddenName){
11432             
11433             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11434             
11435             this.hiddenField.dom.value =
11436                 this.hiddenValue !== undefined ? this.hiddenValue :
11437                 this.value !== undefined ? this.value : '';
11438
11439             // prevent input submission
11440             this.el.dom.removeAttribute('name');
11441             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11442              
11443              
11444         }
11445         
11446 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11447         
11448         this.choices = this.el.select('ul.select2-choices', true).first();
11449         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11450         if(this.triggerList){
11451             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11452         }
11453          
11454         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11455         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11456         
11457         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11458         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11459         
11460         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11461         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11462         
11463         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11464         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11465         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11466         
11467         this.okBtn.hide();
11468         this.cancelBtn.hide();
11469         
11470         var _this = this;
11471         
11472         (function(){
11473             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11474             _this.list.setWidth(lw);
11475         }).defer(100);
11476         
11477         this.list.on('mouseover', this.onViewOver, this);
11478         this.list.on('mousemove', this.onViewMove, this);
11479         
11480         this.list.on('scroll', this.onViewScroll, this);
11481         
11482         if(!this.tpl){
11483             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>';
11484         }
11485
11486         this.view = new Roo.View(this.list, this.tpl, {
11487             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11488         });
11489         
11490         //this.view.wrapEl.setDisplayed(false);
11491         this.view.on('click', this.onViewClick, this);
11492         
11493         
11494         
11495         this.store.on('beforeload', this.onBeforeLoad, this);
11496         this.store.on('load', this.onLoad, this);
11497         this.store.on('loadexception', this.onLoadException, this);
11498         
11499         if(this.editable){
11500             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11501                 "up" : function(e){
11502                     this.inKeyMode = true;
11503                     this.selectPrev();
11504                 },
11505
11506                 "down" : function(e){
11507                     this.inKeyMode = true;
11508                     this.selectNext();
11509                 },
11510
11511                 "enter" : function(e){
11512                     if(this.fireEvent("specialkey", this, e)){
11513                         this.onViewClick(false);
11514                     }
11515                     
11516                     return true;
11517                 },
11518
11519                 "esc" : function(e){
11520                     this.onTickableFooterButtonClick(e, false, false);
11521                 },
11522
11523                 "tab" : function(e){
11524                     this.fireEvent("specialkey", this, e);
11525                     
11526                     this.onTickableFooterButtonClick(e, false, false);
11527                     
11528                     return true;
11529                 },
11530
11531                 scope : this,
11532
11533                 doRelay : function(e, fn, key){
11534                     if(this.scope.isExpanded()){
11535                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11536                     }
11537                     return true;
11538                 },
11539
11540                 forceKeyDown: true
11541             });
11542         }
11543         
11544         this.queryDelay = Math.max(this.queryDelay || 10,
11545                 this.mode == 'local' ? 10 : 250);
11546         
11547         
11548         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11549         
11550         if(this.typeAhead){
11551             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11552         }
11553         
11554         if(this.editable !== false){
11555             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11556         }
11557         
11558     },
11559
11560     onDestroy : function(){
11561         if(this.view){
11562             this.view.setStore(null);
11563             this.view.el.removeAllListeners();
11564             this.view.el.remove();
11565             this.view.purgeListeners();
11566         }
11567         if(this.list){
11568             this.list.dom.innerHTML  = '';
11569         }
11570         
11571         if(this.store){
11572             this.store.un('beforeload', this.onBeforeLoad, this);
11573             this.store.un('load', this.onLoad, this);
11574             this.store.un('loadexception', this.onLoadException, this);
11575         }
11576         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11577     },
11578
11579     // private
11580     fireKey : function(e){
11581         if(e.isNavKeyPress() && !this.list.isVisible()){
11582             this.fireEvent("specialkey", this, e);
11583         }
11584     },
11585
11586     // private
11587     onResize: function(w, h){
11588 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11589 //        
11590 //        if(typeof w != 'number'){
11591 //            // we do not handle it!?!?
11592 //            return;
11593 //        }
11594 //        var tw = this.trigger.getWidth();
11595 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11596 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11597 //        var x = w - tw;
11598 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11599 //            
11600 //        //this.trigger.setStyle('left', x+'px');
11601 //        
11602 //        if(this.list && this.listWidth === undefined){
11603 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11604 //            this.list.setWidth(lw);
11605 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11606 //        }
11607         
11608     
11609         
11610     },
11611
11612     /**
11613      * Allow or prevent the user from directly editing the field text.  If false is passed,
11614      * the user will only be able to select from the items defined in the dropdown list.  This method
11615      * is the runtime equivalent of setting the 'editable' config option at config time.
11616      * @param {Boolean} value True to allow the user to directly edit the field text
11617      */
11618     setEditable : function(value){
11619         if(value == this.editable){
11620             return;
11621         }
11622         this.editable = value;
11623         if(!value){
11624             this.inputEl().dom.setAttribute('readOnly', true);
11625             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11626             this.inputEl().addClass('x-combo-noedit');
11627         }else{
11628             this.inputEl().dom.setAttribute('readOnly', false);
11629             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11630             this.inputEl().removeClass('x-combo-noedit');
11631         }
11632     },
11633
11634     // private
11635     
11636     onBeforeLoad : function(combo,opts){
11637         if(!this.hasFocus){
11638             return;
11639         }
11640          if (!opts.add) {
11641             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11642          }
11643         this.restrictHeight();
11644         this.selectedIndex = -1;
11645     },
11646
11647     // private
11648     onLoad : function(){
11649         
11650         this.hasQuery = false;
11651         
11652         if(!this.hasFocus){
11653             return;
11654         }
11655         
11656         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11657             this.loading.hide();
11658         }
11659              
11660         if(this.store.getCount() > 0){
11661             this.expand();
11662             this.restrictHeight();
11663             if(this.lastQuery == this.allQuery){
11664                 if(this.editable && !this.tickable){
11665                     this.inputEl().dom.select();
11666                 }
11667                 
11668                 if(
11669                     !this.selectByValue(this.value, true) &&
11670                     this.autoFocus && 
11671                     (
11672                         !this.store.lastOptions ||
11673                         typeof(this.store.lastOptions.add) == 'undefined' || 
11674                         this.store.lastOptions.add != true
11675                     )
11676                 ){
11677                     this.select(0, true);
11678                 }
11679             }else{
11680                 if(this.autoFocus){
11681                     this.selectNext();
11682                 }
11683                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11684                     this.taTask.delay(this.typeAheadDelay);
11685                 }
11686             }
11687         }else{
11688             this.onEmptyResults();
11689         }
11690         
11691         //this.el.focus();
11692     },
11693     // private
11694     onLoadException : function()
11695     {
11696         this.hasQuery = false;
11697         
11698         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11699             this.loading.hide();
11700         }
11701         
11702         if(this.tickable && this.editable){
11703             return;
11704         }
11705         
11706         this.collapse();
11707         
11708         Roo.log(this.store.reader.jsonData);
11709         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11710             // fixme
11711             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11712         }
11713         
11714         
11715     },
11716     // private
11717     onTypeAhead : function(){
11718         if(this.store.getCount() > 0){
11719             var r = this.store.getAt(0);
11720             var newValue = r.data[this.displayField];
11721             var len = newValue.length;
11722             var selStart = this.getRawValue().length;
11723             
11724             if(selStart != len){
11725                 this.setRawValue(newValue);
11726                 this.selectText(selStart, newValue.length);
11727             }
11728         }
11729     },
11730
11731     // private
11732     onSelect : function(record, index){
11733         
11734         if(this.fireEvent('beforeselect', this, record, index) !== false){
11735         
11736             this.setFromData(index > -1 ? record.data : false);
11737             
11738             this.collapse();
11739             this.fireEvent('select', this, record, index);
11740         }
11741     },
11742
11743     /**
11744      * Returns the currently selected field value or empty string if no value is set.
11745      * @return {String} value The selected value
11746      */
11747     getValue : function(){
11748         
11749         if(this.multiple){
11750             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11751         }
11752         
11753         if(this.valueField){
11754             return typeof this.value != 'undefined' ? this.value : '';
11755         }else{
11756             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11757         }
11758     },
11759
11760     /**
11761      * Clears any text/value currently set in the field
11762      */
11763     clearValue : function(){
11764         if(this.hiddenField){
11765             this.hiddenField.dom.value = '';
11766         }
11767         this.value = '';
11768         this.setRawValue('');
11769         this.lastSelectionText = '';
11770         this.lastData = false;
11771         
11772     },
11773
11774     /**
11775      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11776      * will be displayed in the field.  If the value does not match the data value of an existing item,
11777      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11778      * Otherwise the field will be blank (although the value will still be set).
11779      * @param {String} value The value to match
11780      */
11781     setValue : function(v){
11782         if(this.multiple){
11783             this.syncValue();
11784             return;
11785         }
11786         
11787         var text = v;
11788         if(this.valueField){
11789             var r = this.findRecord(this.valueField, v);
11790             if(r){
11791                 text = r.data[this.displayField];
11792             }else if(this.valueNotFoundText !== undefined){
11793                 text = this.valueNotFoundText;
11794             }
11795         }
11796         this.lastSelectionText = text;
11797         if(this.hiddenField){
11798             this.hiddenField.dom.value = v;
11799         }
11800         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11801         this.value = v;
11802     },
11803     /**
11804      * @property {Object} the last set data for the element
11805      */
11806     
11807     lastData : false,
11808     /**
11809      * Sets the value of the field based on a object which is related to the record format for the store.
11810      * @param {Object} value the value to set as. or false on reset?
11811      */
11812     setFromData : function(o){
11813         
11814         if(this.multiple){
11815             this.addItem(o);
11816             return;
11817         }
11818             
11819         var dv = ''; // display value
11820         var vv = ''; // value value..
11821         this.lastData = o;
11822         if (this.displayField) {
11823             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11824         } else {
11825             // this is an error condition!!!
11826             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11827         }
11828         
11829         if(this.valueField){
11830             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11831         }
11832         
11833         if(this.hiddenField){
11834             this.hiddenField.dom.value = vv;
11835             
11836             this.lastSelectionText = dv;
11837             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11838             this.value = vv;
11839             return;
11840         }
11841         // no hidden field.. - we store the value in 'value', but still display
11842         // display field!!!!
11843         this.lastSelectionText = dv;
11844         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11845         this.value = vv;
11846         
11847         
11848     },
11849     // private
11850     reset : function(){
11851         // overridden so that last data is reset..
11852         
11853         if(this.multiple){
11854             this.clearItem();
11855             return;
11856         }
11857         
11858         this.setValue(this.originalValue);
11859         this.clearInvalid();
11860         this.lastData = false;
11861         if (this.view) {
11862             this.view.clearSelections();
11863         }
11864     },
11865     // private
11866     findRecord : function(prop, value){
11867         var record;
11868         if(this.store.getCount() > 0){
11869             this.store.each(function(r){
11870                 if(r.data[prop] == value){
11871                     record = r;
11872                     return false;
11873                 }
11874                 return true;
11875             });
11876         }
11877         return record;
11878     },
11879     
11880     getName: function()
11881     {
11882         // returns hidden if it's set..
11883         if (!this.rendered) {return ''};
11884         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11885         
11886     },
11887     // private
11888     onViewMove : function(e, t){
11889         this.inKeyMode = false;
11890     },
11891
11892     // private
11893     onViewOver : function(e, t){
11894         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11895             return;
11896         }
11897         var item = this.view.findItemFromChild(t);
11898         
11899         if(item){
11900             var index = this.view.indexOf(item);
11901             this.select(index, false);
11902         }
11903     },
11904
11905     // private
11906     onViewClick : function(view, doFocus, el, e)
11907     {
11908         var index = this.view.getSelectedIndexes()[0];
11909         
11910         var r = this.store.getAt(index);
11911         
11912         if(this.tickable){
11913             
11914             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
11915                 return;
11916             }
11917             
11918             var rm = false;
11919             var _this = this;
11920             
11921             Roo.each(this.tickItems, function(v,k){
11922                 
11923                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11924                     _this.tickItems.splice(k, 1);
11925                     
11926                     if(typeof(e) == 'undefined' && view == false){
11927                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
11928                     }
11929                     
11930                     rm = true;
11931                     return;
11932                 }
11933             });
11934             
11935             if(rm){
11936                 return;
11937             }
11938             
11939             this.tickItems.push(r.data);
11940             
11941             if(typeof(e) == 'undefined' && view == false){
11942                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
11943             }
11944                     
11945             return;
11946         }
11947         
11948         if(r){
11949             this.onSelect(r, index);
11950         }
11951         if(doFocus !== false && !this.blockFocus){
11952             this.inputEl().focus();
11953         }
11954     },
11955
11956     // private
11957     restrictHeight : function(){
11958         //this.innerList.dom.style.height = '';
11959         //var inner = this.innerList.dom;
11960         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11961         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11962         //this.list.beginUpdate();
11963         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11964         this.list.alignTo(this.inputEl(), this.listAlign);
11965         this.list.alignTo(this.inputEl(), this.listAlign);
11966         //this.list.endUpdate();
11967     },
11968
11969     // private
11970     onEmptyResults : function(){
11971         
11972         if(this.tickable && this.editable){
11973             this.restrictHeight();
11974             return;
11975         }
11976         
11977         this.collapse();
11978     },
11979
11980     /**
11981      * Returns true if the dropdown list is expanded, else false.
11982      */
11983     isExpanded : function(){
11984         return this.list.isVisible();
11985     },
11986
11987     /**
11988      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11989      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11990      * @param {String} value The data value of the item to select
11991      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11992      * selected item if it is not currently in view (defaults to true)
11993      * @return {Boolean} True if the value matched an item in the list, else false
11994      */
11995     selectByValue : function(v, scrollIntoView){
11996         if(v !== undefined && v !== null){
11997             var r = this.findRecord(this.valueField || this.displayField, v);
11998             if(r){
11999                 this.select(this.store.indexOf(r), scrollIntoView);
12000                 return true;
12001             }
12002         }
12003         return false;
12004     },
12005
12006     /**
12007      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12008      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12009      * @param {Number} index The zero-based index of the list item to select
12010      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12011      * selected item if it is not currently in view (defaults to true)
12012      */
12013     select : function(index, scrollIntoView){
12014         this.selectedIndex = index;
12015         this.view.select(index);
12016         if(scrollIntoView !== false){
12017             var el = this.view.getNode(index);
12018             /*
12019              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12020              */
12021             if(el){
12022                 this.list.scrollChildIntoView(el, false);
12023             }
12024         }
12025     },
12026
12027     // private
12028     selectNext : function(){
12029         var ct = this.store.getCount();
12030         if(ct > 0){
12031             if(this.selectedIndex == -1){
12032                 this.select(0);
12033             }else if(this.selectedIndex < ct-1){
12034                 this.select(this.selectedIndex+1);
12035             }
12036         }
12037     },
12038
12039     // private
12040     selectPrev : function(){
12041         var ct = this.store.getCount();
12042         if(ct > 0){
12043             if(this.selectedIndex == -1){
12044                 this.select(0);
12045             }else if(this.selectedIndex != 0){
12046                 this.select(this.selectedIndex-1);
12047             }
12048         }
12049     },
12050
12051     // private
12052     onKeyUp : function(e){
12053         if(this.editable !== false && !e.isSpecialKey()){
12054             this.lastKey = e.getKey();
12055             this.dqTask.delay(this.queryDelay);
12056         }
12057     },
12058
12059     // private
12060     validateBlur : function(){
12061         return !this.list || !this.list.isVisible();   
12062     },
12063
12064     // private
12065     initQuery : function(){
12066         
12067         var v = this.getRawValue();
12068         
12069         if(this.tickable && this.editable){
12070             v = this.tickableInputEl().getValue();
12071         }
12072         
12073         this.doQuery(v);
12074     },
12075
12076     // private
12077     doForce : function(){
12078         if(this.inputEl().dom.value.length > 0){
12079             this.inputEl().dom.value =
12080                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12081              
12082         }
12083     },
12084
12085     /**
12086      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12087      * query allowing the query action to be canceled if needed.
12088      * @param {String} query The SQL query to execute
12089      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12090      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12091      * saved in the current store (defaults to false)
12092      */
12093     doQuery : function(q, forceAll){
12094         
12095         if(q === undefined || q === null){
12096             q = '';
12097         }
12098         var qe = {
12099             query: q,
12100             forceAll: forceAll,
12101             combo: this,
12102             cancel:false
12103         };
12104         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12105             return false;
12106         }
12107         q = qe.query;
12108         
12109         forceAll = qe.forceAll;
12110         if(forceAll === true || (q.length >= this.minChars)){
12111             
12112             this.hasQuery = true;
12113             
12114             if(this.lastQuery != q || this.alwaysQuery){
12115                 this.lastQuery = q;
12116                 if(this.mode == 'local'){
12117                     this.selectedIndex = -1;
12118                     if(forceAll){
12119                         this.store.clearFilter();
12120                     }else{
12121                         
12122                         if(this.specialFilter){
12123                             this.fireEvent('specialfilter', this);
12124                             this.onLoad();
12125                             return;
12126                         }
12127                         
12128                         this.store.filter(this.displayField, q);
12129                     }
12130                     
12131                     this.store.fireEvent("datachanged", this.store);
12132                     
12133                     this.onLoad();
12134                     
12135                     
12136                 }else{
12137                     
12138                     this.store.baseParams[this.queryParam] = q;
12139                     
12140                     var options = {params : this.getParams(q)};
12141                     
12142                     if(this.loadNext){
12143                         options.add = true;
12144                         options.params.start = this.page * this.pageSize;
12145                     }
12146                     
12147                     this.store.load(options);
12148                     
12149                     /*
12150                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12151                      *  we should expand the list on onLoad
12152                      *  so command out it
12153                      */
12154 //                    this.expand();
12155                 }
12156             }else{
12157                 this.selectedIndex = -1;
12158                 this.onLoad();   
12159             }
12160         }
12161         
12162         this.loadNext = false;
12163     },
12164     
12165     // private
12166     getParams : function(q){
12167         var p = {};
12168         //p[this.queryParam] = q;
12169         
12170         if(this.pageSize){
12171             p.start = 0;
12172             p.limit = this.pageSize;
12173         }
12174         return p;
12175     },
12176
12177     /**
12178      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12179      */
12180     collapse : function(){
12181         if(!this.isExpanded()){
12182             return;
12183         }
12184         
12185         this.list.hide();
12186         
12187         if(this.tickable){
12188             this.hasFocus = false;
12189             this.okBtn.hide();
12190             this.cancelBtn.hide();
12191             this.trigger.show();
12192             
12193             if(this.editable){
12194                 this.tickableInputEl().dom.value = '';
12195                 this.tickableInputEl().blur();
12196             }
12197             
12198         }
12199         
12200         Roo.get(document).un('mousedown', this.collapseIf, this);
12201         Roo.get(document).un('mousewheel', this.collapseIf, this);
12202         if (!this.editable) {
12203             Roo.get(document).un('keydown', this.listKeyPress, this);
12204         }
12205         this.fireEvent('collapse', this);
12206     },
12207
12208     // private
12209     collapseIf : function(e){
12210         var in_combo  = e.within(this.el);
12211         var in_list =  e.within(this.list);
12212         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12213         
12214         if (in_combo || in_list || is_list) {
12215             //e.stopPropagation();
12216             return;
12217         }
12218         
12219         if(this.tickable){
12220             this.onTickableFooterButtonClick(e, false, false);
12221         }
12222
12223         this.collapse();
12224         
12225     },
12226
12227     /**
12228      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12229      */
12230     expand : function(){
12231        
12232         if(this.isExpanded() || !this.hasFocus){
12233             return;
12234         }
12235         
12236         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12237         this.list.setWidth(lw);
12238         
12239         
12240          Roo.log('expand');
12241         
12242         this.list.show();
12243         
12244         this.restrictHeight();
12245         
12246         if(this.tickable){
12247             
12248             this.tickItems = Roo.apply([], this.item);
12249             
12250             this.okBtn.show();
12251             this.cancelBtn.show();
12252             this.trigger.hide();
12253             
12254             if(this.editable){
12255                 this.tickableInputEl().focus();
12256             }
12257             
12258         }
12259         
12260         Roo.get(document).on('mousedown', this.collapseIf, this);
12261         Roo.get(document).on('mousewheel', this.collapseIf, this);
12262         if (!this.editable) {
12263             Roo.get(document).on('keydown', this.listKeyPress, this);
12264         }
12265         
12266         this.fireEvent('expand', this);
12267     },
12268
12269     // private
12270     // Implements the default empty TriggerField.onTriggerClick function
12271     onTriggerClick : function(e)
12272     {
12273         Roo.log('trigger click');
12274         
12275         if(this.disabled || !this.triggerList){
12276             return;
12277         }
12278         
12279         this.page = 0;
12280         this.loadNext = false;
12281         
12282         if(this.isExpanded()){
12283             this.collapse();
12284             if (!this.blockFocus) {
12285                 this.inputEl().focus();
12286             }
12287             
12288         }else {
12289             this.hasFocus = true;
12290             if(this.triggerAction == 'all') {
12291                 this.doQuery(this.allQuery, true);
12292             } else {
12293                 this.doQuery(this.getRawValue());
12294             }
12295             if (!this.blockFocus) {
12296                 this.inputEl().focus();
12297             }
12298         }
12299     },
12300     
12301     onTickableTriggerClick : function(e)
12302     {
12303         if(this.disabled){
12304             return;
12305         }
12306         
12307         this.page = 0;
12308         this.loadNext = false;
12309         this.hasFocus = true;
12310         
12311         if(this.triggerAction == 'all') {
12312             this.doQuery(this.allQuery, true);
12313         } else {
12314             this.doQuery(this.getRawValue());
12315         }
12316     },
12317     
12318     onSearchFieldClick : function(e)
12319     {
12320         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12321             this.onTickableFooterButtonClick(e, false, false);
12322             return;
12323         }
12324         
12325         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12326             return;
12327         }
12328         
12329         this.page = 0;
12330         this.loadNext = false;
12331         this.hasFocus = true;
12332         
12333         if(this.triggerAction == 'all') {
12334             this.doQuery(this.allQuery, true);
12335         } else {
12336             this.doQuery(this.getRawValue());
12337         }
12338     },
12339     
12340     listKeyPress : function(e)
12341     {
12342         //Roo.log('listkeypress');
12343         // scroll to first matching element based on key pres..
12344         if (e.isSpecialKey()) {
12345             return false;
12346         }
12347         var k = String.fromCharCode(e.getKey()).toUpperCase();
12348         //Roo.log(k);
12349         var match  = false;
12350         var csel = this.view.getSelectedNodes();
12351         var cselitem = false;
12352         if (csel.length) {
12353             var ix = this.view.indexOf(csel[0]);
12354             cselitem  = this.store.getAt(ix);
12355             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12356                 cselitem = false;
12357             }
12358             
12359         }
12360         
12361         this.store.each(function(v) { 
12362             if (cselitem) {
12363                 // start at existing selection.
12364                 if (cselitem.id == v.id) {
12365                     cselitem = false;
12366                 }
12367                 return true;
12368             }
12369                 
12370             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12371                 match = this.store.indexOf(v);
12372                 return false;
12373             }
12374             return true;
12375         }, this);
12376         
12377         if (match === false) {
12378             return true; // no more action?
12379         }
12380         // scroll to?
12381         this.view.select(match);
12382         var sn = Roo.get(this.view.getSelectedNodes()[0])
12383         sn.scrollIntoView(sn.dom.parentNode, false);
12384     },
12385     
12386     onViewScroll : function(e, t){
12387         
12388         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){
12389             return;
12390         }
12391         
12392         this.hasQuery = true;
12393         
12394         this.loading = this.list.select('.loading', true).first();
12395         
12396         if(this.loading === null){
12397             this.list.createChild({
12398                 tag: 'div',
12399                 cls: 'loading select2-more-results select2-active',
12400                 html: 'Loading more results...'
12401             })
12402             
12403             this.loading = this.list.select('.loading', true).first();
12404             
12405             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12406             
12407             this.loading.hide();
12408         }
12409         
12410         this.loading.show();
12411         
12412         var _combo = this;
12413         
12414         this.page++;
12415         this.loadNext = true;
12416         
12417         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12418         
12419         return;
12420     },
12421     
12422     addItem : function(o)
12423     {   
12424         var dv = ''; // display value
12425         
12426         if (this.displayField) {
12427             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12428         } else {
12429             // this is an error condition!!!
12430             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12431         }
12432         
12433         if(!dv.length){
12434             return;
12435         }
12436         
12437         var choice = this.choices.createChild({
12438             tag: 'li',
12439             cls: 'select2-search-choice',
12440             cn: [
12441                 {
12442                     tag: 'div',
12443                     html: dv
12444                 },
12445                 {
12446                     tag: 'a',
12447                     href: '#',
12448                     cls: 'select2-search-choice-close',
12449                     tabindex: '-1'
12450                 }
12451             ]
12452             
12453         }, this.searchField);
12454         
12455         var close = choice.select('a.select2-search-choice-close', true).first()
12456         
12457         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12458         
12459         this.item.push(o);
12460         
12461         this.lastData = o;
12462         
12463         this.syncValue();
12464         
12465         this.inputEl().dom.value = '';
12466         
12467         this.validate();
12468     },
12469     
12470     onRemoveItem : function(e, _self, o)
12471     {
12472         e.preventDefault();
12473         
12474         this.lastItem = Roo.apply([], this.item);
12475         
12476         var index = this.item.indexOf(o.data) * 1;
12477         
12478         if( index < 0){
12479             Roo.log('not this item?!');
12480             return;
12481         }
12482         
12483         this.item.splice(index, 1);
12484         o.item.remove();
12485         
12486         this.syncValue();
12487         
12488         this.fireEvent('remove', this, e);
12489         
12490         this.validate();
12491         
12492     },
12493     
12494     syncValue : function()
12495     {
12496         if(!this.item.length){
12497             this.clearValue();
12498             return;
12499         }
12500             
12501         var value = [];
12502         var _this = this;
12503         Roo.each(this.item, function(i){
12504             if(_this.valueField){
12505                 value.push(i[_this.valueField]);
12506                 return;
12507             }
12508
12509             value.push(i);
12510         });
12511
12512         this.value = value.join(',');
12513
12514         if(this.hiddenField){
12515             this.hiddenField.dom.value = this.value;
12516         }
12517         
12518         this.store.fireEvent("datachanged", this.store);
12519     },
12520     
12521     clearItem : function()
12522     {
12523         if(!this.multiple){
12524             return;
12525         }
12526         
12527         this.item = [];
12528         
12529         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12530            c.remove();
12531         });
12532         
12533         this.syncValue();
12534         
12535         this.validate();
12536     },
12537     
12538     inputEl: function ()
12539     {
12540         if(this.tickable){
12541             return this.searchField;
12542         }
12543         return this.el.select('input.form-control',true).first();
12544     },
12545     
12546     
12547     onTickableFooterButtonClick : function(e, btn, el)
12548     {
12549         e.preventDefault();
12550         
12551         this.lastItem = Roo.apply([], this.item);
12552         
12553         if(btn && btn.name == 'cancel'){
12554             this.tickItems = Roo.apply([], this.item);
12555             this.collapse();
12556             return;
12557         }
12558         
12559         this.clearItem();
12560         
12561         var _this = this;
12562         
12563         Roo.each(this.tickItems, function(o){
12564             _this.addItem(o);
12565         });
12566         
12567         this.collapse();
12568         
12569     },
12570     
12571     validate : function()
12572     {
12573         var v = this.getRawValue();
12574         
12575         if(this.multiple){
12576             v = this.getValue();
12577         }
12578         
12579         if(this.disabled || this.allowBlank || v.length){
12580             this.markValid();
12581             return true;
12582         }
12583         
12584         this.markInvalid();
12585         return false;
12586     },
12587     
12588     tickableInputEl : function()
12589     {
12590         if(!this.tickable || !this.editable){
12591             return this.inputEl();
12592         }
12593         
12594         return this.inputEl().select('.select2-search-field-input', true).first();
12595     }
12596     
12597     
12598
12599     /** 
12600     * @cfg {Boolean} grow 
12601     * @hide 
12602     */
12603     /** 
12604     * @cfg {Number} growMin 
12605     * @hide 
12606     */
12607     /** 
12608     * @cfg {Number} growMax 
12609     * @hide 
12610     */
12611     /**
12612      * @hide
12613      * @method autoSize
12614      */
12615 });
12616 /*
12617  * Based on:
12618  * Ext JS Library 1.1.1
12619  * Copyright(c) 2006-2007, Ext JS, LLC.
12620  *
12621  * Originally Released Under LGPL - original licence link has changed is not relivant.
12622  *
12623  * Fork - LGPL
12624  * <script type="text/javascript">
12625  */
12626
12627 /**
12628  * @class Roo.View
12629  * @extends Roo.util.Observable
12630  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12631  * This class also supports single and multi selection modes. <br>
12632  * Create a data model bound view:
12633  <pre><code>
12634  var store = new Roo.data.Store(...);
12635
12636  var view = new Roo.View({
12637     el : "my-element",
12638     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12639  
12640     singleSelect: true,
12641     selectedClass: "ydataview-selected",
12642     store: store
12643  });
12644
12645  // listen for node click?
12646  view.on("click", function(vw, index, node, e){
12647  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12648  });
12649
12650  // load XML data
12651  dataModel.load("foobar.xml");
12652  </code></pre>
12653  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12654  * <br><br>
12655  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12656  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12657  * 
12658  * Note: old style constructor is still suported (container, template, config)
12659  * 
12660  * @constructor
12661  * Create a new View
12662  * @param {Object} config The config object
12663  * 
12664  */
12665 Roo.View = function(config, depreciated_tpl, depreciated_config){
12666     
12667     this.parent = false;
12668     
12669     if (typeof(depreciated_tpl) == 'undefined') {
12670         // new way.. - universal constructor.
12671         Roo.apply(this, config);
12672         this.el  = Roo.get(this.el);
12673     } else {
12674         // old format..
12675         this.el  = Roo.get(config);
12676         this.tpl = depreciated_tpl;
12677         Roo.apply(this, depreciated_config);
12678     }
12679     this.wrapEl  = this.el.wrap().wrap();
12680     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12681     
12682     
12683     if(typeof(this.tpl) == "string"){
12684         this.tpl = new Roo.Template(this.tpl);
12685     } else {
12686         // support xtype ctors..
12687         this.tpl = new Roo.factory(this.tpl, Roo);
12688     }
12689     
12690     
12691     this.tpl.compile();
12692     
12693     /** @private */
12694     this.addEvents({
12695         /**
12696          * @event beforeclick
12697          * Fires before a click is processed. Returns false to cancel the default action.
12698          * @param {Roo.View} this
12699          * @param {Number} index The index of the target node
12700          * @param {HTMLElement} node The target node
12701          * @param {Roo.EventObject} e The raw event object
12702          */
12703             "beforeclick" : true,
12704         /**
12705          * @event click
12706          * Fires when a template node is clicked.
12707          * @param {Roo.View} this
12708          * @param {Number} index The index of the target node
12709          * @param {HTMLElement} node The target node
12710          * @param {Roo.EventObject} e The raw event object
12711          */
12712             "click" : true,
12713         /**
12714          * @event dblclick
12715          * Fires when a template node is double clicked.
12716          * @param {Roo.View} this
12717          * @param {Number} index The index of the target node
12718          * @param {HTMLElement} node The target node
12719          * @param {Roo.EventObject} e The raw event object
12720          */
12721             "dblclick" : true,
12722         /**
12723          * @event contextmenu
12724          * Fires when a template node is right clicked.
12725          * @param {Roo.View} this
12726          * @param {Number} index The index of the target node
12727          * @param {HTMLElement} node The target node
12728          * @param {Roo.EventObject} e The raw event object
12729          */
12730             "contextmenu" : true,
12731         /**
12732          * @event selectionchange
12733          * Fires when the selected nodes change.
12734          * @param {Roo.View} this
12735          * @param {Array} selections Array of the selected nodes
12736          */
12737             "selectionchange" : true,
12738     
12739         /**
12740          * @event beforeselect
12741          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12742          * @param {Roo.View} this
12743          * @param {HTMLElement} node The node to be selected
12744          * @param {Array} selections Array of currently selected nodes
12745          */
12746             "beforeselect" : true,
12747         /**
12748          * @event preparedata
12749          * Fires on every row to render, to allow you to change the data.
12750          * @param {Roo.View} this
12751          * @param {Object} data to be rendered (change this)
12752          */
12753           "preparedata" : true
12754           
12755           
12756         });
12757
12758
12759
12760     this.el.on({
12761         "click": this.onClick,
12762         "dblclick": this.onDblClick,
12763         "contextmenu": this.onContextMenu,
12764         scope:this
12765     });
12766
12767     this.selections = [];
12768     this.nodes = [];
12769     this.cmp = new Roo.CompositeElementLite([]);
12770     if(this.store){
12771         this.store = Roo.factory(this.store, Roo.data);
12772         this.setStore(this.store, true);
12773     }
12774     
12775     if ( this.footer && this.footer.xtype) {
12776            
12777          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12778         
12779         this.footer.dataSource = this.store
12780         this.footer.container = fctr;
12781         this.footer = Roo.factory(this.footer, Roo);
12782         fctr.insertFirst(this.el);
12783         
12784         // this is a bit insane - as the paging toolbar seems to detach the el..
12785 //        dom.parentNode.parentNode.parentNode
12786          // they get detached?
12787     }
12788     
12789     
12790     Roo.View.superclass.constructor.call(this);
12791     
12792     
12793 };
12794
12795 Roo.extend(Roo.View, Roo.util.Observable, {
12796     
12797      /**
12798      * @cfg {Roo.data.Store} store Data store to load data from.
12799      */
12800     store : false,
12801     
12802     /**
12803      * @cfg {String|Roo.Element} el The container element.
12804      */
12805     el : '',
12806     
12807     /**
12808      * @cfg {String|Roo.Template} tpl The template used by this View 
12809      */
12810     tpl : false,
12811     /**
12812      * @cfg {String} dataName the named area of the template to use as the data area
12813      *                          Works with domtemplates roo-name="name"
12814      */
12815     dataName: false,
12816     /**
12817      * @cfg {String} selectedClass The css class to add to selected nodes
12818      */
12819     selectedClass : "x-view-selected",
12820      /**
12821      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12822      */
12823     emptyText : "",
12824     
12825     /**
12826      * @cfg {String} text to display on mask (default Loading)
12827      */
12828     mask : false,
12829     /**
12830      * @cfg {Boolean} multiSelect Allow multiple selection
12831      */
12832     multiSelect : false,
12833     /**
12834      * @cfg {Boolean} singleSelect Allow single selection
12835      */
12836     singleSelect:  false,
12837     
12838     /**
12839      * @cfg {Boolean} toggleSelect - selecting 
12840      */
12841     toggleSelect : false,
12842     
12843     /**
12844      * @cfg {Boolean} tickable - selecting 
12845      */
12846     tickable : false,
12847     
12848     /**
12849      * Returns the element this view is bound to.
12850      * @return {Roo.Element}
12851      */
12852     getEl : function(){
12853         return this.wrapEl;
12854     },
12855     
12856     
12857
12858     /**
12859      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12860      */
12861     refresh : function(){
12862         //Roo.log('refresh');
12863         var t = this.tpl;
12864         
12865         // if we are using something like 'domtemplate', then
12866         // the what gets used is:
12867         // t.applySubtemplate(NAME, data, wrapping data..)
12868         // the outer template then get' applied with
12869         //     the store 'extra data'
12870         // and the body get's added to the
12871         //      roo-name="data" node?
12872         //      <span class='roo-tpl-{name}'></span> ?????
12873         
12874         
12875         
12876         this.clearSelections();
12877         this.el.update("");
12878         var html = [];
12879         var records = this.store.getRange();
12880         if(records.length < 1) {
12881             
12882             // is this valid??  = should it render a template??
12883             
12884             this.el.update(this.emptyText);
12885             return;
12886         }
12887         var el = this.el;
12888         if (this.dataName) {
12889             this.el.update(t.apply(this.store.meta)); //????
12890             el = this.el.child('.roo-tpl-' + this.dataName);
12891         }
12892         
12893         for(var i = 0, len = records.length; i < len; i++){
12894             var data = this.prepareData(records[i].data, i, records[i]);
12895             this.fireEvent("preparedata", this, data, i, records[i]);
12896             
12897             var d = Roo.apply({}, data);
12898             
12899             if(this.tickable){
12900                 Roo.apply(d, {'roo-id' : Roo.id()});
12901                 
12902                 var _this = this;
12903             
12904                 Roo.each(this.parent.item, function(item){
12905                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12906                         return;
12907                     }
12908                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12909                 });
12910             }
12911             
12912             html[html.length] = Roo.util.Format.trim(
12913                 this.dataName ?
12914                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12915                     t.apply(d)
12916             );
12917         }
12918         
12919         
12920         
12921         el.update(html.join(""));
12922         this.nodes = el.dom.childNodes;
12923         this.updateIndexes(0);
12924     },
12925     
12926
12927     /**
12928      * Function to override to reformat the data that is sent to
12929      * the template for each node.
12930      * DEPRICATED - use the preparedata event handler.
12931      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12932      * a JSON object for an UpdateManager bound view).
12933      */
12934     prepareData : function(data, index, record)
12935     {
12936         this.fireEvent("preparedata", this, data, index, record);
12937         return data;
12938     },
12939
12940     onUpdate : function(ds, record){
12941         // Roo.log('on update');   
12942         this.clearSelections();
12943         var index = this.store.indexOf(record);
12944         var n = this.nodes[index];
12945         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12946         n.parentNode.removeChild(n);
12947         this.updateIndexes(index, index);
12948     },
12949
12950     
12951     
12952 // --------- FIXME     
12953     onAdd : function(ds, records, index)
12954     {
12955         //Roo.log(['on Add', ds, records, index] );        
12956         this.clearSelections();
12957         if(this.nodes.length == 0){
12958             this.refresh();
12959             return;
12960         }
12961         var n = this.nodes[index];
12962         for(var i = 0, len = records.length; i < len; i++){
12963             var d = this.prepareData(records[i].data, i, records[i]);
12964             if(n){
12965                 this.tpl.insertBefore(n, d);
12966             }else{
12967                 
12968                 this.tpl.append(this.el, d);
12969             }
12970         }
12971         this.updateIndexes(index);
12972     },
12973
12974     onRemove : function(ds, record, index){
12975        // Roo.log('onRemove');
12976         this.clearSelections();
12977         var el = this.dataName  ?
12978             this.el.child('.roo-tpl-' + this.dataName) :
12979             this.el; 
12980         
12981         el.dom.removeChild(this.nodes[index]);
12982         this.updateIndexes(index);
12983     },
12984
12985     /**
12986      * Refresh an individual node.
12987      * @param {Number} index
12988      */
12989     refreshNode : function(index){
12990         this.onUpdate(this.store, this.store.getAt(index));
12991     },
12992
12993     updateIndexes : function(startIndex, endIndex){
12994         var ns = this.nodes;
12995         startIndex = startIndex || 0;
12996         endIndex = endIndex || ns.length - 1;
12997         for(var i = startIndex; i <= endIndex; i++){
12998             ns[i].nodeIndex = i;
12999         }
13000     },
13001
13002     /**
13003      * Changes the data store this view uses and refresh the view.
13004      * @param {Store} store
13005      */
13006     setStore : function(store, initial){
13007         if(!initial && this.store){
13008             this.store.un("datachanged", this.refresh);
13009             this.store.un("add", this.onAdd);
13010             this.store.un("remove", this.onRemove);
13011             this.store.un("update", this.onUpdate);
13012             this.store.un("clear", this.refresh);
13013             this.store.un("beforeload", this.onBeforeLoad);
13014             this.store.un("load", this.onLoad);
13015             this.store.un("loadexception", this.onLoad);
13016         }
13017         if(store){
13018           
13019             store.on("datachanged", this.refresh, this);
13020             store.on("add", this.onAdd, this);
13021             store.on("remove", this.onRemove, this);
13022             store.on("update", this.onUpdate, this);
13023             store.on("clear", this.refresh, this);
13024             store.on("beforeload", this.onBeforeLoad, this);
13025             store.on("load", this.onLoad, this);
13026             store.on("loadexception", this.onLoad, this);
13027         }
13028         
13029         if(store){
13030             this.refresh();
13031         }
13032     },
13033     /**
13034      * onbeforeLoad - masks the loading area.
13035      *
13036      */
13037     onBeforeLoad : function(store,opts)
13038     {
13039          //Roo.log('onBeforeLoad');   
13040         if (!opts.add) {
13041             this.el.update("");
13042         }
13043         this.el.mask(this.mask ? this.mask : "Loading" ); 
13044     },
13045     onLoad : function ()
13046     {
13047         this.el.unmask();
13048     },
13049     
13050
13051     /**
13052      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
13053      * @param {HTMLElement} node
13054      * @return {HTMLElement} The template node
13055      */
13056     findItemFromChild : function(node){
13057         var el = this.dataName  ?
13058             this.el.child('.roo-tpl-' + this.dataName,true) :
13059             this.el.dom; 
13060         
13061         if(!node || node.parentNode == el){
13062                     return node;
13063             }
13064             var p = node.parentNode;
13065             while(p && p != el){
13066             if(p.parentNode == el){
13067                 return p;
13068             }
13069             p = p.parentNode;
13070         }
13071             return null;
13072     },
13073
13074     /** @ignore */
13075     onClick : function(e){
13076         var item = this.findItemFromChild(e.getTarget());
13077         if(item){
13078             var index = this.indexOf(item);
13079             if(this.onItemClick(item, index, e) !== false){
13080                 this.fireEvent("click", this, index, item, e);
13081             }
13082         }else{
13083             this.clearSelections();
13084         }
13085     },
13086
13087     /** @ignore */
13088     onContextMenu : function(e){
13089         var item = this.findItemFromChild(e.getTarget());
13090         if(item){
13091             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
13092         }
13093     },
13094
13095     /** @ignore */
13096     onDblClick : function(e){
13097         var item = this.findItemFromChild(e.getTarget());
13098         if(item){
13099             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
13100         }
13101     },
13102
13103     onItemClick : function(item, index, e)
13104     {
13105         if(this.fireEvent("beforeclick", this, index, item, e) === false){
13106             return false;
13107         }
13108         if (this.toggleSelect) {
13109             var m = this.isSelected(item) ? 'unselect' : 'select';
13110             //Roo.log(m);
13111             var _t = this;
13112             _t[m](item, true, false);
13113             return true;
13114         }
13115         if(this.multiSelect || this.singleSelect){
13116             if(this.multiSelect && e.shiftKey && this.lastSelection){
13117                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
13118             }else{
13119                 this.select(item, this.multiSelect && e.ctrlKey);
13120                 this.lastSelection = item;
13121             }
13122             
13123             if(!this.tickable){
13124                 e.preventDefault();
13125             }
13126             
13127         }
13128         return true;
13129     },
13130
13131     /**
13132      * Get the number of selected nodes.
13133      * @return {Number}
13134      */
13135     getSelectionCount : function(){
13136         return this.selections.length;
13137     },
13138
13139     /**
13140      * Get the currently selected nodes.
13141      * @return {Array} An array of HTMLElements
13142      */
13143     getSelectedNodes : function(){
13144         return this.selections;
13145     },
13146
13147     /**
13148      * Get the indexes of the selected nodes.
13149      * @return {Array}
13150      */
13151     getSelectedIndexes : function(){
13152         var indexes = [], s = this.selections;
13153         for(var i = 0, len = s.length; i < len; i++){
13154             indexes.push(s[i].nodeIndex);
13155         }
13156         return indexes;
13157     },
13158
13159     /**
13160      * Clear all selections
13161      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
13162      */
13163     clearSelections : function(suppressEvent){
13164         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
13165             this.cmp.elements = this.selections;
13166             this.cmp.removeClass(this.selectedClass);
13167             this.selections = [];
13168             if(!suppressEvent){
13169                 this.fireEvent("selectionchange", this, this.selections);
13170             }
13171         }
13172     },
13173
13174     /**
13175      * Returns true if the passed node is selected
13176      * @param {HTMLElement/Number} node The node or node index
13177      * @return {Boolean}
13178      */
13179     isSelected : function(node){
13180         var s = this.selections;
13181         if(s.length < 1){
13182             return false;
13183         }
13184         node = this.getNode(node);
13185         return s.indexOf(node) !== -1;
13186     },
13187
13188     /**
13189      * Selects nodes.
13190      * @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
13191      * @param {Boolean} keepExisting (optional) true to keep existing selections
13192      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13193      */
13194     select : function(nodeInfo, keepExisting, suppressEvent){
13195         if(nodeInfo instanceof Array){
13196             if(!keepExisting){
13197                 this.clearSelections(true);
13198             }
13199             for(var i = 0, len = nodeInfo.length; i < len; i++){
13200                 this.select(nodeInfo[i], true, true);
13201             }
13202             return;
13203         } 
13204         var node = this.getNode(nodeInfo);
13205         if(!node || this.isSelected(node)){
13206             return; // already selected.
13207         }
13208         if(!keepExisting){
13209             this.clearSelections(true);
13210         }
13211         
13212         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
13213             Roo.fly(node).addClass(this.selectedClass);
13214             this.selections.push(node);
13215             if(!suppressEvent){
13216                 this.fireEvent("selectionchange", this, this.selections);
13217             }
13218         }
13219         
13220         
13221     },
13222       /**
13223      * Unselects nodes.
13224      * @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
13225      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
13226      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
13227      */
13228     unselect : function(nodeInfo, keepExisting, suppressEvent)
13229     {
13230         if(nodeInfo instanceof Array){
13231             Roo.each(this.selections, function(s) {
13232                 this.unselect(s, nodeInfo);
13233             }, this);
13234             return;
13235         }
13236         var node = this.getNode(nodeInfo);
13237         if(!node || !this.isSelected(node)){
13238             //Roo.log("not selected");
13239             return; // not selected.
13240         }
13241         // fireevent???
13242         var ns = [];
13243         Roo.each(this.selections, function(s) {
13244             if (s == node ) {
13245                 Roo.fly(node).removeClass(this.selectedClass);
13246
13247                 return;
13248             }
13249             ns.push(s);
13250         },this);
13251         
13252         this.selections= ns;
13253         this.fireEvent("selectionchange", this, this.selections);
13254     },
13255
13256     /**
13257      * Gets a template node.
13258      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13259      * @return {HTMLElement} The node or null if it wasn't found
13260      */
13261     getNode : function(nodeInfo){
13262         if(typeof nodeInfo == "string"){
13263             return document.getElementById(nodeInfo);
13264         }else if(typeof nodeInfo == "number"){
13265             return this.nodes[nodeInfo];
13266         }
13267         return nodeInfo;
13268     },
13269
13270     /**
13271      * Gets a range template nodes.
13272      * @param {Number} startIndex
13273      * @param {Number} endIndex
13274      * @return {Array} An array of nodes
13275      */
13276     getNodes : function(start, end){
13277         var ns = this.nodes;
13278         start = start || 0;
13279         end = typeof end == "undefined" ? ns.length - 1 : end;
13280         var nodes = [];
13281         if(start <= end){
13282             for(var i = start; i <= end; i++){
13283                 nodes.push(ns[i]);
13284             }
13285         } else{
13286             for(var i = start; i >= end; i--){
13287                 nodes.push(ns[i]);
13288             }
13289         }
13290         return nodes;
13291     },
13292
13293     /**
13294      * Finds the index of the passed node
13295      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
13296      * @return {Number} The index of the node or -1
13297      */
13298     indexOf : function(node){
13299         node = this.getNode(node);
13300         if(typeof node.nodeIndex == "number"){
13301             return node.nodeIndex;
13302         }
13303         var ns = this.nodes;
13304         for(var i = 0, len = ns.length; i < len; i++){
13305             if(ns[i] == node){
13306                 return i;
13307             }
13308         }
13309         return -1;
13310     }
13311 });
13312 /*
13313  * - LGPL
13314  *
13315  * based on jquery fullcalendar
13316  * 
13317  */
13318
13319 Roo.bootstrap = Roo.bootstrap || {};
13320 /**
13321  * @class Roo.bootstrap.Calendar
13322  * @extends Roo.bootstrap.Component
13323  * Bootstrap Calendar class
13324  * @cfg {Boolean} loadMask (true|false) default false
13325  * @cfg {Object} header generate the user specific header of the calendar, default false
13326
13327  * @constructor
13328  * Create a new Container
13329  * @param {Object} config The config object
13330  */
13331
13332
13333
13334 Roo.bootstrap.Calendar = function(config){
13335     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
13336      this.addEvents({
13337         /**
13338              * @event select
13339              * Fires when a date is selected
13340              * @param {DatePicker} this
13341              * @param {Date} date The selected date
13342              */
13343         'select': true,
13344         /**
13345              * @event monthchange
13346              * Fires when the displayed month changes 
13347              * @param {DatePicker} this
13348              * @param {Date} date The selected month
13349              */
13350         'monthchange': true,
13351         /**
13352              * @event evententer
13353              * Fires when mouse over an event
13354              * @param {Calendar} this
13355              * @param {event} Event
13356              */
13357         'evententer': true,
13358         /**
13359              * @event eventleave
13360              * Fires when the mouse leaves an
13361              * @param {Calendar} this
13362              * @param {event}
13363              */
13364         'eventleave': true,
13365         /**
13366              * @event eventclick
13367              * Fires when the mouse click an
13368              * @param {Calendar} this
13369              * @param {event}
13370              */
13371         'eventclick': true
13372         
13373     });
13374
13375 };
13376
13377 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
13378     
13379      /**
13380      * @cfg {Number} startDay
13381      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
13382      */
13383     startDay : 0,
13384     
13385     loadMask : false,
13386     
13387     header : false,
13388       
13389     getAutoCreate : function(){
13390         
13391         
13392         var fc_button = function(name, corner, style, content ) {
13393             return Roo.apply({},{
13394                 tag : 'span',
13395                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
13396                          (corner.length ?
13397                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
13398                             ''
13399                         ),
13400                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
13401                 unselectable: 'on'
13402             });
13403         };
13404         
13405         var header = {};
13406         
13407         if(!this.header){
13408             header = {
13409                 tag : 'table',
13410                 cls : 'fc-header',
13411                 style : 'width:100%',
13412                 cn : [
13413                     {
13414                         tag: 'tr',
13415                         cn : [
13416                             {
13417                                 tag : 'td',
13418                                 cls : 'fc-header-left',
13419                                 cn : [
13420                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
13421                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
13422                                     { tag: 'span', cls: 'fc-header-space' },
13423                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
13424
13425
13426                                 ]
13427                             },
13428
13429                             {
13430                                 tag : 'td',
13431                                 cls : 'fc-header-center',
13432                                 cn : [
13433                                     {
13434                                         tag: 'span',
13435                                         cls: 'fc-header-title',
13436                                         cn : {
13437                                             tag: 'H2',
13438                                             html : 'month / year'
13439                                         }
13440                                     }
13441
13442                                 ]
13443                             },
13444                             {
13445                                 tag : 'td',
13446                                 cls : 'fc-header-right',
13447                                 cn : [
13448                               /*      fc_button('month', 'left', '', 'month' ),
13449                                     fc_button('week', '', '', 'week' ),
13450                                     fc_button('day', 'right', '', 'day' )
13451                                 */    
13452
13453                                 ]
13454                             }
13455
13456                         ]
13457                     }
13458                 ]
13459             };
13460         }
13461         
13462         header = this.header;
13463         
13464        
13465         var cal_heads = function() {
13466             var ret = [];
13467             // fixme - handle this.
13468             
13469             for (var i =0; i < Date.dayNames.length; i++) {
13470                 var d = Date.dayNames[i];
13471                 ret.push({
13472                     tag: 'th',
13473                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
13474                     html : d.substring(0,3)
13475                 });
13476                 
13477             }
13478             ret[0].cls += ' fc-first';
13479             ret[6].cls += ' fc-last';
13480             return ret;
13481         };
13482         var cal_cell = function(n) {
13483             return  {
13484                 tag: 'td',
13485                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
13486                 cn : [
13487                     {
13488                         cn : [
13489                             {
13490                                 cls: 'fc-day-number',
13491                                 html: 'D'
13492                             },
13493                             {
13494                                 cls: 'fc-day-content',
13495                              
13496                                 cn : [
13497                                      {
13498                                         style: 'position: relative;' // height: 17px;
13499                                     }
13500                                 ]
13501                             }
13502                             
13503                             
13504                         ]
13505                     }
13506                 ]
13507                 
13508             }
13509         };
13510         var cal_rows = function() {
13511             
13512             var ret = [];
13513             for (var r = 0; r < 6; r++) {
13514                 var row= {
13515                     tag : 'tr',
13516                     cls : 'fc-week',
13517                     cn : []
13518                 };
13519                 
13520                 for (var i =0; i < Date.dayNames.length; i++) {
13521                     var d = Date.dayNames[i];
13522                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13523
13524                 }
13525                 row.cn[0].cls+=' fc-first';
13526                 row.cn[0].cn[0].style = 'min-height:90px';
13527                 row.cn[6].cls+=' fc-last';
13528                 ret.push(row);
13529                 
13530             }
13531             ret[0].cls += ' fc-first';
13532             ret[4].cls += ' fc-prev-last';
13533             ret[5].cls += ' fc-last';
13534             return ret;
13535             
13536         };
13537         
13538         var cal_table = {
13539             tag: 'table',
13540             cls: 'fc-border-separate',
13541             style : 'width:100%',
13542             cellspacing  : 0,
13543             cn : [
13544                 { 
13545                     tag: 'thead',
13546                     cn : [
13547                         { 
13548                             tag: 'tr',
13549                             cls : 'fc-first fc-last',
13550                             cn : cal_heads()
13551                         }
13552                     ]
13553                 },
13554                 { 
13555                     tag: 'tbody',
13556                     cn : cal_rows()
13557                 }
13558                   
13559             ]
13560         };
13561          
13562          var cfg = {
13563             cls : 'fc fc-ltr',
13564             cn : [
13565                 header,
13566                 {
13567                     cls : 'fc-content',
13568                     style : "position: relative;",
13569                     cn : [
13570                         {
13571                             cls : 'fc-view fc-view-month fc-grid',
13572                             style : 'position: relative',
13573                             unselectable : 'on',
13574                             cn : [
13575                                 {
13576                                     cls : 'fc-event-container',
13577                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13578                                 },
13579                                 cal_table
13580                             ]
13581                         }
13582                     ]
13583     
13584                 }
13585            ] 
13586             
13587         };
13588         
13589          
13590         
13591         return cfg;
13592     },
13593     
13594     
13595     initEvents : function()
13596     {
13597         if(!this.store){
13598             throw "can not find store for calendar";
13599         }
13600         
13601         var mark = {
13602             tag: "div",
13603             cls:"x-dlg-mask",
13604             style: "text-align:center",
13605             cn: [
13606                 {
13607                     tag: "div",
13608                     style: "background-color:white;width:50%;margin:250 auto",
13609                     cn: [
13610                         {
13611                             tag: "img",
13612                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13613                         },
13614                         {
13615                             tag: "span",
13616                             html: "Loading"
13617                         }
13618                         
13619                     ]
13620                 }
13621             ]
13622         }
13623         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13624         
13625         var size = this.el.select('.fc-content', true).first().getSize();
13626         this.maskEl.setSize(size.width, size.height);
13627         this.maskEl.enableDisplayMode("block");
13628         if(!this.loadMask){
13629             this.maskEl.hide();
13630         }
13631         
13632         this.store = Roo.factory(this.store, Roo.data);
13633         this.store.on('load', this.onLoad, this);
13634         this.store.on('beforeload', this.onBeforeLoad, this);
13635         
13636         this.resize();
13637         
13638         this.cells = this.el.select('.fc-day',true);
13639         //Roo.log(this.cells);
13640         this.textNodes = this.el.query('.fc-day-number');
13641         this.cells.addClassOnOver('fc-state-hover');
13642         
13643         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13644         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13645         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13646         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13647         
13648         this.on('monthchange', this.onMonthChange, this);
13649         
13650         this.update(new Date().clearTime());
13651     },
13652     
13653     resize : function() {
13654         var sz  = this.el.getSize();
13655         
13656         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13657         this.el.select('.fc-day-content div',true).setHeight(34);
13658     },
13659     
13660     
13661     // private
13662     showPrevMonth : function(e){
13663         this.update(this.activeDate.add("mo", -1));
13664     },
13665     showToday : function(e){
13666         this.update(new Date().clearTime());
13667     },
13668     // private
13669     showNextMonth : function(e){
13670         this.update(this.activeDate.add("mo", 1));
13671     },
13672
13673     // private
13674     showPrevYear : function(){
13675         this.update(this.activeDate.add("y", -1));
13676     },
13677
13678     // private
13679     showNextYear : function(){
13680         this.update(this.activeDate.add("y", 1));
13681     },
13682
13683     
13684    // private
13685     update : function(date)
13686     {
13687         var vd = this.activeDate;
13688         this.activeDate = date;
13689 //        if(vd && this.el){
13690 //            var t = date.getTime();
13691 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13692 //                Roo.log('using add remove');
13693 //                
13694 //                this.fireEvent('monthchange', this, date);
13695 //                
13696 //                this.cells.removeClass("fc-state-highlight");
13697 //                this.cells.each(function(c){
13698 //                   if(c.dateValue == t){
13699 //                       c.addClass("fc-state-highlight");
13700 //                       setTimeout(function(){
13701 //                            try{c.dom.firstChild.focus();}catch(e){}
13702 //                       }, 50);
13703 //                       return false;
13704 //                   }
13705 //                   return true;
13706 //                });
13707 //                return;
13708 //            }
13709 //        }
13710         
13711         var days = date.getDaysInMonth();
13712         
13713         var firstOfMonth = date.getFirstDateOfMonth();
13714         var startingPos = firstOfMonth.getDay()-this.startDay;
13715         
13716         if(startingPos < this.startDay){
13717             startingPos += 7;
13718         }
13719         
13720         var pm = date.add(Date.MONTH, -1);
13721         var prevStart = pm.getDaysInMonth()-startingPos;
13722 //        
13723         this.cells = this.el.select('.fc-day',true);
13724         this.textNodes = this.el.query('.fc-day-number');
13725         this.cells.addClassOnOver('fc-state-hover');
13726         
13727         var cells = this.cells.elements;
13728         var textEls = this.textNodes;
13729         
13730         Roo.each(cells, function(cell){
13731             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13732         });
13733         
13734         days += startingPos;
13735
13736         // convert everything to numbers so it's fast
13737         var day = 86400000;
13738         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13739         //Roo.log(d);
13740         //Roo.log(pm);
13741         //Roo.log(prevStart);
13742         
13743         var today = new Date().clearTime().getTime();
13744         var sel = date.clearTime().getTime();
13745         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13746         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13747         var ddMatch = this.disabledDatesRE;
13748         var ddText = this.disabledDatesText;
13749         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13750         var ddaysText = this.disabledDaysText;
13751         var format = this.format;
13752         
13753         var setCellClass = function(cal, cell){
13754             cell.row = 0;
13755             cell.events = [];
13756             cell.more = [];
13757             //Roo.log('set Cell Class');
13758             cell.title = "";
13759             var t = d.getTime();
13760             
13761             //Roo.log(d);
13762             
13763             cell.dateValue = t;
13764             if(t == today){
13765                 cell.className += " fc-today";
13766                 cell.className += " fc-state-highlight";
13767                 cell.title = cal.todayText;
13768             }
13769             if(t == sel){
13770                 // disable highlight in other month..
13771                 //cell.className += " fc-state-highlight";
13772                 
13773             }
13774             // disabling
13775             if(t < min) {
13776                 cell.className = " fc-state-disabled";
13777                 cell.title = cal.minText;
13778                 return;
13779             }
13780             if(t > max) {
13781                 cell.className = " fc-state-disabled";
13782                 cell.title = cal.maxText;
13783                 return;
13784             }
13785             if(ddays){
13786                 if(ddays.indexOf(d.getDay()) != -1){
13787                     cell.title = ddaysText;
13788                     cell.className = " fc-state-disabled";
13789                 }
13790             }
13791             if(ddMatch && format){
13792                 var fvalue = d.dateFormat(format);
13793                 if(ddMatch.test(fvalue)){
13794                     cell.title = ddText.replace("%0", fvalue);
13795                     cell.className = " fc-state-disabled";
13796                 }
13797             }
13798             
13799             if (!cell.initialClassName) {
13800                 cell.initialClassName = cell.dom.className;
13801             }
13802             
13803             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13804         };
13805
13806         var i = 0;
13807         
13808         for(; i < startingPos; i++) {
13809             textEls[i].innerHTML = (++prevStart);
13810             d.setDate(d.getDate()+1);
13811             
13812             cells[i].className = "fc-past fc-other-month";
13813             setCellClass(this, cells[i]);
13814         }
13815         
13816         var intDay = 0;
13817         
13818         for(; i < days; i++){
13819             intDay = i - startingPos + 1;
13820             textEls[i].innerHTML = (intDay);
13821             d.setDate(d.getDate()+1);
13822             
13823             cells[i].className = ''; // "x-date-active";
13824             setCellClass(this, cells[i]);
13825         }
13826         var extraDays = 0;
13827         
13828         for(; i < 42; i++) {
13829             textEls[i].innerHTML = (++extraDays);
13830             d.setDate(d.getDate()+1);
13831             
13832             cells[i].className = "fc-future fc-other-month";
13833             setCellClass(this, cells[i]);
13834         }
13835         
13836         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13837         
13838         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13839         
13840         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13841         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13842         
13843         if(totalRows != 6){
13844             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13845             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13846         }
13847         
13848         this.fireEvent('monthchange', this, date);
13849         
13850         
13851         /*
13852         if(!this.internalRender){
13853             var main = this.el.dom.firstChild;
13854             var w = main.offsetWidth;
13855             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13856             Roo.fly(main).setWidth(w);
13857             this.internalRender = true;
13858             // opera does not respect the auto grow header center column
13859             // then, after it gets a width opera refuses to recalculate
13860             // without a second pass
13861             if(Roo.isOpera && !this.secondPass){
13862                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13863                 this.secondPass = true;
13864                 this.update.defer(10, this, [date]);
13865             }
13866         }
13867         */
13868         
13869     },
13870     
13871     findCell : function(dt) {
13872         dt = dt.clearTime().getTime();
13873         var ret = false;
13874         this.cells.each(function(c){
13875             //Roo.log("check " +c.dateValue + '?=' + dt);
13876             if(c.dateValue == dt){
13877                 ret = c;
13878                 return false;
13879             }
13880             return true;
13881         });
13882         
13883         return ret;
13884     },
13885     
13886     findCells : function(ev) {
13887         var s = ev.start.clone().clearTime().getTime();
13888        // Roo.log(s);
13889         var e= ev.end.clone().clearTime().getTime();
13890        // Roo.log(e);
13891         var ret = [];
13892         this.cells.each(function(c){
13893              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13894             
13895             if(c.dateValue > e){
13896                 return ;
13897             }
13898             if(c.dateValue < s){
13899                 return ;
13900             }
13901             ret.push(c);
13902         });
13903         
13904         return ret;    
13905     },
13906     
13907 //    findBestRow: function(cells)
13908 //    {
13909 //        var ret = 0;
13910 //        
13911 //        for (var i =0 ; i < cells.length;i++) {
13912 //            ret  = Math.max(cells[i].rows || 0,ret);
13913 //        }
13914 //        return ret;
13915 //        
13916 //    },
13917     
13918     
13919     addItem : function(ev)
13920     {
13921         // look for vertical location slot in
13922         var cells = this.findCells(ev);
13923         
13924 //        ev.row = this.findBestRow(cells);
13925         
13926         // work out the location.
13927         
13928         var crow = false;
13929         var rows = [];
13930         for(var i =0; i < cells.length; i++) {
13931             
13932             cells[i].row = cells[0].row;
13933             
13934             if(i == 0){
13935                 cells[i].row = cells[i].row + 1;
13936             }
13937             
13938             if (!crow) {
13939                 crow = {
13940                     start : cells[i],
13941                     end :  cells[i]
13942                 };
13943                 continue;
13944             }
13945             if (crow.start.getY() == cells[i].getY()) {
13946                 // on same row.
13947                 crow.end = cells[i];
13948                 continue;
13949             }
13950             // different row.
13951             rows.push(crow);
13952             crow = {
13953                 start: cells[i],
13954                 end : cells[i]
13955             };
13956             
13957         }
13958         
13959         rows.push(crow);
13960         ev.els = [];
13961         ev.rows = rows;
13962         ev.cells = cells;
13963         
13964         cells[0].events.push(ev);
13965         
13966         this.calevents.push(ev);
13967     },
13968     
13969     clearEvents: function() {
13970         
13971         if(!this.calevents){
13972             return;
13973         }
13974         
13975         Roo.each(this.cells.elements, function(c){
13976             c.row = 0;
13977             c.events = [];
13978             c.more = [];
13979         });
13980         
13981         Roo.each(this.calevents, function(e) {
13982             Roo.each(e.els, function(el) {
13983                 el.un('mouseenter' ,this.onEventEnter, this);
13984                 el.un('mouseleave' ,this.onEventLeave, this);
13985                 el.remove();
13986             },this);
13987         },this);
13988         
13989         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13990             e.remove();
13991         });
13992         
13993     },
13994     
13995     renderEvents: function()
13996     {   
13997         var _this = this;
13998         
13999         this.cells.each(function(c) {
14000             
14001             if(c.row < 5){
14002                 return;
14003             }
14004             
14005             var ev = c.events;
14006             
14007             var r = 4;
14008             if(c.row != c.events.length){
14009                 r = 4 - (4 - (c.row - c.events.length));
14010             }
14011             
14012             c.events = ev.slice(0, r);
14013             c.more = ev.slice(r);
14014             
14015             if(c.more.length && c.more.length == 1){
14016                 c.events.push(c.more.pop());
14017             }
14018             
14019             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
14020             
14021         });
14022             
14023         this.cells.each(function(c) {
14024             
14025             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
14026             
14027             
14028             for (var e = 0; e < c.events.length; e++){
14029                 var ev = c.events[e];
14030                 var rows = ev.rows;
14031                 
14032                 for(var i = 0; i < rows.length; i++) {
14033                 
14034                     // how many rows should it span..
14035
14036                     var  cfg = {
14037                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
14038                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
14039
14040                         unselectable : "on",
14041                         cn : [
14042                             {
14043                                 cls: 'fc-event-inner',
14044                                 cn : [
14045     //                                {
14046     //                                  tag:'span',
14047     //                                  cls: 'fc-event-time',
14048     //                                  html : cells.length > 1 ? '' : ev.time
14049     //                                },
14050                                     {
14051                                       tag:'span',
14052                                       cls: 'fc-event-title',
14053                                       html : String.format('{0}', ev.title)
14054                                     }
14055
14056
14057                                 ]
14058                             },
14059                             {
14060                                 cls: 'ui-resizable-handle ui-resizable-e',
14061                                 html : '&nbsp;&nbsp;&nbsp'
14062                             }
14063
14064                         ]
14065                     };
14066
14067                     if (i == 0) {
14068                         cfg.cls += ' fc-event-start';
14069                     }
14070                     if ((i+1) == rows.length) {
14071                         cfg.cls += ' fc-event-end';
14072                     }
14073
14074                     var ctr = _this.el.select('.fc-event-container',true).first();
14075                     var cg = ctr.createChild(cfg);
14076
14077                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
14078                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
14079
14080                     var r = (c.more.length) ? 1 : 0;
14081                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
14082                     cg.setWidth(ebox.right - sbox.x -2);
14083
14084                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
14085                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
14086                     cg.on('click', _this.onEventClick, _this, ev);
14087
14088                     ev.els.push(cg);
14089                     
14090                 }
14091                 
14092             }
14093             
14094             
14095             if(c.more.length){
14096                 var  cfg = {
14097                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
14098                     style : 'position: absolute',
14099                     unselectable : "on",
14100                     cn : [
14101                         {
14102                             cls: 'fc-event-inner',
14103                             cn : [
14104                                 {
14105                                   tag:'span',
14106                                   cls: 'fc-event-title',
14107                                   html : 'More'
14108                                 }
14109
14110
14111                             ]
14112                         },
14113                         {
14114                             cls: 'ui-resizable-handle ui-resizable-e',
14115                             html : '&nbsp;&nbsp;&nbsp'
14116                         }
14117
14118                     ]
14119                 };
14120
14121                 var ctr = _this.el.select('.fc-event-container',true).first();
14122                 var cg = ctr.createChild(cfg);
14123
14124                 var sbox = c.select('.fc-day-content',true).first().getBox();
14125                 var ebox = c.select('.fc-day-content',true).first().getBox();
14126                 //Roo.log(cg);
14127                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
14128                 cg.setWidth(ebox.right - sbox.x -2);
14129
14130                 cg.on('click', _this.onMoreEventClick, _this, c.more);
14131                 
14132             }
14133             
14134         });
14135         
14136         
14137         
14138     },
14139     
14140     onEventEnter: function (e, el,event,d) {
14141         this.fireEvent('evententer', this, el, event);
14142     },
14143     
14144     onEventLeave: function (e, el,event,d) {
14145         this.fireEvent('eventleave', this, el, event);
14146     },
14147     
14148     onEventClick: function (e, el,event,d) {
14149         this.fireEvent('eventclick', this, el, event);
14150     },
14151     
14152     onMonthChange: function () {
14153         this.store.load();
14154     },
14155     
14156     onMoreEventClick: function(e, el, more)
14157     {
14158         var _this = this;
14159         
14160         this.calpopover.placement = 'right';
14161         this.calpopover.setTitle('More');
14162         
14163         this.calpopover.setContent('');
14164         
14165         var ctr = this.calpopover.el.select('.popover-content', true).first();
14166         
14167         Roo.each(more, function(m){
14168             var cfg = {
14169                 cls : 'fc-event-hori fc-event-draggable',
14170                 html : m.title
14171             }
14172             var cg = ctr.createChild(cfg);
14173             
14174             cg.on('click', _this.onEventClick, _this, m);
14175         });
14176         
14177         this.calpopover.show(el);
14178         
14179         
14180     },
14181     
14182     onLoad: function () 
14183     {   
14184         this.calevents = [];
14185         var cal = this;
14186         
14187         if(this.store.getCount() > 0){
14188             this.store.data.each(function(d){
14189                cal.addItem({
14190                     id : d.data.id,
14191                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
14192                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
14193                     time : d.data.start_time,
14194                     title : d.data.title,
14195                     description : d.data.description,
14196                     venue : d.data.venue
14197                 });
14198             });
14199         }
14200         
14201         this.renderEvents();
14202         
14203         if(this.calevents.length && this.loadMask){
14204             this.maskEl.hide();
14205         }
14206     },
14207     
14208     onBeforeLoad: function()
14209     {
14210         this.clearEvents();
14211         if(this.loadMask){
14212             this.maskEl.show();
14213         }
14214     }
14215 });
14216
14217  
14218  /*
14219  * - LGPL
14220  *
14221  * element
14222  * 
14223  */
14224
14225 /**
14226  * @class Roo.bootstrap.Popover
14227  * @extends Roo.bootstrap.Component
14228  * Bootstrap Popover class
14229  * @cfg {String} html contents of the popover   (or false to use children..)
14230  * @cfg {String} title of popover (or false to hide)
14231  * @cfg {String} placement how it is placed
14232  * @cfg {String} trigger click || hover (or false to trigger manually)
14233  * @cfg {String} over what (parent or false to trigger manually.)
14234  * @cfg {Number} delay - delay before showing
14235  
14236  * @constructor
14237  * Create a new Popover
14238  * @param {Object} config The config object
14239  */
14240
14241 Roo.bootstrap.Popover = function(config){
14242     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
14243 };
14244
14245 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
14246     
14247     title: 'Fill in a title',
14248     html: false,
14249     
14250     placement : 'right',
14251     trigger : 'hover', // hover
14252     
14253     delay : 0,
14254     
14255     over: 'parent',
14256     
14257     can_build_overlaid : false,
14258     
14259     getChildContainer : function()
14260     {
14261         return this.el.select('.popover-content',true).first();
14262     },
14263     
14264     getAutoCreate : function(){
14265          Roo.log('make popover?');
14266         var cfg = {
14267            cls : 'popover roo-dynamic',
14268            style: 'display:block',
14269            cn : [
14270                 {
14271                     cls : 'arrow'
14272                 },
14273                 {
14274                     cls : 'popover-inner',
14275                     cn : [
14276                         {
14277                             tag: 'h3',
14278                             cls: 'popover-title',
14279                             html : this.title
14280                         },
14281                         {
14282                             cls : 'popover-content',
14283                             html : this.html
14284                         }
14285                     ]
14286                     
14287                 }
14288            ]
14289         };
14290         
14291         return cfg;
14292     },
14293     setTitle: function(str)
14294     {
14295         this.el.select('.popover-title',true).first().dom.innerHTML = str;
14296     },
14297     setContent: function(str)
14298     {
14299         this.el.select('.popover-content',true).first().dom.innerHTML = str;
14300     },
14301     // as it get's added to the bottom of the page.
14302     onRender : function(ct, position)
14303     {
14304         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
14305         if(!this.el){
14306             var cfg = Roo.apply({},  this.getAutoCreate());
14307             cfg.id = Roo.id();
14308             
14309             if (this.cls) {
14310                 cfg.cls += ' ' + this.cls;
14311             }
14312             if (this.style) {
14313                 cfg.style = this.style;
14314             }
14315             Roo.log("adding to ")
14316             this.el = Roo.get(document.body).createChild(cfg, position);
14317             Roo.log(this.el);
14318         }
14319         this.initEvents();
14320     },
14321     
14322     initEvents : function()
14323     {
14324         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
14325         this.el.enableDisplayMode('block');
14326         this.el.hide();
14327         if (this.over === false) {
14328             return; 
14329         }
14330         if (this.triggers === false) {
14331             return;
14332         }
14333         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14334         var triggers = this.trigger ? this.trigger.split(' ') : [];
14335         Roo.each(triggers, function(trigger) {
14336         
14337             if (trigger == 'click') {
14338                 on_el.on('click', this.toggle, this);
14339             } else if (trigger != 'manual') {
14340                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
14341                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
14342       
14343                 on_el.on(eventIn  ,this.enter, this);
14344                 on_el.on(eventOut, this.leave, this);
14345             }
14346         }, this);
14347         
14348     },
14349     
14350     
14351     // private
14352     timeout : null,
14353     hoverState : null,
14354     
14355     toggle : function () {
14356         this.hoverState == 'in' ? this.leave() : this.enter();
14357     },
14358     
14359     enter : function () {
14360        
14361     
14362         clearTimeout(this.timeout);
14363     
14364         this.hoverState = 'in';
14365     
14366         if (!this.delay || !this.delay.show) {
14367             this.show();
14368             return;
14369         }
14370         var _t = this;
14371         this.timeout = setTimeout(function () {
14372             if (_t.hoverState == 'in') {
14373                 _t.show();
14374             }
14375         }, this.delay.show)
14376     },
14377     leave : function() {
14378         clearTimeout(this.timeout);
14379     
14380         this.hoverState = 'out';
14381     
14382         if (!this.delay || !this.delay.hide) {
14383             this.hide();
14384             return;
14385         }
14386         var _t = this;
14387         this.timeout = setTimeout(function () {
14388             if (_t.hoverState == 'out') {
14389                 _t.hide();
14390             }
14391         }, this.delay.hide)
14392     },
14393     
14394     show : function (on_el)
14395     {
14396         if (!on_el) {
14397             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
14398         }
14399         // set content.
14400         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
14401         if (this.html !== false) {
14402             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
14403         }
14404         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
14405         if (!this.title.length) {
14406             this.el.select('.popover-title',true).hide();
14407         }
14408         
14409         var placement = typeof this.placement == 'function' ?
14410             this.placement.call(this, this.el, on_el) :
14411             this.placement;
14412             
14413         var autoToken = /\s?auto?\s?/i;
14414         var autoPlace = autoToken.test(placement);
14415         if (autoPlace) {
14416             placement = placement.replace(autoToken, '') || 'top';
14417         }
14418         
14419         //this.el.detach()
14420         //this.el.setXY([0,0]);
14421         this.el.show();
14422         this.el.dom.style.display='block';
14423         this.el.addClass(placement);
14424         
14425         //this.el.appendTo(on_el);
14426         
14427         var p = this.getPosition();
14428         var box = this.el.getBox();
14429         
14430         if (autoPlace) {
14431             // fixme..
14432         }
14433         var align = Roo.bootstrap.Popover.alignment[placement];
14434         this.el.alignTo(on_el, align[0],align[1]);
14435         //var arrow = this.el.select('.arrow',true).first();
14436         //arrow.set(align[2], 
14437         
14438         this.el.addClass('in');
14439         this.hoverState = null;
14440         
14441         if (this.el.hasClass('fade')) {
14442             // fade it?
14443         }
14444         
14445     },
14446     hide : function()
14447     {
14448         this.el.setXY([0,0]);
14449         this.el.removeClass('in');
14450         this.el.hide();
14451         
14452     }
14453     
14454 });
14455
14456 Roo.bootstrap.Popover.alignment = {
14457     'left' : ['r-l', [-10,0], 'right'],
14458     'right' : ['l-r', [10,0], 'left'],
14459     'bottom' : ['t-b', [0,10], 'top'],
14460     'top' : [ 'b-t', [0,-10], 'bottom']
14461 };
14462
14463  /*
14464  * - LGPL
14465  *
14466  * Progress
14467  * 
14468  */
14469
14470 /**
14471  * @class Roo.bootstrap.Progress
14472  * @extends Roo.bootstrap.Component
14473  * Bootstrap Progress class
14474  * @cfg {Boolean} striped striped of the progress bar
14475  * @cfg {Boolean} active animated of the progress bar
14476  * 
14477  * 
14478  * @constructor
14479  * Create a new Progress
14480  * @param {Object} config The config object
14481  */
14482
14483 Roo.bootstrap.Progress = function(config){
14484     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
14485 };
14486
14487 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
14488     
14489     striped : false,
14490     active: false,
14491     
14492     getAutoCreate : function(){
14493         var cfg = {
14494             tag: 'div',
14495             cls: 'progress'
14496         };
14497         
14498         
14499         if(this.striped){
14500             cfg.cls += ' progress-striped';
14501         }
14502       
14503         if(this.active){
14504             cfg.cls += ' active';
14505         }
14506         
14507         
14508         return cfg;
14509     }
14510    
14511 });
14512
14513  
14514
14515  /*
14516  * - LGPL
14517  *
14518  * ProgressBar
14519  * 
14520  */
14521
14522 /**
14523  * @class Roo.bootstrap.ProgressBar
14524  * @extends Roo.bootstrap.Component
14525  * Bootstrap ProgressBar class
14526  * @cfg {Number} aria_valuenow aria-value now
14527  * @cfg {Number} aria_valuemin aria-value min
14528  * @cfg {Number} aria_valuemax aria-value max
14529  * @cfg {String} label label for the progress bar
14530  * @cfg {String} panel (success | info | warning | danger )
14531  * @cfg {String} role role of the progress bar
14532  * @cfg {String} sr_only text
14533  * 
14534  * 
14535  * @constructor
14536  * Create a new ProgressBar
14537  * @param {Object} config The config object
14538  */
14539
14540 Roo.bootstrap.ProgressBar = function(config){
14541     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14542 };
14543
14544 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14545     
14546     aria_valuenow : 0,
14547     aria_valuemin : 0,
14548     aria_valuemax : 100,
14549     label : false,
14550     panel : false,
14551     role : false,
14552     sr_only: false,
14553     
14554     getAutoCreate : function()
14555     {
14556         
14557         var cfg = {
14558             tag: 'div',
14559             cls: 'progress-bar',
14560             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14561         };
14562         
14563         if(this.sr_only){
14564             cfg.cn = {
14565                 tag: 'span',
14566                 cls: 'sr-only',
14567                 html: this.sr_only
14568             }
14569         }
14570         
14571         if(this.role){
14572             cfg.role = this.role;
14573         }
14574         
14575         if(this.aria_valuenow){
14576             cfg['aria-valuenow'] = this.aria_valuenow;
14577         }
14578         
14579         if(this.aria_valuemin){
14580             cfg['aria-valuemin'] = this.aria_valuemin;
14581         }
14582         
14583         if(this.aria_valuemax){
14584             cfg['aria-valuemax'] = this.aria_valuemax;
14585         }
14586         
14587         if(this.label && !this.sr_only){
14588             cfg.html = this.label;
14589         }
14590         
14591         if(this.panel){
14592             cfg.cls += ' progress-bar-' + this.panel;
14593         }
14594         
14595         return cfg;
14596     },
14597     
14598     update : function(aria_valuenow)
14599     {
14600         this.aria_valuenow = aria_valuenow;
14601         
14602         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14603     }
14604    
14605 });
14606
14607  
14608
14609  /*
14610  * - LGPL
14611  *
14612  * column
14613  * 
14614  */
14615
14616 /**
14617  * @class Roo.bootstrap.TabGroup
14618  * @extends Roo.bootstrap.Column
14619  * Bootstrap Column class
14620  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14621  * @cfg {Boolean} carousel true to make the group behave like a carousel
14622  * @cfg {Number} bullets show the panel pointer.. default 0
14623  * @cfg {Boolena} autoslide (true|false) auto slide .. default false
14624  * @cfg {Number} timer auto slide timer .. default 0 millisecond
14625  * 
14626  * @constructor
14627  * Create a new TabGroup
14628  * @param {Object} config The config object
14629  */
14630
14631 Roo.bootstrap.TabGroup = function(config){
14632     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14633     if (!this.navId) {
14634         this.navId = Roo.id();
14635     }
14636     this.tabs = [];
14637     Roo.bootstrap.TabGroup.register(this);
14638     
14639 };
14640
14641 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14642     
14643     carousel : false,
14644     transition : false,
14645     bullets : 0,
14646     timer : 0,
14647     autoslide : false,
14648     slideFn : false,
14649     
14650     getAutoCreate : function()
14651     {
14652         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14653         
14654         cfg.cls += ' tab-content';
14655         
14656         Roo.log('get auto create...............');
14657         
14658         if (this.carousel) {
14659             cfg.cls += ' carousel slide';
14660             
14661             cfg.cn = [{
14662                cls : 'carousel-inner'
14663             }];
14664         
14665             if(this.bullets > 0){
14666                 
14667                 var bullets = {
14668                     cls : 'carousel-bullets',
14669                     cn : []
14670                 };
14671                 
14672                 for (var i = 0; i < this.bullets; i++){
14673                     bullets.cn.push({
14674                         cls : 'bullet bullet-' + i
14675                     });
14676                 }
14677                 
14678                 bullets.cn.push({
14679                     cls : 'clear'
14680                 });
14681                 
14682                 cfg.cn[0].cn = bullets;
14683             }
14684         }
14685         
14686         return cfg;
14687     },
14688     
14689     initEvents:  function()
14690     {
14691         Roo.log('-------- init events on tab group ---------');
14692         
14693         var _this = this;
14694         
14695         if(this.bullets > 0){
14696             
14697             for (var i = 0; i < this.bullets; i++){
14698                 var bullet = this.el.select('.bullet-' + i, true).first();
14699                 
14700                 if(!bullet){
14701                     continue;
14702                 }
14703                 
14704                 bullet.on('click', (function(e, el, o, ii, t){
14705                     
14706                     e.preventDefault();
14707                     
14708                     _this.showPanel(ii);
14709                     
14710                     if(_this.autoslide && _this.slideFn){
14711                         clearInterval(_this.slideFn);
14712                         _this.slideFn = window.setInterval(function() {
14713                             _this.showPanelNext();
14714                         }, _this.timer);
14715                     }
14716                     
14717                 }).createDelegate(this, [i, bullet], true));
14718             }
14719         }
14720         
14721         if(this.autoslide){
14722             this.slideFn = window.setInterval(function() {
14723                 _this.showPanelNext();
14724             }, this.timer);
14725         }
14726     },
14727     
14728     getChildContainer : function()
14729     {
14730         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14731     },
14732     
14733     /**
14734     * register a Navigation item
14735     * @param {Roo.bootstrap.NavItem} the navitem to add
14736     */
14737     register : function(item)
14738     {
14739         this.tabs.push( item);
14740         item.navId = this.navId; // not really needed..
14741     
14742     },
14743     
14744     getActivePanel : function()
14745     {
14746         var r = false;
14747         Roo.each(this.tabs, function(t) {
14748             if (t.active) {
14749                 r = t;
14750                 return false;
14751             }
14752             return null;
14753         });
14754         return r;
14755         
14756     },
14757     getPanelByName : function(n)
14758     {
14759         var r = false;
14760         Roo.each(this.tabs, function(t) {
14761             if (t.tabId == n) {
14762                 r = t;
14763                 return false;
14764             }
14765             return null;
14766         });
14767         return r;
14768     },
14769     indexOfPanel : function(p)
14770     {
14771         var r = false;
14772         Roo.each(this.tabs, function(t,i) {
14773             if (t.tabId == p.tabId) {
14774                 r = i;
14775                 return false;
14776             }
14777             return null;
14778         });
14779         return r;
14780     },
14781     /**
14782      * show a specific panel
14783      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14784      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14785      */
14786     showPanel : function (pan)
14787     {
14788         if(this.transition){
14789             Roo.log("waiting for the transitionend");
14790             return;
14791         }
14792         
14793         if (typeof(pan) == 'number') {
14794             pan = this.tabs[pan];
14795         }
14796         if (typeof(pan) == 'string') {
14797             pan = this.getPanelByName(pan);
14798         }
14799         if (pan.tabId == this.getActivePanel().tabId) {
14800             return true;
14801         }
14802         var cur = this.getActivePanel();
14803         
14804         if (false === cur.fireEvent('beforedeactivate')) {
14805             return false;
14806         }
14807         
14808         if(this.bullets > 0){
14809             this.setActiveBullet(this.indexOfPanel(pan));
14810         }
14811         
14812         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
14813             
14814             this.transition = true;
14815             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14816             var lr = dir == 'next' ? 'left' : 'right';
14817             pan.el.addClass(dir); // or prev
14818             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14819             cur.el.addClass(lr); // or right
14820             pan.el.addClass(lr);
14821             
14822             var _this = this;
14823             cur.el.on('transitionend', function() {
14824                 Roo.log("trans end?");
14825                 
14826                 pan.el.removeClass([lr,dir]);
14827                 pan.setActive(true);
14828                 
14829                 cur.el.removeClass([lr]);
14830                 cur.setActive(false);
14831                 
14832                 _this.transition = false;
14833                 
14834             }, this, { single:  true } );
14835             
14836             return true;
14837         }
14838         
14839         cur.setActive(false);
14840         pan.setActive(true);
14841         
14842         return true;
14843         
14844     },
14845     showPanelNext : function()
14846     {
14847         var i = this.indexOfPanel(this.getActivePanel());
14848         
14849         if (i >= this.tabs.length - 1 && !this.autoslide) {
14850             return;
14851         }
14852         
14853         if (i >= this.tabs.length - 1 && this.autoslide) {
14854             i = -1;
14855         }
14856         
14857         this.showPanel(this.tabs[i+1]);
14858     },
14859     
14860     showPanelPrev : function()
14861     {
14862         var i = this.indexOfPanel(this.getActivePanel());
14863         
14864         if (i  < 1 && !this.autoslide) {
14865             return;
14866         }
14867         
14868         if (i < 1 && this.autoslide) {
14869             i = this.tabs.length;
14870         }
14871         
14872         this.showPanel(this.tabs[i-1]);
14873     },
14874     
14875     setActiveBullet : function(i)
14876     {
14877         Roo.each(this.el.select('.bullet', true).elements, function(el){
14878             el.removeClass('selected');
14879         });
14880
14881         var bullet = this.el.select('.bullet-' + i, true).first();
14882         
14883         if(!bullet){
14884             return;
14885         }
14886         
14887         bullet.addClass('selected');
14888     }
14889     
14890     
14891   
14892 });
14893
14894  
14895
14896  
14897  
14898 Roo.apply(Roo.bootstrap.TabGroup, {
14899     
14900     groups: {},
14901      /**
14902     * register a Navigation Group
14903     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14904     */
14905     register : function(navgrp)
14906     {
14907         this.groups[navgrp.navId] = navgrp;
14908         
14909     },
14910     /**
14911     * fetch a Navigation Group based on the navigation ID
14912     * if one does not exist , it will get created.
14913     * @param {string} the navgroup to add
14914     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14915     */
14916     get: function(navId) {
14917         if (typeof(this.groups[navId]) == 'undefined') {
14918             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14919         }
14920         return this.groups[navId] ;
14921     }
14922     
14923     
14924     
14925 });
14926
14927  /*
14928  * - LGPL
14929  *
14930  * TabPanel
14931  * 
14932  */
14933
14934 /**
14935  * @class Roo.bootstrap.TabPanel
14936  * @extends Roo.bootstrap.Component
14937  * Bootstrap TabPanel class
14938  * @cfg {Boolean} active panel active
14939  * @cfg {String} html panel content
14940  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14941  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14942  * 
14943  * 
14944  * @constructor
14945  * Create a new TabPanel
14946  * @param {Object} config The config object
14947  */
14948
14949 Roo.bootstrap.TabPanel = function(config){
14950     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14951     this.addEvents({
14952         /**
14953              * @event changed
14954              * Fires when the active status changes
14955              * @param {Roo.bootstrap.TabPanel} this
14956              * @param {Boolean} state the new state
14957             
14958          */
14959         'changed': true,
14960         /**
14961              * @event beforedeactivate
14962              * Fires before a tab is de-activated - can be used to do validation on a form.
14963              * @param {Roo.bootstrap.TabPanel} this
14964              * @return {Boolean} false if there is an error
14965             
14966          */
14967         'beforedeactivate': true
14968      });
14969     
14970     this.tabId = this.tabId || Roo.id();
14971   
14972 };
14973
14974 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14975     
14976     active: false,
14977     html: false,
14978     tabId: false,
14979     navId : false,
14980     
14981     getAutoCreate : function(){
14982         var cfg = {
14983             tag: 'div',
14984             // item is needed for carousel - not sure if it has any effect otherwise
14985             cls: 'tab-pane item',
14986             html: this.html || ''
14987         };
14988         
14989         if(this.active){
14990             cfg.cls += ' active';
14991         }
14992         
14993         if(this.tabId){
14994             cfg.tabId = this.tabId;
14995         }
14996         
14997         
14998         return cfg;
14999     },
15000     
15001     initEvents:  function()
15002     {
15003         Roo.log('-------- init events on tab panel ---------');
15004         
15005         var p = this.parent();
15006         this.navId = this.navId || p.navId;
15007         
15008         if (typeof(this.navId) != 'undefined') {
15009             // not really needed.. but just in case.. parent should be a NavGroup.
15010             var tg = Roo.bootstrap.TabGroup.get(this.navId);
15011             Roo.log(['register', tg, this]);
15012             tg.register(this);
15013             
15014             var i = tg.tabs.length - 1;
15015             
15016             if(this.active && tg.bullets > 0 && i < tg.bullets){
15017                 tg.setActiveBullet(i);
15018             }
15019         }
15020         
15021     },
15022     
15023     
15024     onRender : function(ct, position)
15025     {
15026        // Roo.log("Call onRender: " + this.xtype);
15027         
15028         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
15029         
15030         
15031         
15032         
15033         
15034     },
15035     
15036     setActive: function(state)
15037     {
15038         Roo.log("panel - set active " + this.tabId + "=" + state);
15039         
15040         this.active = state;
15041         if (!state) {
15042             this.el.removeClass('active');
15043             
15044         } else  if (!this.el.hasClass('active')) {
15045             this.el.addClass('active');
15046         }
15047         
15048         this.fireEvent('changed', this, state);
15049     }
15050     
15051     
15052 });
15053  
15054
15055  
15056
15057  /*
15058  * - LGPL
15059  *
15060  * DateField
15061  * 
15062  */
15063
15064 /**
15065  * @class Roo.bootstrap.DateField
15066  * @extends Roo.bootstrap.Input
15067  * Bootstrap DateField class
15068  * @cfg {Number} weekStart default 0
15069  * @cfg {String} viewMode default empty, (months|years)
15070  * @cfg {String} minViewMode default empty, (months|years)
15071  * @cfg {Number} startDate default -Infinity
15072  * @cfg {Number} endDate default Infinity
15073  * @cfg {Boolean} todayHighlight default false
15074  * @cfg {Boolean} todayBtn default false
15075  * @cfg {Boolean} calendarWeeks default false
15076  * @cfg {Object} daysOfWeekDisabled default empty
15077  * @cfg {Boolean} singleMode default false (true | false)
15078  * 
15079  * @cfg {Boolean} keyboardNavigation default true
15080  * @cfg {String} language default en
15081  * 
15082  * @constructor
15083  * Create a new DateField
15084  * @param {Object} config The config object
15085  */
15086
15087 Roo.bootstrap.DateField = function(config){
15088     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
15089      this.addEvents({
15090             /**
15091              * @event show
15092              * Fires when this field show.
15093              * @param {Roo.bootstrap.DateField} this
15094              * @param {Mixed} date The date value
15095              */
15096             show : true,
15097             /**
15098              * @event show
15099              * Fires when this field hide.
15100              * @param {Roo.bootstrap.DateField} this
15101              * @param {Mixed} date The date value
15102              */
15103             hide : true,
15104             /**
15105              * @event select
15106              * Fires when select a date.
15107              * @param {Roo.bootstrap.DateField} this
15108              * @param {Mixed} date The date value
15109              */
15110             select : true
15111         });
15112 };
15113
15114 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
15115     
15116     /**
15117      * @cfg {String} format
15118      * The default date format string which can be overriden for localization support.  The format must be
15119      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
15120      */
15121     format : "m/d/y",
15122     /**
15123      * @cfg {String} altFormats
15124      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
15125      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
15126      */
15127     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
15128     
15129     weekStart : 0,
15130     
15131     viewMode : '',
15132     
15133     minViewMode : '',
15134     
15135     todayHighlight : false,
15136     
15137     todayBtn: false,
15138     
15139     language: 'en',
15140     
15141     keyboardNavigation: true,
15142     
15143     calendarWeeks: false,
15144     
15145     startDate: -Infinity,
15146     
15147     endDate: Infinity,
15148     
15149     daysOfWeekDisabled: [],
15150     
15151     _events: [],
15152     
15153     singleMode : false,
15154     
15155     UTCDate: function()
15156     {
15157         return new Date(Date.UTC.apply(Date, arguments));
15158     },
15159     
15160     UTCToday: function()
15161     {
15162         var today = new Date();
15163         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
15164     },
15165     
15166     getDate: function() {
15167             var d = this.getUTCDate();
15168             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
15169     },
15170     
15171     getUTCDate: function() {
15172             return this.date;
15173     },
15174     
15175     setDate: function(d) {
15176             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
15177     },
15178     
15179     setUTCDate: function(d) {
15180             this.date = d;
15181             this.setValue(this.formatDate(this.date));
15182     },
15183         
15184     onRender: function(ct, position)
15185     {
15186         
15187         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
15188         
15189         this.language = this.language || 'en';
15190         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
15191         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
15192         
15193         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
15194         this.format = this.format || 'm/d/y';
15195         this.isInline = false;
15196         this.isInput = true;
15197         this.component = this.el.select('.add-on', true).first() || false;
15198         this.component = (this.component && this.component.length === 0) ? false : this.component;
15199         this.hasInput = this.component && this.inputEL().length;
15200         
15201         if (typeof(this.minViewMode === 'string')) {
15202             switch (this.minViewMode) {
15203                 case 'months':
15204                     this.minViewMode = 1;
15205                     break;
15206                 case 'years':
15207                     this.minViewMode = 2;
15208                     break;
15209                 default:
15210                     this.minViewMode = 0;
15211                     break;
15212             }
15213         }
15214         
15215         if (typeof(this.viewMode === 'string')) {
15216             switch (this.viewMode) {
15217                 case 'months':
15218                     this.viewMode = 1;
15219                     break;
15220                 case 'years':
15221                     this.viewMode = 2;
15222                     break;
15223                 default:
15224                     this.viewMode = 0;
15225                     break;
15226             }
15227         }
15228                 
15229         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
15230         
15231 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
15232         
15233         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15234         
15235         this.picker().on('mousedown', this.onMousedown, this);
15236         this.picker().on('click', this.onClick, this);
15237         
15238         this.picker().addClass('datepicker-dropdown');
15239         
15240         this.startViewMode = this.viewMode;
15241         
15242         if(this.singleMode){
15243             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
15244                 v.setVisibilityMode(Roo.Element.DISPLAY)
15245                 v.hide();
15246             });
15247             
15248             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
15249                 v.setStyle('width', '189px');
15250             });
15251         }
15252         
15253         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
15254             if(!this.calendarWeeks){
15255                 v.remove();
15256                 return;
15257             }
15258             
15259             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15260             v.attr('colspan', function(i, val){
15261                 return parseInt(val) + 1;
15262             });
15263         })
15264                         
15265         
15266         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
15267         
15268         this.setStartDate(this.startDate);
15269         this.setEndDate(this.endDate);
15270         
15271         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
15272         
15273         this.fillDow();
15274         this.fillMonths();
15275         this.update();
15276         this.showMode();
15277         
15278         if(this.isInline) {
15279             this.show();
15280         }
15281     },
15282     
15283     picker : function()
15284     {
15285         return this.pickerEl;
15286 //        return this.el.select('.datepicker', true).first();
15287     },
15288     
15289     fillDow: function()
15290     {
15291         var dowCnt = this.weekStart;
15292         
15293         var dow = {
15294             tag: 'tr',
15295             cn: [
15296                 
15297             ]
15298         };
15299         
15300         if(this.calendarWeeks){
15301             dow.cn.push({
15302                 tag: 'th',
15303                 cls: 'cw',
15304                 html: '&nbsp;'
15305             })
15306         }
15307         
15308         while (dowCnt < this.weekStart + 7) {
15309             dow.cn.push({
15310                 tag: 'th',
15311                 cls: 'dow',
15312                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
15313             });
15314         }
15315         
15316         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
15317     },
15318     
15319     fillMonths: function()
15320     {    
15321         var i = 0;
15322         var months = this.picker().select('>.datepicker-months td', true).first();
15323         
15324         months.dom.innerHTML = '';
15325         
15326         while (i < 12) {
15327             var month = {
15328                 tag: 'span',
15329                 cls: 'month',
15330                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
15331             }
15332             
15333             months.createChild(month);
15334         }
15335         
15336     },
15337     
15338     update: function()
15339     {
15340         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;
15341         
15342         if (this.date < this.startDate) {
15343             this.viewDate = new Date(this.startDate);
15344         } else if (this.date > this.endDate) {
15345             this.viewDate = new Date(this.endDate);
15346         } else {
15347             this.viewDate = new Date(this.date);
15348         }
15349         
15350         this.fill();
15351     },
15352     
15353     fill: function() 
15354     {
15355         var d = new Date(this.viewDate),
15356                 year = d.getUTCFullYear(),
15357                 month = d.getUTCMonth(),
15358                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
15359                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
15360                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
15361                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
15362                 currentDate = this.date && this.date.valueOf(),
15363                 today = this.UTCToday();
15364         
15365         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
15366         
15367 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
15368         
15369 //        this.picker.select('>tfoot th.today').
15370 //                                              .text(dates[this.language].today)
15371 //                                              .toggle(this.todayBtn !== false);
15372     
15373         this.updateNavArrows();
15374         this.fillMonths();
15375                                                 
15376         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
15377         
15378         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
15379          
15380         prevMonth.setUTCDate(day);
15381         
15382         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
15383         
15384         var nextMonth = new Date(prevMonth);
15385         
15386         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
15387         
15388         nextMonth = nextMonth.valueOf();
15389         
15390         var fillMonths = false;
15391         
15392         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
15393         
15394         while(prevMonth.valueOf() < nextMonth) {
15395             var clsName = '';
15396             
15397             if (prevMonth.getUTCDay() === this.weekStart) {
15398                 if(fillMonths){
15399                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
15400                 }
15401                     
15402                 fillMonths = {
15403                     tag: 'tr',
15404                     cn: []
15405                 };
15406                 
15407                 if(this.calendarWeeks){
15408                     // ISO 8601: First week contains first thursday.
15409                     // ISO also states week starts on Monday, but we can be more abstract here.
15410                     var
15411                     // Start of current week: based on weekstart/current date
15412                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
15413                     // Thursday of this week
15414                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
15415                     // First Thursday of year, year from thursday
15416                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
15417                     // Calendar week: ms between thursdays, div ms per day, div 7 days
15418                     calWeek =  (th - yth) / 864e5 / 7 + 1;
15419                     
15420                     fillMonths.cn.push({
15421                         tag: 'td',
15422                         cls: 'cw',
15423                         html: calWeek
15424                     });
15425                 }
15426             }
15427             
15428             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
15429                 clsName += ' old';
15430             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
15431                 clsName += ' new';
15432             }
15433             if (this.todayHighlight &&
15434                 prevMonth.getUTCFullYear() == today.getFullYear() &&
15435                 prevMonth.getUTCMonth() == today.getMonth() &&
15436                 prevMonth.getUTCDate() == today.getDate()) {
15437                 clsName += ' today';
15438             }
15439             
15440             if (currentDate && prevMonth.valueOf() === currentDate) {
15441                 clsName += ' active';
15442             }
15443             
15444             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
15445                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
15446                     clsName += ' disabled';
15447             }
15448             
15449             fillMonths.cn.push({
15450                 tag: 'td',
15451                 cls: 'day ' + clsName,
15452                 html: prevMonth.getDate()
15453             })
15454             
15455             prevMonth.setDate(prevMonth.getDate()+1);
15456         }
15457           
15458         var currentYear = this.date && this.date.getUTCFullYear();
15459         var currentMonth = this.date && this.date.getUTCMonth();
15460         
15461         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
15462         
15463         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
15464             v.removeClass('active');
15465             
15466             if(currentYear === year && k === currentMonth){
15467                 v.addClass('active');
15468             }
15469             
15470             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
15471                 v.addClass('disabled');
15472             }
15473             
15474         });
15475         
15476         
15477         year = parseInt(year/10, 10) * 10;
15478         
15479         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
15480         
15481         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
15482         
15483         year -= 1;
15484         for (var i = -1; i < 11; i++) {
15485             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
15486                 tag: 'span',
15487                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
15488                 html: year
15489             })
15490             
15491             year += 1;
15492         }
15493     },
15494     
15495     showMode: function(dir) 
15496     {
15497         if (dir) {
15498             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
15499         }
15500         
15501         Roo.each(this.picker().select('>div',true).elements, function(v){
15502             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15503             v.hide();
15504         });
15505         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
15506     },
15507     
15508     place: function()
15509     {
15510         if(this.isInline) return;
15511         
15512         this.picker().removeClass(['bottom', 'top']);
15513         
15514         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
15515             /*
15516              * place to the top of element!
15517              *
15518              */
15519             
15520             this.picker().addClass('top');
15521             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
15522             
15523             return;
15524         }
15525         
15526         this.picker().addClass('bottom');
15527         
15528         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
15529     },
15530     
15531     parseDate : function(value)
15532     {
15533         if(!value || value instanceof Date){
15534             return value;
15535         }
15536         var v = Date.parseDate(value, this.format);
15537         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
15538             v = Date.parseDate(value, 'Y-m-d');
15539         }
15540         if(!v && this.altFormats){
15541             if(!this.altFormatsArray){
15542                 this.altFormatsArray = this.altFormats.split("|");
15543             }
15544             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
15545                 v = Date.parseDate(value, this.altFormatsArray[i]);
15546             }
15547         }
15548         return v;
15549     },
15550     
15551     formatDate : function(date, fmt)
15552     {   
15553         return (!date || !(date instanceof Date)) ?
15554         date : date.dateFormat(fmt || this.format);
15555     },
15556     
15557     onFocus : function()
15558     {
15559         Roo.bootstrap.DateField.superclass.onFocus.call(this);
15560         this.show();
15561     },
15562     
15563     onBlur : function()
15564     {
15565         Roo.bootstrap.DateField.superclass.onBlur.call(this);
15566         
15567         var d = this.inputEl().getValue();
15568         
15569         this.setValue(d);
15570                 
15571         this.hide();
15572     },
15573     
15574     show : function()
15575     {
15576         this.picker().show();
15577         this.update();
15578         this.place();
15579         
15580         this.fireEvent('show', this, this.date);
15581     },
15582     
15583     hide : function()
15584     {
15585         if(this.isInline) return;
15586         this.picker().hide();
15587         this.viewMode = this.startViewMode;
15588         this.showMode();
15589         
15590         this.fireEvent('hide', this, this.date);
15591         
15592     },
15593     
15594     onMousedown: function(e)
15595     {
15596         e.stopPropagation();
15597         e.preventDefault();
15598     },
15599     
15600     keyup: function(e)
15601     {
15602         Roo.bootstrap.DateField.superclass.keyup.call(this);
15603         this.update();
15604     },
15605
15606     setValue: function(v)
15607     {
15608         
15609         // v can be a string or a date..
15610         
15611         
15612         var d = new Date(this.parseDate(v) ).clearTime();
15613         
15614         if(isNaN(d.getTime())){
15615             this.date = this.viewDate = '';
15616             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
15617             return;
15618         }
15619         
15620         v = this.formatDate(d);
15621         
15622         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
15623         
15624         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
15625      
15626         this.update();
15627
15628         this.fireEvent('select', this, this.date);
15629         
15630     },
15631     
15632     getValue: function()
15633     {
15634         return this.formatDate(this.date);
15635     },
15636     
15637     fireKey: function(e)
15638     {
15639         if (!this.picker().isVisible()){
15640             if (e.keyCode == 27) // allow escape to hide and re-show picker
15641                 this.show();
15642             return;
15643         }
15644         
15645         var dateChanged = false,
15646         dir, day, month,
15647         newDate, newViewDate;
15648         
15649         switch(e.keyCode){
15650             case 27: // escape
15651                 this.hide();
15652                 e.preventDefault();
15653                 break;
15654             case 37: // left
15655             case 39: // right
15656                 if (!this.keyboardNavigation) break;
15657                 dir = e.keyCode == 37 ? -1 : 1;
15658                 
15659                 if (e.ctrlKey){
15660                     newDate = this.moveYear(this.date, dir);
15661                     newViewDate = this.moveYear(this.viewDate, dir);
15662                 } else if (e.shiftKey){
15663                     newDate = this.moveMonth(this.date, dir);
15664                     newViewDate = this.moveMonth(this.viewDate, dir);
15665                 } else {
15666                     newDate = new Date(this.date);
15667                     newDate.setUTCDate(this.date.getUTCDate() + dir);
15668                     newViewDate = new Date(this.viewDate);
15669                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15670                 }
15671                 if (this.dateWithinRange(newDate)){
15672                     this.date = newDate;
15673                     this.viewDate = newViewDate;
15674                     this.setValue(this.formatDate(this.date));
15675 //                    this.update();
15676                     e.preventDefault();
15677                     dateChanged = true;
15678                 }
15679                 break;
15680             case 38: // up
15681             case 40: // down
15682                 if (!this.keyboardNavigation) break;
15683                 dir = e.keyCode == 38 ? -1 : 1;
15684                 if (e.ctrlKey){
15685                     newDate = this.moveYear(this.date, dir);
15686                     newViewDate = this.moveYear(this.viewDate, dir);
15687                 } else if (e.shiftKey){
15688                     newDate = this.moveMonth(this.date, dir);
15689                     newViewDate = this.moveMonth(this.viewDate, dir);
15690                 } else {
15691                     newDate = new Date(this.date);
15692                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15693                     newViewDate = new Date(this.viewDate);
15694                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15695                 }
15696                 if (this.dateWithinRange(newDate)){
15697                     this.date = newDate;
15698                     this.viewDate = newViewDate;
15699                     this.setValue(this.formatDate(this.date));
15700 //                    this.update();
15701                     e.preventDefault();
15702                     dateChanged = true;
15703                 }
15704                 break;
15705             case 13: // enter
15706                 this.setValue(this.formatDate(this.date));
15707                 this.hide();
15708                 e.preventDefault();
15709                 break;
15710             case 9: // tab
15711                 this.setValue(this.formatDate(this.date));
15712                 this.hide();
15713                 break;
15714             case 16: // shift
15715             case 17: // ctrl
15716             case 18: // alt
15717                 break;
15718             default :
15719                 this.hide();
15720                 
15721         }
15722     },
15723     
15724     
15725     onClick: function(e) 
15726     {
15727         e.stopPropagation();
15728         e.preventDefault();
15729         
15730         var target = e.getTarget();
15731         
15732         if(target.nodeName.toLowerCase() === 'i'){
15733             target = Roo.get(target).dom.parentNode;
15734         }
15735         
15736         var nodeName = target.nodeName;
15737         var className = target.className;
15738         var html = target.innerHTML;
15739         //Roo.log(nodeName);
15740         
15741         switch(nodeName.toLowerCase()) {
15742             case 'th':
15743                 switch(className) {
15744                     case 'switch':
15745                         this.showMode(1);
15746                         break;
15747                     case 'prev':
15748                     case 'next':
15749                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15750                         switch(this.viewMode){
15751                                 case 0:
15752                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15753                                         break;
15754                                 case 1:
15755                                 case 2:
15756                                         this.viewDate = this.moveYear(this.viewDate, dir);
15757                                         break;
15758                         }
15759                         this.fill();
15760                         break;
15761                     case 'today':
15762                         var date = new Date();
15763                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15764 //                        this.fill()
15765                         this.setValue(this.formatDate(this.date));
15766                         
15767                         this.hide();
15768                         break;
15769                 }
15770                 break;
15771             case 'span':
15772                 if (className.indexOf('disabled') < 0) {
15773                     this.viewDate.setUTCDate(1);
15774                     if (className.indexOf('month') > -1) {
15775                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15776                     } else {
15777                         var year = parseInt(html, 10) || 0;
15778                         this.viewDate.setUTCFullYear(year);
15779                         
15780                     }
15781                     
15782                     if(this.singleMode){
15783                         this.setValue(this.formatDate(this.viewDate));
15784                         this.hide();
15785                         return;
15786                     }
15787                     
15788                     this.showMode(-1);
15789                     this.fill();
15790                 }
15791                 break;
15792                 
15793             case 'td':
15794                 //Roo.log(className);
15795                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15796                     var day = parseInt(html, 10) || 1;
15797                     var year = this.viewDate.getUTCFullYear(),
15798                         month = this.viewDate.getUTCMonth();
15799
15800                     if (className.indexOf('old') > -1) {
15801                         if(month === 0 ){
15802                             month = 11;
15803                             year -= 1;
15804                         }else{
15805                             month -= 1;
15806                         }
15807                     } else if (className.indexOf('new') > -1) {
15808                         if (month == 11) {
15809                             month = 0;
15810                             year += 1;
15811                         } else {
15812                             month += 1;
15813                         }
15814                     }
15815                     //Roo.log([year,month,day]);
15816                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15817                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15818 //                    this.fill();
15819                     //Roo.log(this.formatDate(this.date));
15820                     this.setValue(this.formatDate(this.date));
15821                     this.hide();
15822                 }
15823                 break;
15824         }
15825     },
15826     
15827     setStartDate: function(startDate)
15828     {
15829         this.startDate = startDate || -Infinity;
15830         if (this.startDate !== -Infinity) {
15831             this.startDate = this.parseDate(this.startDate);
15832         }
15833         this.update();
15834         this.updateNavArrows();
15835     },
15836
15837     setEndDate: function(endDate)
15838     {
15839         this.endDate = endDate || Infinity;
15840         if (this.endDate !== Infinity) {
15841             this.endDate = this.parseDate(this.endDate);
15842         }
15843         this.update();
15844         this.updateNavArrows();
15845     },
15846     
15847     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15848     {
15849         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15850         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15851             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15852         }
15853         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15854             return parseInt(d, 10);
15855         });
15856         this.update();
15857         this.updateNavArrows();
15858     },
15859     
15860     updateNavArrows: function() 
15861     {
15862         if(this.singleMode){
15863             return;
15864         }
15865         
15866         var d = new Date(this.viewDate),
15867         year = d.getUTCFullYear(),
15868         month = d.getUTCMonth();
15869         
15870         Roo.each(this.picker().select('.prev', true).elements, function(v){
15871             v.show();
15872             switch (this.viewMode) {
15873                 case 0:
15874
15875                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15876                         v.hide();
15877                     }
15878                     break;
15879                 case 1:
15880                 case 2:
15881                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15882                         v.hide();
15883                     }
15884                     break;
15885             }
15886         });
15887         
15888         Roo.each(this.picker().select('.next', true).elements, function(v){
15889             v.show();
15890             switch (this.viewMode) {
15891                 case 0:
15892
15893                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15894                         v.hide();
15895                     }
15896                     break;
15897                 case 1:
15898                 case 2:
15899                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15900                         v.hide();
15901                     }
15902                     break;
15903             }
15904         })
15905     },
15906     
15907     moveMonth: function(date, dir)
15908     {
15909         if (!dir) return date;
15910         var new_date = new Date(date.valueOf()),
15911         day = new_date.getUTCDate(),
15912         month = new_date.getUTCMonth(),
15913         mag = Math.abs(dir),
15914         new_month, test;
15915         dir = dir > 0 ? 1 : -1;
15916         if (mag == 1){
15917             test = dir == -1
15918             // If going back one month, make sure month is not current month
15919             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15920             ? function(){
15921                 return new_date.getUTCMonth() == month;
15922             }
15923             // If going forward one month, make sure month is as expected
15924             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15925             : function(){
15926                 return new_date.getUTCMonth() != new_month;
15927             };
15928             new_month = month + dir;
15929             new_date.setUTCMonth(new_month);
15930             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15931             if (new_month < 0 || new_month > 11)
15932                 new_month = (new_month + 12) % 12;
15933         } else {
15934             // For magnitudes >1, move one month at a time...
15935             for (var i=0; i<mag; i++)
15936                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15937                 new_date = this.moveMonth(new_date, dir);
15938             // ...then reset the day, keeping it in the new month
15939             new_month = new_date.getUTCMonth();
15940             new_date.setUTCDate(day);
15941             test = function(){
15942                 return new_month != new_date.getUTCMonth();
15943             };
15944         }
15945         // Common date-resetting loop -- if date is beyond end of month, make it
15946         // end of month
15947         while (test()){
15948             new_date.setUTCDate(--day);
15949             new_date.setUTCMonth(new_month);
15950         }
15951         return new_date;
15952     },
15953
15954     moveYear: function(date, dir)
15955     {
15956         return this.moveMonth(date, dir*12);
15957     },
15958
15959     dateWithinRange: function(date)
15960     {
15961         return date >= this.startDate && date <= this.endDate;
15962     },
15963
15964     
15965     remove: function() 
15966     {
15967         this.picker().remove();
15968     }
15969    
15970 });
15971
15972 Roo.apply(Roo.bootstrap.DateField,  {
15973     
15974     head : {
15975         tag: 'thead',
15976         cn: [
15977         {
15978             tag: 'tr',
15979             cn: [
15980             {
15981                 tag: 'th',
15982                 cls: 'prev',
15983                 html: '<i class="fa fa-arrow-left"/>'
15984             },
15985             {
15986                 tag: 'th',
15987                 cls: 'switch',
15988                 colspan: '5'
15989             },
15990             {
15991                 tag: 'th',
15992                 cls: 'next',
15993                 html: '<i class="fa fa-arrow-right"/>'
15994             }
15995
15996             ]
15997         }
15998         ]
15999     },
16000     
16001     content : {
16002         tag: 'tbody',
16003         cn: [
16004         {
16005             tag: 'tr',
16006             cn: [
16007             {
16008                 tag: 'td',
16009                 colspan: '7'
16010             }
16011             ]
16012         }
16013         ]
16014     },
16015     
16016     footer : {
16017         tag: 'tfoot',
16018         cn: [
16019         {
16020             tag: 'tr',
16021             cn: [
16022             {
16023                 tag: 'th',
16024                 colspan: '7',
16025                 cls: 'today'
16026             }
16027                     
16028             ]
16029         }
16030         ]
16031     },
16032     
16033     dates:{
16034         en: {
16035             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
16036             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
16037             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
16038             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16039             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
16040             today: "Today"
16041         }
16042     },
16043     
16044     modes: [
16045     {
16046         clsName: 'days',
16047         navFnc: 'Month',
16048         navStep: 1
16049     },
16050     {
16051         clsName: 'months',
16052         navFnc: 'FullYear',
16053         navStep: 1
16054     },
16055     {
16056         clsName: 'years',
16057         navFnc: 'FullYear',
16058         navStep: 10
16059     }]
16060 });
16061
16062 Roo.apply(Roo.bootstrap.DateField,  {
16063   
16064     template : {
16065         tag: 'div',
16066         cls: 'datepicker dropdown-menu roo-dynamic',
16067         cn: [
16068         {
16069             tag: 'div',
16070             cls: 'datepicker-days',
16071             cn: [
16072             {
16073                 tag: 'table',
16074                 cls: 'table-condensed',
16075                 cn:[
16076                 Roo.bootstrap.DateField.head,
16077                 {
16078                     tag: 'tbody'
16079                 },
16080                 Roo.bootstrap.DateField.footer
16081                 ]
16082             }
16083             ]
16084         },
16085         {
16086             tag: 'div',
16087             cls: 'datepicker-months',
16088             cn: [
16089             {
16090                 tag: 'table',
16091                 cls: 'table-condensed',
16092                 cn:[
16093                 Roo.bootstrap.DateField.head,
16094                 Roo.bootstrap.DateField.content,
16095                 Roo.bootstrap.DateField.footer
16096                 ]
16097             }
16098             ]
16099         },
16100         {
16101             tag: 'div',
16102             cls: 'datepicker-years',
16103             cn: [
16104             {
16105                 tag: 'table',
16106                 cls: 'table-condensed',
16107                 cn:[
16108                 Roo.bootstrap.DateField.head,
16109                 Roo.bootstrap.DateField.content,
16110                 Roo.bootstrap.DateField.footer
16111                 ]
16112             }
16113             ]
16114         }
16115         ]
16116     }
16117 });
16118
16119  
16120
16121  /*
16122  * - LGPL
16123  *
16124  * TimeField
16125  * 
16126  */
16127
16128 /**
16129  * @class Roo.bootstrap.TimeField
16130  * @extends Roo.bootstrap.Input
16131  * Bootstrap DateField class
16132  * 
16133  * 
16134  * @constructor
16135  * Create a new TimeField
16136  * @param {Object} config The config object
16137  */
16138
16139 Roo.bootstrap.TimeField = function(config){
16140     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
16141     this.addEvents({
16142             /**
16143              * @event show
16144              * Fires when this field show.
16145              * @param {Roo.bootstrap.DateField} thisthis
16146              * @param {Mixed} date The date value
16147              */
16148             show : true,
16149             /**
16150              * @event show
16151              * Fires when this field hide.
16152              * @param {Roo.bootstrap.DateField} this
16153              * @param {Mixed} date The date value
16154              */
16155             hide : true,
16156             /**
16157              * @event select
16158              * Fires when select a date.
16159              * @param {Roo.bootstrap.DateField} this
16160              * @param {Mixed} date The date value
16161              */
16162             select : true
16163         });
16164 };
16165
16166 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
16167     
16168     /**
16169      * @cfg {String} format
16170      * The default time format string which can be overriden for localization support.  The format must be
16171      * valid according to {@link Date#parseDate} (defaults to 'H:i').
16172      */
16173     format : "H:i",
16174        
16175     onRender: function(ct, position)
16176     {
16177         
16178         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
16179                 
16180         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
16181         
16182         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16183         
16184         this.pop = this.picker().select('>.datepicker-time',true).first();
16185         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16186         
16187         this.picker().on('mousedown', this.onMousedown, this);
16188         this.picker().on('click', this.onClick, this);
16189         
16190         this.picker().addClass('datepicker-dropdown');
16191     
16192         this.fillTime();
16193         this.update();
16194             
16195         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
16196         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
16197         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
16198         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
16199         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
16200         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
16201
16202     },
16203     
16204     fireKey: function(e){
16205         if (!this.picker().isVisible()){
16206             if (e.keyCode == 27) { // allow escape to hide and re-show picker
16207                 this.show();
16208             }
16209             return;
16210         }
16211
16212         e.preventDefault();
16213         
16214         switch(e.keyCode){
16215             case 27: // escape
16216                 this.hide();
16217                 break;
16218             case 37: // left
16219             case 39: // right
16220                 this.onTogglePeriod();
16221                 break;
16222             case 38: // up
16223                 this.onIncrementMinutes();
16224                 break;
16225             case 40: // down
16226                 this.onDecrementMinutes();
16227                 break;
16228             case 13: // enter
16229             case 9: // tab
16230                 this.setTime();
16231                 break;
16232         }
16233     },
16234     
16235     onClick: function(e) {
16236         e.stopPropagation();
16237         e.preventDefault();
16238     },
16239     
16240     picker : function()
16241     {
16242         return this.el.select('.datepicker', true).first();
16243     },
16244     
16245     fillTime: function()
16246     {    
16247         var time = this.pop.select('tbody', true).first();
16248         
16249         time.dom.innerHTML = '';
16250         
16251         time.createChild({
16252             tag: 'tr',
16253             cn: [
16254                 {
16255                     tag: 'td',
16256                     cn: [
16257                         {
16258                             tag: 'a',
16259                             href: '#',
16260                             cls: 'btn',
16261                             cn: [
16262                                 {
16263                                     tag: 'span',
16264                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
16265                                 }
16266                             ]
16267                         } 
16268                     ]
16269                 },
16270                 {
16271                     tag: 'td',
16272                     cls: 'separator'
16273                 },
16274                 {
16275                     tag: 'td',
16276                     cn: [
16277                         {
16278                             tag: 'a',
16279                             href: '#',
16280                             cls: 'btn',
16281                             cn: [
16282                                 {
16283                                     tag: 'span',
16284                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
16285                                 }
16286                             ]
16287                         }
16288                     ]
16289                 },
16290                 {
16291                     tag: 'td',
16292                     cls: 'separator'
16293                 }
16294             ]
16295         });
16296         
16297         time.createChild({
16298             tag: 'tr',
16299             cn: [
16300                 {
16301                     tag: 'td',
16302                     cn: [
16303                         {
16304                             tag: 'span',
16305                             cls: 'timepicker-hour',
16306                             html: '00'
16307                         }  
16308                     ]
16309                 },
16310                 {
16311                     tag: 'td',
16312                     cls: 'separator',
16313                     html: ':'
16314                 },
16315                 {
16316                     tag: 'td',
16317                     cn: [
16318                         {
16319                             tag: 'span',
16320                             cls: 'timepicker-minute',
16321                             html: '00'
16322                         }  
16323                     ]
16324                 },
16325                 {
16326                     tag: 'td',
16327                     cls: 'separator'
16328                 },
16329                 {
16330                     tag: 'td',
16331                     cn: [
16332                         {
16333                             tag: 'button',
16334                             type: 'button',
16335                             cls: 'btn btn-primary period',
16336                             html: 'AM'
16337                             
16338                         }
16339                     ]
16340                 }
16341             ]
16342         });
16343         
16344         time.createChild({
16345             tag: 'tr',
16346             cn: [
16347                 {
16348                     tag: 'td',
16349                     cn: [
16350                         {
16351                             tag: 'a',
16352                             href: '#',
16353                             cls: 'btn',
16354                             cn: [
16355                                 {
16356                                     tag: 'span',
16357                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
16358                                 }
16359                             ]
16360                         }
16361                     ]
16362                 },
16363                 {
16364                     tag: 'td',
16365                     cls: 'separator'
16366                 },
16367                 {
16368                     tag: 'td',
16369                     cn: [
16370                         {
16371                             tag: 'a',
16372                             href: '#',
16373                             cls: 'btn',
16374                             cn: [
16375                                 {
16376                                     tag: 'span',
16377                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
16378                                 }
16379                             ]
16380                         }
16381                     ]
16382                 },
16383                 {
16384                     tag: 'td',
16385                     cls: 'separator'
16386                 }
16387             ]
16388         });
16389         
16390     },
16391     
16392     update: function()
16393     {
16394         
16395         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
16396         
16397         this.fill();
16398     },
16399     
16400     fill: function() 
16401     {
16402         var hours = this.time.getHours();
16403         var minutes = this.time.getMinutes();
16404         var period = 'AM';
16405         
16406         if(hours > 11){
16407             period = 'PM';
16408         }
16409         
16410         if(hours == 0){
16411             hours = 12;
16412         }
16413         
16414         
16415         if(hours > 12){
16416             hours = hours - 12;
16417         }
16418         
16419         if(hours < 10){
16420             hours = '0' + hours;
16421         }
16422         
16423         if(minutes < 10){
16424             minutes = '0' + minutes;
16425         }
16426         
16427         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
16428         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
16429         this.pop.select('button', true).first().dom.innerHTML = period;
16430         
16431     },
16432     
16433     place: function()
16434     {   
16435         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
16436         
16437         var cls = ['bottom'];
16438         
16439         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
16440             cls.pop();
16441             cls.push('top');
16442         }
16443         
16444         cls.push('right');
16445         
16446         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
16447             cls.pop();
16448             cls.push('left');
16449         }
16450         
16451         this.picker().addClass(cls.join('-'));
16452         
16453         var _this = this;
16454         
16455         Roo.each(cls, function(c){
16456             if(c == 'bottom'){
16457                 _this.picker().setTop(_this.inputEl().getHeight());
16458                 return;
16459             }
16460             if(c == 'top'){
16461                 _this.picker().setTop(0 - _this.picker().getHeight());
16462                 return;
16463             }
16464             
16465             if(c == 'left'){
16466                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
16467                 return;
16468             }
16469             if(c == 'right'){
16470                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
16471                 return;
16472             }
16473         });
16474         
16475     },
16476   
16477     onFocus : function()
16478     {
16479         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
16480         this.show();
16481     },
16482     
16483     onBlur : function()
16484     {
16485         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
16486         this.hide();
16487     },
16488     
16489     show : function()
16490     {
16491         this.picker().show();
16492         this.pop.show();
16493         this.update();
16494         this.place();
16495         
16496         this.fireEvent('show', this, this.date);
16497     },
16498     
16499     hide : function()
16500     {
16501         this.picker().hide();
16502         this.pop.hide();
16503         
16504         this.fireEvent('hide', this, this.date);
16505     },
16506     
16507     setTime : function()
16508     {
16509         this.hide();
16510         this.setValue(this.time.format(this.format));
16511         
16512         this.fireEvent('select', this, this.date);
16513         
16514         
16515     },
16516     
16517     onMousedown: function(e){
16518         e.stopPropagation();
16519         e.preventDefault();
16520     },
16521     
16522     onIncrementHours: function()
16523     {
16524         Roo.log('onIncrementHours');
16525         this.time = this.time.add(Date.HOUR, 1);
16526         this.update();
16527         
16528     },
16529     
16530     onDecrementHours: function()
16531     {
16532         Roo.log('onDecrementHours');
16533         this.time = this.time.add(Date.HOUR, -1);
16534         this.update();
16535     },
16536     
16537     onIncrementMinutes: function()
16538     {
16539         Roo.log('onIncrementMinutes');
16540         this.time = this.time.add(Date.MINUTE, 1);
16541         this.update();
16542     },
16543     
16544     onDecrementMinutes: function()
16545     {
16546         Roo.log('onDecrementMinutes');
16547         this.time = this.time.add(Date.MINUTE, -1);
16548         this.update();
16549     },
16550     
16551     onTogglePeriod: function()
16552     {
16553         Roo.log('onTogglePeriod');
16554         this.time = this.time.add(Date.HOUR, 12);
16555         this.update();
16556     }
16557     
16558    
16559 });
16560
16561 Roo.apply(Roo.bootstrap.TimeField,  {
16562     
16563     content : {
16564         tag: 'tbody',
16565         cn: [
16566             {
16567                 tag: 'tr',
16568                 cn: [
16569                 {
16570                     tag: 'td',
16571                     colspan: '7'
16572                 }
16573                 ]
16574             }
16575         ]
16576     },
16577     
16578     footer : {
16579         tag: 'tfoot',
16580         cn: [
16581             {
16582                 tag: 'tr',
16583                 cn: [
16584                 {
16585                     tag: 'th',
16586                     colspan: '7',
16587                     cls: '',
16588                     cn: [
16589                         {
16590                             tag: 'button',
16591                             cls: 'btn btn-info ok',
16592                             html: 'OK'
16593                         }
16594                     ]
16595                 }
16596
16597                 ]
16598             }
16599         ]
16600     }
16601 });
16602
16603 Roo.apply(Roo.bootstrap.TimeField,  {
16604   
16605     template : {
16606         tag: 'div',
16607         cls: 'datepicker dropdown-menu',
16608         cn: [
16609             {
16610                 tag: 'div',
16611                 cls: 'datepicker-time',
16612                 cn: [
16613                 {
16614                     tag: 'table',
16615                     cls: 'table-condensed',
16616                     cn:[
16617                     Roo.bootstrap.TimeField.content,
16618                     Roo.bootstrap.TimeField.footer
16619                     ]
16620                 }
16621                 ]
16622             }
16623         ]
16624     }
16625 });
16626
16627  
16628
16629  /*
16630  * - LGPL
16631  *
16632  * MonthField
16633  * 
16634  */
16635
16636 /**
16637  * @class Roo.bootstrap.MonthField
16638  * @extends Roo.bootstrap.Input
16639  * Bootstrap MonthField class
16640  * 
16641  * @cfg {String} language default en
16642  * 
16643  * @constructor
16644  * Create a new MonthField
16645  * @param {Object} config The config object
16646  */
16647
16648 Roo.bootstrap.MonthField = function(config){
16649     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
16650     
16651     this.addEvents({
16652         /**
16653          * @event show
16654          * Fires when this field show.
16655          * @param {Roo.bootstrap.MonthField} this
16656          * @param {Mixed} date The date value
16657          */
16658         show : true,
16659         /**
16660          * @event show
16661          * Fires when this field hide.
16662          * @param {Roo.bootstrap.MonthField} this
16663          * @param {Mixed} date The date value
16664          */
16665         hide : true,
16666         /**
16667          * @event select
16668          * Fires when select a date.
16669          * @param {Roo.bootstrap.MonthField} this
16670          * @param {String} oldvalue The old value
16671          * @param {String} newvalue The new value
16672          */
16673         select : true
16674     });
16675 };
16676
16677 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
16678     
16679     onRender: function(ct, position)
16680     {
16681         
16682         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
16683         
16684         this.language = this.language || 'en';
16685         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
16686         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
16687         
16688         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
16689         this.isInline = false;
16690         this.isInput = true;
16691         this.component = this.el.select('.add-on', true).first() || false;
16692         this.component = (this.component && this.component.length === 0) ? false : this.component;
16693         this.hasInput = this.component && this.inputEL().length;
16694         
16695         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
16696         
16697         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16698         
16699         this.picker().on('mousedown', this.onMousedown, this);
16700         this.picker().on('click', this.onClick, this);
16701         
16702         this.picker().addClass('datepicker-dropdown');
16703         
16704         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16705             v.setStyle('width', '189px');
16706         });
16707         
16708         this.fillMonths();
16709         
16710         this.update();
16711         
16712         if(this.isInline) {
16713             this.show();
16714         }
16715         
16716     },
16717     
16718     setValue: function(v, suppressEvent)
16719     {   
16720         var o = this.getValue();
16721         
16722         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
16723         
16724         this.update();
16725
16726         if(suppressEvent !== true){
16727             this.fireEvent('select', this, o, v);
16728         }
16729         
16730     },
16731     
16732     getValue: function()
16733     {
16734         return this.value;
16735     },
16736     
16737     onClick: function(e) 
16738     {
16739         e.stopPropagation();
16740         e.preventDefault();
16741         
16742         var target = e.getTarget();
16743         
16744         if(target.nodeName.toLowerCase() === 'i'){
16745             target = Roo.get(target).dom.parentNode;
16746         }
16747         
16748         var nodeName = target.nodeName;
16749         var className = target.className;
16750         var html = target.innerHTML;
16751         
16752         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
16753             return;
16754         }
16755         
16756         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
16757         
16758         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16759         
16760         this.hide();
16761                         
16762     },
16763     
16764     picker : function()
16765     {
16766         return this.pickerEl;
16767     },
16768     
16769     fillMonths: function()
16770     {    
16771         var i = 0;
16772         var months = this.picker().select('>.datepicker-months td', true).first();
16773         
16774         months.dom.innerHTML = '';
16775         
16776         while (i < 12) {
16777             var month = {
16778                 tag: 'span',
16779                 cls: 'month',
16780                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
16781             }
16782             
16783             months.createChild(month);
16784         }
16785         
16786     },
16787     
16788     update: function()
16789     {
16790         var _this = this;
16791         
16792         if(typeof(this.vIndex) == 'undefined' && this.value.length){
16793             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
16794         }
16795         
16796         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
16797             e.removeClass('active');
16798             
16799             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
16800                 e.addClass('active');
16801             }
16802         })
16803     },
16804     
16805     place: function()
16806     {
16807         if(this.isInline) return;
16808         
16809         this.picker().removeClass(['bottom', 'top']);
16810         
16811         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16812             /*
16813              * place to the top of element!
16814              *
16815              */
16816             
16817             this.picker().addClass('top');
16818             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16819             
16820             return;
16821         }
16822         
16823         this.picker().addClass('bottom');
16824         
16825         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16826     },
16827     
16828     onFocus : function()
16829     {
16830         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
16831         this.show();
16832     },
16833     
16834     onBlur : function()
16835     {
16836         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
16837         
16838         var d = this.inputEl().getValue();
16839         
16840         this.setValue(d);
16841                 
16842         this.hide();
16843     },
16844     
16845     show : function()
16846     {
16847         this.picker().show();
16848         this.picker().select('>.datepicker-months', true).first().show();
16849         this.update();
16850         this.place();
16851         
16852         this.fireEvent('show', this, this.date);
16853     },
16854     
16855     hide : function()
16856     {
16857         if(this.isInline) return;
16858         this.picker().hide();
16859         this.fireEvent('hide', this, this.date);
16860         
16861     },
16862     
16863     onMousedown: function(e)
16864     {
16865         e.stopPropagation();
16866         e.preventDefault();
16867     },
16868     
16869     keyup: function(e)
16870     {
16871         Roo.bootstrap.MonthField.superclass.keyup.call(this);
16872         this.update();
16873     },
16874
16875     fireKey: function(e)
16876     {
16877         if (!this.picker().isVisible()){
16878             if (e.keyCode == 27) // allow escape to hide and re-show picker
16879                 this.show();
16880             return;
16881         }
16882         
16883         var dir;
16884         
16885         switch(e.keyCode){
16886             case 27: // escape
16887                 this.hide();
16888                 e.preventDefault();
16889                 break;
16890             case 37: // left
16891             case 39: // right
16892                 dir = e.keyCode == 37 ? -1 : 1;
16893                 
16894                 this.vIndex = this.vIndex + dir;
16895                 
16896                 if(this.vIndex < 0){
16897                     this.vIndex = 0;
16898                 }
16899                 
16900                 if(this.vIndex > 11){
16901                     this.vIndex = 11;
16902                 }
16903                 
16904                 if(isNaN(this.vIndex)){
16905                     this.vIndex = 0;
16906                 }
16907                 
16908                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16909                 
16910                 break;
16911             case 38: // up
16912             case 40: // down
16913                 
16914                 dir = e.keyCode == 38 ? -1 : 1;
16915                 
16916                 this.vIndex = this.vIndex + dir * 4;
16917                 
16918                 if(this.vIndex < 0){
16919                     this.vIndex = 0;
16920                 }
16921                 
16922                 if(this.vIndex > 11){
16923                     this.vIndex = 11;
16924                 }
16925                 
16926                 if(isNaN(this.vIndex)){
16927                     this.vIndex = 0;
16928                 }
16929                 
16930                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16931                 break;
16932                 
16933             case 13: // enter
16934                 
16935                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16936                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16937                 }
16938                 
16939                 this.hide();
16940                 e.preventDefault();
16941                 break;
16942             case 9: // tab
16943                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
16944                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
16945                 }
16946                 this.hide();
16947                 break;
16948             case 16: // shift
16949             case 17: // ctrl
16950             case 18: // alt
16951                 break;
16952             default :
16953                 this.hide();
16954                 
16955         }
16956     },
16957     
16958     remove: function() 
16959     {
16960         this.picker().remove();
16961     }
16962    
16963 });
16964
16965 Roo.apply(Roo.bootstrap.MonthField,  {
16966     
16967     content : {
16968         tag: 'tbody',
16969         cn: [
16970         {
16971             tag: 'tr',
16972             cn: [
16973             {
16974                 tag: 'td',
16975                 colspan: '7'
16976             }
16977             ]
16978         }
16979         ]
16980     },
16981     
16982     dates:{
16983         en: {
16984             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
16985             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
16986         }
16987     }
16988 });
16989
16990 Roo.apply(Roo.bootstrap.MonthField,  {
16991   
16992     template : {
16993         tag: 'div',
16994         cls: 'datepicker dropdown-menu roo-dynamic',
16995         cn: [
16996             {
16997                 tag: 'div',
16998                 cls: 'datepicker-months',
16999                 cn: [
17000                 {
17001                     tag: 'table',
17002                     cls: 'table-condensed',
17003                     cn:[
17004                         Roo.bootstrap.DateField.content
17005                     ]
17006                 }
17007                 ]
17008             }
17009         ]
17010     }
17011 });
17012
17013  
17014
17015  
17016  /*
17017  * - LGPL
17018  *
17019  * CheckBox
17020  * 
17021  */
17022
17023 /**
17024  * @class Roo.bootstrap.CheckBox
17025  * @extends Roo.bootstrap.Input
17026  * Bootstrap CheckBox class
17027  * 
17028  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
17029  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
17030  * @cfg {String} boxLabel The text that appears beside the checkbox
17031  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
17032  * @cfg {Boolean} checked initnal the element
17033  * @cfg {Boolean} inline inline the element (default false)
17034  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
17035  * 
17036  * @constructor
17037  * Create a new CheckBox
17038  * @param {Object} config The config object
17039  */
17040
17041 Roo.bootstrap.CheckBox = function(config){
17042     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
17043    
17044     this.addEvents({
17045         /**
17046         * @event check
17047         * Fires when the element is checked or unchecked.
17048         * @param {Roo.bootstrap.CheckBox} this This input
17049         * @param {Boolean} checked The new checked value
17050         */
17051        check : true
17052     });
17053     
17054 };
17055
17056 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
17057   
17058     inputType: 'checkbox',
17059     inputValue: 1,
17060     valueOff: 0,
17061     boxLabel: false,
17062     checked: false,
17063     weight : false,
17064     inline: false,
17065     
17066     getAutoCreate : function()
17067     {
17068         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17069         
17070         var id = Roo.id();
17071         
17072         var cfg = {};
17073         
17074         cfg.cls = 'form-group ' + this.inputType; //input-group
17075         
17076         if(this.inline){
17077             cfg.cls += ' ' + this.inputType + '-inline';
17078         }
17079         
17080         var input =  {
17081             tag: 'input',
17082             id : id,
17083             type : this.inputType,
17084             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
17085             cls : 'roo-' + this.inputType, //'form-box',
17086             placeholder : this.placeholder || ''
17087             
17088         };
17089         
17090         if (this.weight) { // Validity check?
17091             cfg.cls += " " + this.inputType + "-" + this.weight;
17092         }
17093         
17094         if (this.disabled) {
17095             input.disabled=true;
17096         }
17097         
17098         if(this.checked){
17099             input.checked = this.checked;
17100         }
17101         
17102         if (this.name) {
17103             input.name = this.name;
17104         }
17105         
17106         if (this.size) {
17107             input.cls += ' input-' + this.size;
17108         }
17109         
17110         var settings=this;
17111         
17112         ['xs','sm','md','lg'].map(function(size){
17113             if (settings[size]) {
17114                 cfg.cls += ' col-' + size + '-' + settings[size];
17115             }
17116         });
17117         
17118         var inputblock = input;
17119          
17120         if (this.before || this.after) {
17121             
17122             inputblock = {
17123                 cls : 'input-group',
17124                 cn :  [] 
17125             };
17126             
17127             if (this.before) {
17128                 inputblock.cn.push({
17129                     tag :'span',
17130                     cls : 'input-group-addon',
17131                     html : this.before
17132                 });
17133             }
17134             
17135             inputblock.cn.push(input);
17136             
17137             if (this.after) {
17138                 inputblock.cn.push({
17139                     tag :'span',
17140                     cls : 'input-group-addon',
17141                     html : this.after
17142                 });
17143             }
17144             
17145         }
17146         
17147         if (align ==='left' && this.fieldLabel.length) {
17148                 Roo.log("left and has label");
17149                 cfg.cn = [
17150                     
17151                     {
17152                         tag: 'label',
17153                         'for' :  id,
17154                         cls : 'control-label col-md-' + this.labelWidth,
17155                         html : this.fieldLabel
17156                         
17157                     },
17158                     {
17159                         cls : "col-md-" + (12 - this.labelWidth), 
17160                         cn: [
17161                             inputblock
17162                         ]
17163                     }
17164                     
17165                 ];
17166         } else if ( this.fieldLabel.length) {
17167                 Roo.log(" label");
17168                 cfg.cn = [
17169                    
17170                     {
17171                         tag: this.boxLabel ? 'span' : 'label',
17172                         'for': id,
17173                         cls: 'control-label box-input-label',
17174                         //cls : 'input-group-addon',
17175                         html : this.fieldLabel
17176                         
17177                     },
17178                     
17179                     inputblock
17180                     
17181                 ];
17182
17183         } else {
17184             
17185                 Roo.log(" no label && no align");
17186                 cfg.cn = [  inputblock ] ;
17187                 
17188                 
17189         }
17190         if(this.boxLabel){
17191              var boxLabelCfg = {
17192                 tag: 'label',
17193                 //'for': id, // box label is handled by onclick - so no for...
17194                 cls: 'box-label',
17195                 html: this.boxLabel
17196             }
17197             
17198             if(this.tooltip){
17199                 boxLabelCfg.tooltip = this.tooltip;
17200             }
17201              
17202             cfg.cn.push(boxLabelCfg);
17203         }
17204         
17205         
17206        
17207         return cfg;
17208         
17209     },
17210     
17211     /**
17212      * return the real input element.
17213      */
17214     inputEl: function ()
17215     {
17216         return this.el.select('input.roo-' + this.inputType,true).first();
17217     },
17218     
17219     labelEl: function()
17220     {
17221         return this.el.select('label.control-label',true).first();
17222     },
17223     /* depricated... */
17224     
17225     label: function()
17226     {
17227         return this.labelEl();
17228     },
17229     
17230     initEvents : function()
17231     {
17232 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17233         
17234         this.inputEl().on('click', this.onClick,  this);
17235         
17236         if (this.boxLabel) { 
17237             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
17238         }
17239         
17240         this.startValue = this.getValue();
17241         
17242         if(this.groupId){
17243             Roo.bootstrap.CheckBox.register(this);
17244         }
17245     },
17246     
17247     onClick : function()
17248     {   
17249         this.setChecked(!this.checked);
17250     },
17251     
17252     setChecked : function(state,suppressEvent)
17253     {
17254         this.startValue = this.getValue();
17255         
17256         if(this.inputType == 'radio'){
17257             
17258             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17259                 e.dom.checked = false;
17260             });
17261             
17262             this.inputEl().dom.checked = true;
17263             
17264             this.inputEl().dom.value = this.inputValue;
17265             
17266             if(suppressEvent !== true){
17267                 this.fireEvent('check', this, true);
17268             }
17269             
17270             this.validate();
17271             
17272             return;
17273         }
17274         
17275         this.checked = state;
17276         
17277         this.inputEl().dom.checked = state;
17278         
17279         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17280         
17281         if(suppressEvent !== true){
17282             this.fireEvent('check', this, state);
17283         }
17284         
17285         this.validate();
17286     },
17287     
17288     getValue : function()
17289     {
17290         if(this.inputType == 'radio'){
17291             return this.getGroupValue();
17292         }
17293         
17294         return this.inputEl().getValue();
17295         
17296     },
17297     
17298     getGroupValue : function()
17299     {
17300         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
17301             return '';
17302         }
17303         
17304         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
17305     },
17306     
17307     setValue : function(v,suppressEvent)
17308     {
17309         if(this.inputType == 'radio'){
17310             this.setGroupValue(v, suppressEvent);
17311             return;
17312         }
17313         
17314         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
17315         
17316         this.validate();
17317     },
17318     
17319     setGroupValue : function(v, suppressEvent)
17320     {
17321         this.startValue = this.getValue();
17322         
17323         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17324             e.dom.checked = false;
17325             
17326             if(e.dom.value == v){
17327                 e.dom.checked = true;
17328             }
17329         });
17330         
17331         if(suppressEvent !== true){
17332             this.fireEvent('check', this, true);
17333         }
17334
17335         this.validate();
17336         
17337         return;
17338     },
17339     
17340     validate : function()
17341     {
17342         if(
17343                 this.disabled || 
17344                 (this.inputType == 'radio' && this.validateRadio()) ||
17345                 (this.inputType == 'checkbox' && this.validateCheckbox())
17346         ){
17347             this.markValid();
17348             return true;
17349         }
17350         
17351         this.markInvalid();
17352         return false;
17353     },
17354     
17355     validateRadio : function()
17356     {
17357         var valid = false;
17358         
17359         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17360             if(!e.dom.checked){
17361                 return;
17362             }
17363             
17364             valid = true;
17365             
17366             return false;
17367         });
17368         
17369         return valid;
17370     },
17371     
17372     validateCheckbox : function()
17373     {
17374         if(!this.groupId){
17375             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
17376         }
17377         
17378         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17379         
17380         if(!group){
17381             return false;
17382         }
17383         
17384         var r = false;
17385         
17386         for(var i in group){
17387             if(r){
17388                 break;
17389             }
17390             
17391             r = (group[i].getValue() == group[i].inputValue) ? true : false;
17392         }
17393         
17394         return r;
17395     },
17396     
17397     /**
17398      * Mark this field as valid
17399      */
17400     markValid : function()
17401     {
17402         if(this.allowBlank){
17403             return;
17404         }
17405         
17406         var _this = this;
17407         
17408         this.fireEvent('valid', this);
17409         
17410         if(this.inputType == 'radio'){
17411             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17412                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17413                 e.findParent('.form-group', false, true).addClass(_this.validClass);
17414             });
17415             
17416             return;
17417         }
17418         
17419         if(!this.groupId){
17420             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17421             this.el.findParent('.form-group', false, true).addClass(this.validClass);
17422             return;
17423         }
17424         
17425         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17426             
17427         if(!group){
17428             return;
17429         }
17430         
17431         for(var i in group){
17432             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17433             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
17434         }
17435     },
17436     
17437      /**
17438      * Mark this field as invalid
17439      * @param {String} msg The validation message
17440      */
17441     markInvalid : function(msg)
17442     {
17443         if(this.allowBlank){
17444             return;
17445         }
17446         
17447         var _this = this;
17448         
17449         this.fireEvent('invalid', this, msg);
17450         
17451         if(this.inputType == 'radio'){
17452             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
17453                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
17454                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
17455             });
17456             
17457             return;
17458         }
17459         
17460         if(!this.groupId){
17461             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17462             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
17463             return;
17464         }
17465         
17466         var group = Roo.bootstrap.CheckBox.get(this.groupId);
17467             
17468         if(!group){
17469             return;
17470         }
17471         
17472         for(var i in group){
17473             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
17474             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
17475         }
17476         
17477     }
17478     
17479 });
17480
17481 Roo.apply(Roo.bootstrap.CheckBox, {
17482     
17483     groups: {},
17484     
17485      /**
17486     * register a CheckBox Group
17487     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
17488     */
17489     register : function(checkbox)
17490     {
17491         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
17492             this.groups[checkbox.groupId] = {};
17493         }
17494         
17495         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
17496             return;
17497         }
17498         
17499         this.groups[checkbox.groupId][checkbox.name] = checkbox;
17500         
17501     },
17502     /**
17503     * fetch a CheckBox Group based on the group ID
17504     * @param {string} the group ID
17505     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
17506     */
17507     get: function(groupId) {
17508         if (typeof(this.groups[groupId]) == 'undefined') {
17509             return false;
17510         }
17511         
17512         return this.groups[groupId] ;
17513     }
17514     
17515     
17516 });
17517 /*
17518  * - LGPL
17519  *
17520  * Radio
17521  *
17522  *
17523  * not inline
17524  *<div class="radio">
17525   <label>
17526     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
17527     Option one is this and that&mdash;be sure to include why it's great
17528   </label>
17529 </div>
17530  *
17531  *
17532  *inline
17533  *<span>
17534  *<label class="radio-inline">fieldLabel</label>
17535  *<label class="radio-inline">
17536   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
17537 </label>
17538 <span>
17539  * 
17540  * 
17541  */
17542
17543 /**
17544  * @class Roo.bootstrap.Radio
17545  * @extends Roo.bootstrap.CheckBox
17546  * Bootstrap Radio class
17547
17548  * @constructor
17549  * Create a new Radio
17550  * @param {Object} config The config object
17551  */
17552
17553 Roo.bootstrap.Radio = function(config){
17554     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
17555    
17556 };
17557
17558 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
17559     
17560     inputType: 'radio',
17561     inputValue: '',
17562     valueOff: '',
17563     
17564     getAutoCreate : function()
17565     {
17566         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
17567         align = align || 'left'; // default...
17568         
17569         
17570         
17571         var id = Roo.id();
17572         
17573         var cfg = {
17574                 tag : this.inline ? 'span' : 'div',
17575                 cls : '',
17576                 cn : []
17577         };
17578         
17579         var inline = this.inline ? ' radio-inline' : '';
17580         
17581         var lbl = {
17582                 tag: 'label' ,
17583                 // does not need for, as we wrap the input with it..
17584                 'for' : id,
17585                 cls : 'control-label box-label' + inline,
17586                 cn : []
17587         };
17588         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
17589         
17590         var fieldLabel = {
17591             tag: 'label' ,
17592             //cls : 'control-label' + inline,
17593             html : this.fieldLabel,
17594             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
17595         };
17596         
17597  
17598         
17599         
17600         var input =  {
17601             tag: 'input',
17602             id : id,
17603             type : this.inputType,
17604             //value : (!this.checked) ? this.valueOff : this.inputValue,
17605             value : this.inputValue,
17606             cls : 'roo-radio',
17607             placeholder : this.placeholder || '' // ?? needed????
17608             
17609         };
17610         if (this.weight) { // Validity check?
17611             input.cls += " radio-" + this.weight;
17612         }
17613         if (this.disabled) {
17614             input.disabled=true;
17615         }
17616         
17617         if(this.checked){
17618             input.checked = this.checked;
17619         }
17620         
17621         if (this.name) {
17622             input.name = this.name;
17623         }
17624         
17625         if (this.size) {
17626             input.cls += ' input-' + this.size;
17627         }
17628         
17629         //?? can span's inline have a width??
17630         
17631         var settings=this;
17632         ['xs','sm','md','lg'].map(function(size){
17633             if (settings[size]) {
17634                 cfg.cls += ' col-' + size + '-' + settings[size];
17635             }
17636         });
17637         
17638         var inputblock = input;
17639         
17640         if (this.before || this.after) {
17641             
17642             inputblock = {
17643                 cls : 'input-group',
17644                 tag : 'span',
17645                 cn :  [] 
17646             };
17647             if (this.before) {
17648                 inputblock.cn.push({
17649                     tag :'span',
17650                     cls : 'input-group-addon',
17651                     html : this.before
17652                 });
17653             }
17654             inputblock.cn.push(input);
17655             if (this.after) {
17656                 inputblock.cn.push({
17657                     tag :'span',
17658                     cls : 'input-group-addon',
17659                     html : this.after
17660                 });
17661             }
17662             
17663         };
17664         
17665         
17666         if (this.fieldLabel && this.fieldLabel.length) {
17667             cfg.cn.push(fieldLabel);
17668         }
17669        
17670         // normal bootstrap puts the input inside the label.
17671         // however with our styled version - it has to go after the input.
17672        
17673         //lbl.cn.push(inputblock);
17674         
17675         var lblwrap =  {
17676             tag: 'span',
17677             cls: 'radio' + inline,
17678             cn: [
17679                 inputblock,
17680                 lbl
17681             ]
17682         };
17683         
17684         cfg.cn.push( lblwrap);
17685         
17686         if(this.boxLabel){
17687             lbl.cn.push({
17688                 tag: 'span',
17689                 html: this.boxLabel
17690             })
17691         }
17692          
17693         
17694         return cfg;
17695         
17696     },
17697     
17698     initEvents : function()
17699     {
17700 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
17701         
17702         this.inputEl().on('click', this.onClick,  this);
17703         if (this.boxLabel) {
17704             Roo.log('find label')
17705             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
17706         }
17707         
17708     },
17709     
17710     inputEl: function ()
17711     {
17712         return this.el.select('input.roo-radio',true).first();
17713     },
17714     onClick : function()
17715     {   
17716         Roo.log("click");
17717         this.setChecked(true);
17718     },
17719     
17720     setChecked : function(state,suppressEvent)
17721     {
17722         if(state){
17723             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17724                 v.dom.checked = false;
17725             });
17726         }
17727         Roo.log(this.inputEl().dom);
17728         this.checked = state;
17729         this.inputEl().dom.checked = state;
17730         
17731         if(suppressEvent !== true){
17732             this.fireEvent('check', this, state);
17733         }
17734         
17735         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
17736         
17737     },
17738     
17739     getGroupValue : function()
17740     {
17741         var value = '';
17742         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
17743             if(v.dom.checked == true){
17744                 value = v.dom.value;
17745             }
17746         });
17747         
17748         return value;
17749     },
17750     
17751     /**
17752      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
17753      * @return {Mixed} value The field value
17754      */
17755     getValue : function(){
17756         return this.getGroupValue();
17757     }
17758     
17759 });
17760
17761  
17762 //<script type="text/javascript">
17763
17764 /*
17765  * Based  Ext JS Library 1.1.1
17766  * Copyright(c) 2006-2007, Ext JS, LLC.
17767  * LGPL
17768  *
17769  */
17770  
17771 /**
17772  * @class Roo.HtmlEditorCore
17773  * @extends Roo.Component
17774  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
17775  *
17776  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
17777  */
17778
17779 Roo.HtmlEditorCore = function(config){
17780     
17781     
17782     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
17783     
17784     
17785     this.addEvents({
17786         /**
17787          * @event initialize
17788          * Fires when the editor is fully initialized (including the iframe)
17789          * @param {Roo.HtmlEditorCore} this
17790          */
17791         initialize: true,
17792         /**
17793          * @event activate
17794          * Fires when the editor is first receives the focus. Any insertion must wait
17795          * until after this event.
17796          * @param {Roo.HtmlEditorCore} this
17797          */
17798         activate: true,
17799          /**
17800          * @event beforesync
17801          * Fires before the textarea is updated with content from the editor iframe. Return false
17802          * to cancel the sync.
17803          * @param {Roo.HtmlEditorCore} this
17804          * @param {String} html
17805          */
17806         beforesync: true,
17807          /**
17808          * @event beforepush
17809          * Fires before the iframe editor is updated with content from the textarea. Return false
17810          * to cancel the push.
17811          * @param {Roo.HtmlEditorCore} this
17812          * @param {String} html
17813          */
17814         beforepush: true,
17815          /**
17816          * @event sync
17817          * Fires when the textarea is updated with content from the editor iframe.
17818          * @param {Roo.HtmlEditorCore} this
17819          * @param {String} html
17820          */
17821         sync: true,
17822          /**
17823          * @event push
17824          * Fires when the iframe editor is updated with content from the textarea.
17825          * @param {Roo.HtmlEditorCore} this
17826          * @param {String} html
17827          */
17828         push: true,
17829         
17830         /**
17831          * @event editorevent
17832          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17833          * @param {Roo.HtmlEditorCore} this
17834          */
17835         editorevent: true
17836         
17837     });
17838     
17839     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
17840     
17841     // defaults : white / black...
17842     this.applyBlacklists();
17843     
17844     
17845     
17846 };
17847
17848
17849 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
17850
17851
17852      /**
17853      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
17854      */
17855     
17856     owner : false,
17857     
17858      /**
17859      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17860      *                        Roo.resizable.
17861      */
17862     resizable : false,
17863      /**
17864      * @cfg {Number} height (in pixels)
17865      */   
17866     height: 300,
17867    /**
17868      * @cfg {Number} width (in pixels)
17869      */   
17870     width: 500,
17871     
17872     /**
17873      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17874      * 
17875      */
17876     stylesheets: false,
17877     
17878     // id of frame..
17879     frameId: false,
17880     
17881     // private properties
17882     validationEvent : false,
17883     deferHeight: true,
17884     initialized : false,
17885     activated : false,
17886     sourceEditMode : false,
17887     onFocus : Roo.emptyFn,
17888     iframePad:3,
17889     hideMode:'offsets',
17890     
17891     clearUp: true,
17892     
17893     // blacklist + whitelisted elements..
17894     black: false,
17895     white: false,
17896      
17897     
17898
17899     /**
17900      * Protected method that will not generally be called directly. It
17901      * is called when the editor initializes the iframe with HTML contents. Override this method if you
17902      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
17903      */
17904     getDocMarkup : function(){
17905         // body styles..
17906         var st = '';
17907         
17908         // inherit styels from page...?? 
17909         if (this.stylesheets === false) {
17910             
17911             Roo.get(document.head).select('style').each(function(node) {
17912                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17913             });
17914             
17915             Roo.get(document.head).select('link').each(function(node) { 
17916                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
17917             });
17918             
17919         } else if (!this.stylesheets.length) {
17920                 // simple..
17921                 st = '<style type="text/css">' +
17922                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17923                    '</style>';
17924         } else { 
17925             
17926         }
17927         
17928         st +=  '<style type="text/css">' +
17929             'IMG { cursor: pointer } ' +
17930         '</style>';
17931
17932         
17933         return '<html><head>' + st  +
17934             //<style type="text/css">' +
17935             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
17936             //'</style>' +
17937             ' </head><body class="roo-htmleditor-body"></body></html>';
17938     },
17939
17940     // private
17941     onRender : function(ct, position)
17942     {
17943         var _t = this;
17944         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
17945         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
17946         
17947         
17948         this.el.dom.style.border = '0 none';
17949         this.el.dom.setAttribute('tabIndex', -1);
17950         this.el.addClass('x-hidden hide');
17951         
17952         
17953         
17954         if(Roo.isIE){ // fix IE 1px bogus margin
17955             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
17956         }
17957        
17958         
17959         this.frameId = Roo.id();
17960         
17961          
17962         
17963         var iframe = this.owner.wrap.createChild({
17964             tag: 'iframe',
17965             cls: 'form-control', // bootstrap..
17966             id: this.frameId,
17967             name: this.frameId,
17968             frameBorder : 'no',
17969             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
17970         }, this.el
17971         );
17972         
17973         
17974         this.iframe = iframe.dom;
17975
17976          this.assignDocWin();
17977         
17978         this.doc.designMode = 'on';
17979        
17980         this.doc.open();
17981         this.doc.write(this.getDocMarkup());
17982         this.doc.close();
17983
17984         
17985         var task = { // must defer to wait for browser to be ready
17986             run : function(){
17987                 //console.log("run task?" + this.doc.readyState);
17988                 this.assignDocWin();
17989                 if(this.doc.body || this.doc.readyState == 'complete'){
17990                     try {
17991                         this.doc.designMode="on";
17992                     } catch (e) {
17993                         return;
17994                     }
17995                     Roo.TaskMgr.stop(task);
17996                     this.initEditor.defer(10, this);
17997                 }
17998             },
17999             interval : 10,
18000             duration: 10000,
18001             scope: this
18002         };
18003         Roo.TaskMgr.start(task);
18004
18005     },
18006
18007     // private
18008     onResize : function(w, h)
18009     {
18010          Roo.log('resize: ' +w + ',' + h );
18011         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
18012         if(!this.iframe){
18013             return;
18014         }
18015         if(typeof w == 'number'){
18016             
18017             this.iframe.style.width = w + 'px';
18018         }
18019         if(typeof h == 'number'){
18020             
18021             this.iframe.style.height = h + 'px';
18022             if(this.doc){
18023                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
18024             }
18025         }
18026         
18027     },
18028
18029     /**
18030      * Toggles the editor between standard and source edit mode.
18031      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18032      */
18033     toggleSourceEdit : function(sourceEditMode){
18034         
18035         this.sourceEditMode = sourceEditMode === true;
18036         
18037         if(this.sourceEditMode){
18038  
18039             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
18040             
18041         }else{
18042             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
18043             //this.iframe.className = '';
18044             this.deferFocus();
18045         }
18046         //this.setSize(this.owner.wrap.getSize());
18047         //this.fireEvent('editmodechange', this, this.sourceEditMode);
18048     },
18049
18050     
18051   
18052
18053     /**
18054      * Protected method that will not generally be called directly. If you need/want
18055      * custom HTML cleanup, this is the method you should override.
18056      * @param {String} html The HTML to be cleaned
18057      * return {String} The cleaned HTML
18058      */
18059     cleanHtml : function(html){
18060         html = String(html);
18061         if(html.length > 5){
18062             if(Roo.isSafari){ // strip safari nonsense
18063                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
18064             }
18065         }
18066         if(html == '&nbsp;'){
18067             html = '';
18068         }
18069         return html;
18070     },
18071
18072     /**
18073      * HTML Editor -> Textarea
18074      * Protected method that will not generally be called directly. Syncs the contents
18075      * of the editor iframe with the textarea.
18076      */
18077     syncValue : function(){
18078         if(this.initialized){
18079             var bd = (this.doc.body || this.doc.documentElement);
18080             //this.cleanUpPaste(); -- this is done else where and causes havoc..
18081             var html = bd.innerHTML;
18082             if(Roo.isSafari){
18083                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
18084                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
18085                 if(m && m[1]){
18086                     html = '<div style="'+m[0]+'">' + html + '</div>';
18087                 }
18088             }
18089             html = this.cleanHtml(html);
18090             // fix up the special chars.. normaly like back quotes in word...
18091             // however we do not want to do this with chinese..
18092             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
18093                 var cc = b.charCodeAt();
18094                 if (
18095                     (cc >= 0x4E00 && cc < 0xA000 ) ||
18096                     (cc >= 0x3400 && cc < 0x4E00 ) ||
18097                     (cc >= 0xf900 && cc < 0xfb00 )
18098                 ) {
18099                         return b;
18100                 }
18101                 return "&#"+cc+";" 
18102             });
18103             if(this.owner.fireEvent('beforesync', this, html) !== false){
18104                 this.el.dom.value = html;
18105                 this.owner.fireEvent('sync', this, html);
18106             }
18107         }
18108     },
18109
18110     /**
18111      * Protected method that will not generally be called directly. Pushes the value of the textarea
18112      * into the iframe editor.
18113      */
18114     pushValue : function(){
18115         if(this.initialized){
18116             var v = this.el.dom.value.trim();
18117             
18118 //            if(v.length < 1){
18119 //                v = '&#160;';
18120 //            }
18121             
18122             if(this.owner.fireEvent('beforepush', this, v) !== false){
18123                 var d = (this.doc.body || this.doc.documentElement);
18124                 d.innerHTML = v;
18125                 this.cleanUpPaste();
18126                 this.el.dom.value = d.innerHTML;
18127                 this.owner.fireEvent('push', this, v);
18128             }
18129         }
18130     },
18131
18132     // private
18133     deferFocus : function(){
18134         this.focus.defer(10, this);
18135     },
18136
18137     // doc'ed in Field
18138     focus : function(){
18139         if(this.win && !this.sourceEditMode){
18140             this.win.focus();
18141         }else{
18142             this.el.focus();
18143         }
18144     },
18145     
18146     assignDocWin: function()
18147     {
18148         var iframe = this.iframe;
18149         
18150          if(Roo.isIE){
18151             this.doc = iframe.contentWindow.document;
18152             this.win = iframe.contentWindow;
18153         } else {
18154 //            if (!Roo.get(this.frameId)) {
18155 //                return;
18156 //            }
18157 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18158 //            this.win = Roo.get(this.frameId).dom.contentWindow;
18159             
18160             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
18161                 return;
18162             }
18163             
18164             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
18165             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
18166         }
18167     },
18168     
18169     // private
18170     initEditor : function(){
18171         //console.log("INIT EDITOR");
18172         this.assignDocWin();
18173         
18174         
18175         
18176         this.doc.designMode="on";
18177         this.doc.open();
18178         this.doc.write(this.getDocMarkup());
18179         this.doc.close();
18180         
18181         var dbody = (this.doc.body || this.doc.documentElement);
18182         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
18183         // this copies styles from the containing element into thsi one..
18184         // not sure why we need all of this..
18185         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
18186         
18187         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
18188         //ss['background-attachment'] = 'fixed'; // w3c
18189         dbody.bgProperties = 'fixed'; // ie
18190         //Roo.DomHelper.applyStyles(dbody, ss);
18191         Roo.EventManager.on(this.doc, {
18192             //'mousedown': this.onEditorEvent,
18193             'mouseup': this.onEditorEvent,
18194             'dblclick': this.onEditorEvent,
18195             'click': this.onEditorEvent,
18196             'keyup': this.onEditorEvent,
18197             buffer:100,
18198             scope: this
18199         });
18200         if(Roo.isGecko){
18201             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
18202         }
18203         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
18204             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
18205         }
18206         this.initialized = true;
18207
18208         this.owner.fireEvent('initialize', this);
18209         this.pushValue();
18210     },
18211
18212     // private
18213     onDestroy : function(){
18214         
18215         
18216         
18217         if(this.rendered){
18218             
18219             //for (var i =0; i < this.toolbars.length;i++) {
18220             //    // fixme - ask toolbars for heights?
18221             //    this.toolbars[i].onDestroy();
18222            // }
18223             
18224             //this.wrap.dom.innerHTML = '';
18225             //this.wrap.remove();
18226         }
18227     },
18228
18229     // private
18230     onFirstFocus : function(){
18231         
18232         this.assignDocWin();
18233         
18234         
18235         this.activated = true;
18236          
18237     
18238         if(Roo.isGecko){ // prevent silly gecko errors
18239             this.win.focus();
18240             var s = this.win.getSelection();
18241             if(!s.focusNode || s.focusNode.nodeType != 3){
18242                 var r = s.getRangeAt(0);
18243                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
18244                 r.collapse(true);
18245                 this.deferFocus();
18246             }
18247             try{
18248                 this.execCmd('useCSS', true);
18249                 this.execCmd('styleWithCSS', false);
18250             }catch(e){}
18251         }
18252         this.owner.fireEvent('activate', this);
18253     },
18254
18255     // private
18256     adjustFont: function(btn){
18257         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
18258         //if(Roo.isSafari){ // safari
18259         //    adjust *= 2;
18260        // }
18261         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
18262         if(Roo.isSafari){ // safari
18263             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
18264             v =  (v < 10) ? 10 : v;
18265             v =  (v > 48) ? 48 : v;
18266             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
18267             
18268         }
18269         
18270         
18271         v = Math.max(1, v+adjust);
18272         
18273         this.execCmd('FontSize', v  );
18274     },
18275
18276     onEditorEvent : function(e){
18277         this.owner.fireEvent('editorevent', this, e);
18278       //  this.updateToolbar();
18279         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
18280     },
18281
18282     insertTag : function(tg)
18283     {
18284         // could be a bit smarter... -> wrap the current selected tRoo..
18285         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
18286             
18287             range = this.createRange(this.getSelection());
18288             var wrappingNode = this.doc.createElement(tg.toLowerCase());
18289             wrappingNode.appendChild(range.extractContents());
18290             range.insertNode(wrappingNode);
18291
18292             return;
18293             
18294             
18295             
18296         }
18297         this.execCmd("formatblock",   tg);
18298         
18299     },
18300     
18301     insertText : function(txt)
18302     {
18303         
18304         
18305         var range = this.createRange();
18306         range.deleteContents();
18307                //alert(Sender.getAttribute('label'));
18308                
18309         range.insertNode(this.doc.createTextNode(txt));
18310     } ,
18311     
18312      
18313
18314     /**
18315      * Executes a Midas editor command on the editor document and performs necessary focus and
18316      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
18317      * @param {String} cmd The Midas command
18318      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18319      */
18320     relayCmd : function(cmd, value){
18321         this.win.focus();
18322         this.execCmd(cmd, value);
18323         this.owner.fireEvent('editorevent', this);
18324         //this.updateToolbar();
18325         this.owner.deferFocus();
18326     },
18327
18328     /**
18329      * Executes a Midas editor command directly on the editor document.
18330      * For visual commands, you should use {@link #relayCmd} instead.
18331      * <b>This should only be called after the editor is initialized.</b>
18332      * @param {String} cmd The Midas command
18333      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
18334      */
18335     execCmd : function(cmd, value){
18336         this.doc.execCommand(cmd, false, value === undefined ? null : value);
18337         this.syncValue();
18338     },
18339  
18340  
18341    
18342     /**
18343      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
18344      * to insert tRoo.
18345      * @param {String} text | dom node.. 
18346      */
18347     insertAtCursor : function(text)
18348     {
18349         
18350         
18351         
18352         if(!this.activated){
18353             return;
18354         }
18355         /*
18356         if(Roo.isIE){
18357             this.win.focus();
18358             var r = this.doc.selection.createRange();
18359             if(r){
18360                 r.collapse(true);
18361                 r.pasteHTML(text);
18362                 this.syncValue();
18363                 this.deferFocus();
18364             
18365             }
18366             return;
18367         }
18368         */
18369         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
18370             this.win.focus();
18371             
18372             
18373             // from jquery ui (MIT licenced)
18374             var range, node;
18375             var win = this.win;
18376             
18377             if (win.getSelection && win.getSelection().getRangeAt) {
18378                 range = win.getSelection().getRangeAt(0);
18379                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
18380                 range.insertNode(node);
18381             } else if (win.document.selection && win.document.selection.createRange) {
18382                 // no firefox support
18383                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18384                 win.document.selection.createRange().pasteHTML(txt);
18385             } else {
18386                 // no firefox support
18387                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
18388                 this.execCmd('InsertHTML', txt);
18389             } 
18390             
18391             this.syncValue();
18392             
18393             this.deferFocus();
18394         }
18395     },
18396  // private
18397     mozKeyPress : function(e){
18398         if(e.ctrlKey){
18399             var c = e.getCharCode(), cmd;
18400           
18401             if(c > 0){
18402                 c = String.fromCharCode(c).toLowerCase();
18403                 switch(c){
18404                     case 'b':
18405                         cmd = 'bold';
18406                         break;
18407                     case 'i':
18408                         cmd = 'italic';
18409                         break;
18410                     
18411                     case 'u':
18412                         cmd = 'underline';
18413                         break;
18414                     
18415                     case 'v':
18416                         this.cleanUpPaste.defer(100, this);
18417                         return;
18418                         
18419                 }
18420                 if(cmd){
18421                     this.win.focus();
18422                     this.execCmd(cmd);
18423                     this.deferFocus();
18424                     e.preventDefault();
18425                 }
18426                 
18427             }
18428         }
18429     },
18430
18431     // private
18432     fixKeys : function(){ // load time branching for fastest keydown performance
18433         if(Roo.isIE){
18434             return function(e){
18435                 var k = e.getKey(), r;
18436                 if(k == e.TAB){
18437                     e.stopEvent();
18438                     r = this.doc.selection.createRange();
18439                     if(r){
18440                         r.collapse(true);
18441                         r.pasteHTML('&#160;&#160;&#160;&#160;');
18442                         this.deferFocus();
18443                     }
18444                     return;
18445                 }
18446                 
18447                 if(k == e.ENTER){
18448                     r = this.doc.selection.createRange();
18449                     if(r){
18450                         var target = r.parentElement();
18451                         if(!target || target.tagName.toLowerCase() != 'li'){
18452                             e.stopEvent();
18453                             r.pasteHTML('<br />');
18454                             r.collapse(false);
18455                             r.select();
18456                         }
18457                     }
18458                 }
18459                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18460                     this.cleanUpPaste.defer(100, this);
18461                     return;
18462                 }
18463                 
18464                 
18465             };
18466         }else if(Roo.isOpera){
18467             return function(e){
18468                 var k = e.getKey();
18469                 if(k == e.TAB){
18470                     e.stopEvent();
18471                     this.win.focus();
18472                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
18473                     this.deferFocus();
18474                 }
18475                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18476                     this.cleanUpPaste.defer(100, this);
18477                     return;
18478                 }
18479                 
18480             };
18481         }else if(Roo.isSafari){
18482             return function(e){
18483                 var k = e.getKey();
18484                 
18485                 if(k == e.TAB){
18486                     e.stopEvent();
18487                     this.execCmd('InsertText','\t');
18488                     this.deferFocus();
18489                     return;
18490                 }
18491                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
18492                     this.cleanUpPaste.defer(100, this);
18493                     return;
18494                 }
18495                 
18496              };
18497         }
18498     }(),
18499     
18500     getAllAncestors: function()
18501     {
18502         var p = this.getSelectedNode();
18503         var a = [];
18504         if (!p) {
18505             a.push(p); // push blank onto stack..
18506             p = this.getParentElement();
18507         }
18508         
18509         
18510         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
18511             a.push(p);
18512             p = p.parentNode;
18513         }
18514         a.push(this.doc.body);
18515         return a;
18516     },
18517     lastSel : false,
18518     lastSelNode : false,
18519     
18520     
18521     getSelection : function() 
18522     {
18523         this.assignDocWin();
18524         return Roo.isIE ? this.doc.selection : this.win.getSelection();
18525     },
18526     
18527     getSelectedNode: function() 
18528     {
18529         // this may only work on Gecko!!!
18530         
18531         // should we cache this!!!!
18532         
18533         
18534         
18535          
18536         var range = this.createRange(this.getSelection()).cloneRange();
18537         
18538         if (Roo.isIE) {
18539             var parent = range.parentElement();
18540             while (true) {
18541                 var testRange = range.duplicate();
18542                 testRange.moveToElementText(parent);
18543                 if (testRange.inRange(range)) {
18544                     break;
18545                 }
18546                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
18547                     break;
18548                 }
18549                 parent = parent.parentElement;
18550             }
18551             return parent;
18552         }
18553         
18554         // is ancestor a text element.
18555         var ac =  range.commonAncestorContainer;
18556         if (ac.nodeType == 3) {
18557             ac = ac.parentNode;
18558         }
18559         
18560         var ar = ac.childNodes;
18561          
18562         var nodes = [];
18563         var other_nodes = [];
18564         var has_other_nodes = false;
18565         for (var i=0;i<ar.length;i++) {
18566             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
18567                 continue;
18568             }
18569             // fullly contained node.
18570             
18571             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
18572                 nodes.push(ar[i]);
18573                 continue;
18574             }
18575             
18576             // probably selected..
18577             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
18578                 other_nodes.push(ar[i]);
18579                 continue;
18580             }
18581             // outer..
18582             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
18583                 continue;
18584             }
18585             
18586             
18587             has_other_nodes = true;
18588         }
18589         if (!nodes.length && other_nodes.length) {
18590             nodes= other_nodes;
18591         }
18592         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
18593             return false;
18594         }
18595         
18596         return nodes[0];
18597     },
18598     createRange: function(sel)
18599     {
18600         // this has strange effects when using with 
18601         // top toolbar - not sure if it's a great idea.
18602         //this.editor.contentWindow.focus();
18603         if (typeof sel != "undefined") {
18604             try {
18605                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
18606             } catch(e) {
18607                 return this.doc.createRange();
18608             }
18609         } else {
18610             return this.doc.createRange();
18611         }
18612     },
18613     getParentElement: function()
18614     {
18615         
18616         this.assignDocWin();
18617         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
18618         
18619         var range = this.createRange(sel);
18620          
18621         try {
18622             var p = range.commonAncestorContainer;
18623             while (p.nodeType == 3) { // text node
18624                 p = p.parentNode;
18625             }
18626             return p;
18627         } catch (e) {
18628             return null;
18629         }
18630     
18631     },
18632     /***
18633      *
18634      * Range intersection.. the hard stuff...
18635      *  '-1' = before
18636      *  '0' = hits..
18637      *  '1' = after.
18638      *         [ -- selected range --- ]
18639      *   [fail]                        [fail]
18640      *
18641      *    basically..
18642      *      if end is before start or  hits it. fail.
18643      *      if start is after end or hits it fail.
18644      *
18645      *   if either hits (but other is outside. - then it's not 
18646      *   
18647      *    
18648      **/
18649     
18650     
18651     // @see http://www.thismuchiknow.co.uk/?p=64.
18652     rangeIntersectsNode : function(range, node)
18653     {
18654         var nodeRange = node.ownerDocument.createRange();
18655         try {
18656             nodeRange.selectNode(node);
18657         } catch (e) {
18658             nodeRange.selectNodeContents(node);
18659         }
18660     
18661         var rangeStartRange = range.cloneRange();
18662         rangeStartRange.collapse(true);
18663     
18664         var rangeEndRange = range.cloneRange();
18665         rangeEndRange.collapse(false);
18666     
18667         var nodeStartRange = nodeRange.cloneRange();
18668         nodeStartRange.collapse(true);
18669     
18670         var nodeEndRange = nodeRange.cloneRange();
18671         nodeEndRange.collapse(false);
18672     
18673         return rangeStartRange.compareBoundaryPoints(
18674                  Range.START_TO_START, nodeEndRange) == -1 &&
18675                rangeEndRange.compareBoundaryPoints(
18676                  Range.START_TO_START, nodeStartRange) == 1;
18677         
18678          
18679     },
18680     rangeCompareNode : function(range, node)
18681     {
18682         var nodeRange = node.ownerDocument.createRange();
18683         try {
18684             nodeRange.selectNode(node);
18685         } catch (e) {
18686             nodeRange.selectNodeContents(node);
18687         }
18688         
18689         
18690         range.collapse(true);
18691     
18692         nodeRange.collapse(true);
18693      
18694         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
18695         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
18696          
18697         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
18698         
18699         var nodeIsBefore   =  ss == 1;
18700         var nodeIsAfter    = ee == -1;
18701         
18702         if (nodeIsBefore && nodeIsAfter)
18703             return 0; // outer
18704         if (!nodeIsBefore && nodeIsAfter)
18705             return 1; //right trailed.
18706         
18707         if (nodeIsBefore && !nodeIsAfter)
18708             return 2;  // left trailed.
18709         // fully contined.
18710         return 3;
18711     },
18712
18713     // private? - in a new class?
18714     cleanUpPaste :  function()
18715     {
18716         // cleans up the whole document..
18717         Roo.log('cleanuppaste');
18718         
18719         this.cleanUpChildren(this.doc.body);
18720         var clean = this.cleanWordChars(this.doc.body.innerHTML);
18721         if (clean != this.doc.body.innerHTML) {
18722             this.doc.body.innerHTML = clean;
18723         }
18724         
18725     },
18726     
18727     cleanWordChars : function(input) {// change the chars to hex code
18728         var he = Roo.HtmlEditorCore;
18729         
18730         var output = input;
18731         Roo.each(he.swapCodes, function(sw) { 
18732             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
18733             
18734             output = output.replace(swapper, sw[1]);
18735         });
18736         
18737         return output;
18738     },
18739     
18740     
18741     cleanUpChildren : function (n)
18742     {
18743         if (!n.childNodes.length) {
18744             return;
18745         }
18746         for (var i = n.childNodes.length-1; i > -1 ; i--) {
18747            this.cleanUpChild(n.childNodes[i]);
18748         }
18749     },
18750     
18751     
18752         
18753     
18754     cleanUpChild : function (node)
18755     {
18756         var ed = this;
18757         //console.log(node);
18758         if (node.nodeName == "#text") {
18759             // clean up silly Windows -- stuff?
18760             return; 
18761         }
18762         if (node.nodeName == "#comment") {
18763             node.parentNode.removeChild(node);
18764             // clean up silly Windows -- stuff?
18765             return; 
18766         }
18767         var lcname = node.tagName.toLowerCase();
18768         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
18769         // whitelist of tags..
18770         
18771         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
18772             // remove node.
18773             node.parentNode.removeChild(node);
18774             return;
18775             
18776         }
18777         
18778         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
18779         
18780         // remove <a name=....> as rendering on yahoo mailer is borked with this.
18781         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
18782         
18783         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
18784         //    remove_keep_children = true;
18785         //}
18786         
18787         if (remove_keep_children) {
18788             this.cleanUpChildren(node);
18789             // inserts everything just before this node...
18790             while (node.childNodes.length) {
18791                 var cn = node.childNodes[0];
18792                 node.removeChild(cn);
18793                 node.parentNode.insertBefore(cn, node);
18794             }
18795             node.parentNode.removeChild(node);
18796             return;
18797         }
18798         
18799         if (!node.attributes || !node.attributes.length) {
18800             this.cleanUpChildren(node);
18801             return;
18802         }
18803         
18804         function cleanAttr(n,v)
18805         {
18806             
18807             if (v.match(/^\./) || v.match(/^\//)) {
18808                 return;
18809             }
18810             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
18811                 return;
18812             }
18813             if (v.match(/^#/)) {
18814                 return;
18815             }
18816 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
18817             node.removeAttribute(n);
18818             
18819         }
18820         
18821         var cwhite = this.cwhite;
18822         var cblack = this.cblack;
18823             
18824         function cleanStyle(n,v)
18825         {
18826             if (v.match(/expression/)) { //XSS?? should we even bother..
18827                 node.removeAttribute(n);
18828                 return;
18829             }
18830             
18831             var parts = v.split(/;/);
18832             var clean = [];
18833             
18834             Roo.each(parts, function(p) {
18835                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
18836                 if (!p.length) {
18837                     return true;
18838                 }
18839                 var l = p.split(':').shift().replace(/\s+/g,'');
18840                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
18841                 
18842                 if ( cwhite.length && cblack.indexOf(l) > -1) {
18843 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18844                     //node.removeAttribute(n);
18845                     return true;
18846                 }
18847                 //Roo.log()
18848                 // only allow 'c whitelisted system attributes'
18849                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
18850 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
18851                     //node.removeAttribute(n);
18852                     return true;
18853                 }
18854                 
18855                 
18856                  
18857                 
18858                 clean.push(p);
18859                 return true;
18860             });
18861             if (clean.length) { 
18862                 node.setAttribute(n, clean.join(';'));
18863             } else {
18864                 node.removeAttribute(n);
18865             }
18866             
18867         }
18868         
18869         
18870         for (var i = node.attributes.length-1; i > -1 ; i--) {
18871             var a = node.attributes[i];
18872             //console.log(a);
18873             
18874             if (a.name.toLowerCase().substr(0,2)=='on')  {
18875                 node.removeAttribute(a.name);
18876                 continue;
18877             }
18878             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
18879                 node.removeAttribute(a.name);
18880                 continue;
18881             }
18882             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
18883                 cleanAttr(a.name,a.value); // fixme..
18884                 continue;
18885             }
18886             if (a.name == 'style') {
18887                 cleanStyle(a.name,a.value);
18888                 continue;
18889             }
18890             /// clean up MS crap..
18891             // tecnically this should be a list of valid class'es..
18892             
18893             
18894             if (a.name == 'class') {
18895                 if (a.value.match(/^Mso/)) {
18896                     node.className = '';
18897                 }
18898                 
18899                 if (a.value.match(/body/)) {
18900                     node.className = '';
18901                 }
18902                 continue;
18903             }
18904             
18905             // style cleanup!?
18906             // class cleanup?
18907             
18908         }
18909         
18910         
18911         this.cleanUpChildren(node);
18912         
18913         
18914     },
18915     /**
18916      * Clean up MS wordisms...
18917      */
18918     cleanWord : function(node)
18919     {
18920         var _t = this;
18921         var cleanWordChildren = function()
18922         {
18923             if (!node.childNodes.length) {
18924                 return;
18925             }
18926             for (var i = node.childNodes.length-1; i > -1 ; i--) {
18927                _t.cleanWord(node.childNodes[i]);
18928             }
18929         }
18930         
18931         
18932         if (!node) {
18933             this.cleanWord(this.doc.body);
18934             return;
18935         }
18936         if (node.nodeName == "#text") {
18937             // clean up silly Windows -- stuff?
18938             return; 
18939         }
18940         if (node.nodeName == "#comment") {
18941             node.parentNode.removeChild(node);
18942             // clean up silly Windows -- stuff?
18943             return; 
18944         }
18945         
18946         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
18947             node.parentNode.removeChild(node);
18948             return;
18949         }
18950         
18951         // remove - but keep children..
18952         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
18953             while (node.childNodes.length) {
18954                 var cn = node.childNodes[0];
18955                 node.removeChild(cn);
18956                 node.parentNode.insertBefore(cn, node);
18957             }
18958             node.parentNode.removeChild(node);
18959             cleanWordChildren();
18960             return;
18961         }
18962         // clean styles
18963         if (node.className.length) {
18964             
18965             var cn = node.className.split(/\W+/);
18966             var cna = [];
18967             Roo.each(cn, function(cls) {
18968                 if (cls.match(/Mso[a-zA-Z]+/)) {
18969                     return;
18970                 }
18971                 cna.push(cls);
18972             });
18973             node.className = cna.length ? cna.join(' ') : '';
18974             if (!cna.length) {
18975                 node.removeAttribute("class");
18976             }
18977         }
18978         
18979         if (node.hasAttribute("lang")) {
18980             node.removeAttribute("lang");
18981         }
18982         
18983         if (node.hasAttribute("style")) {
18984             
18985             var styles = node.getAttribute("style").split(";");
18986             var nstyle = [];
18987             Roo.each(styles, function(s) {
18988                 if (!s.match(/:/)) {
18989                     return;
18990                 }
18991                 var kv = s.split(":");
18992                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
18993                     return;
18994                 }
18995                 // what ever is left... we allow.
18996                 nstyle.push(s);
18997             });
18998             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
18999             if (!nstyle.length) {
19000                 node.removeAttribute('style');
19001             }
19002         }
19003         
19004         cleanWordChildren();
19005         
19006         
19007     },
19008     domToHTML : function(currentElement, depth, nopadtext) {
19009         
19010         depth = depth || 0;
19011         nopadtext = nopadtext || false;
19012     
19013         if (!currentElement) {
19014             return this.domToHTML(this.doc.body);
19015         }
19016         
19017         //Roo.log(currentElement);
19018         var j;
19019         var allText = false;
19020         var nodeName = currentElement.nodeName;
19021         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
19022         
19023         if  (nodeName == '#text') {
19024             
19025             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
19026         }
19027         
19028         
19029         var ret = '';
19030         if (nodeName != 'BODY') {
19031              
19032             var i = 0;
19033             // Prints the node tagName, such as <A>, <IMG>, etc
19034             if (tagName) {
19035                 var attr = [];
19036                 for(i = 0; i < currentElement.attributes.length;i++) {
19037                     // quoting?
19038                     var aname = currentElement.attributes.item(i).name;
19039                     if (!currentElement.attributes.item(i).value.length) {
19040                         continue;
19041                     }
19042                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
19043                 }
19044                 
19045                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
19046             } 
19047             else {
19048                 
19049                 // eack
19050             }
19051         } else {
19052             tagName = false;
19053         }
19054         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
19055             return ret;
19056         }
19057         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
19058             nopadtext = true;
19059         }
19060         
19061         
19062         // Traverse the tree
19063         i = 0;
19064         var currentElementChild = currentElement.childNodes.item(i);
19065         var allText = true;
19066         var innerHTML  = '';
19067         lastnode = '';
19068         while (currentElementChild) {
19069             // Formatting code (indent the tree so it looks nice on the screen)
19070             var nopad = nopadtext;
19071             if (lastnode == 'SPAN') {
19072                 nopad  = true;
19073             }
19074             // text
19075             if  (currentElementChild.nodeName == '#text') {
19076                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
19077                 toadd = nopadtext ? toadd : toadd.trim();
19078                 if (!nopad && toadd.length > 80) {
19079                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
19080                 }
19081                 innerHTML  += toadd;
19082                 
19083                 i++;
19084                 currentElementChild = currentElement.childNodes.item(i);
19085                 lastNode = '';
19086                 continue;
19087             }
19088             allText = false;
19089             
19090             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
19091                 
19092             // Recursively traverse the tree structure of the child node
19093             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
19094             lastnode = currentElementChild.nodeName;
19095             i++;
19096             currentElementChild=currentElement.childNodes.item(i);
19097         }
19098         
19099         ret += innerHTML;
19100         
19101         if (!allText) {
19102                 // The remaining code is mostly for formatting the tree
19103             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
19104         }
19105         
19106         
19107         if (tagName) {
19108             ret+= "</"+tagName+">";
19109         }
19110         return ret;
19111         
19112     },
19113         
19114     applyBlacklists : function()
19115     {
19116         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
19117         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
19118         
19119         this.white = [];
19120         this.black = [];
19121         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
19122             if (b.indexOf(tag) > -1) {
19123                 return;
19124             }
19125             this.white.push(tag);
19126             
19127         }, this);
19128         
19129         Roo.each(w, function(tag) {
19130             if (b.indexOf(tag) > -1) {
19131                 return;
19132             }
19133             if (this.white.indexOf(tag) > -1) {
19134                 return;
19135             }
19136             this.white.push(tag);
19137             
19138         }, this);
19139         
19140         
19141         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
19142             if (w.indexOf(tag) > -1) {
19143                 return;
19144             }
19145             this.black.push(tag);
19146             
19147         }, this);
19148         
19149         Roo.each(b, function(tag) {
19150             if (w.indexOf(tag) > -1) {
19151                 return;
19152             }
19153             if (this.black.indexOf(tag) > -1) {
19154                 return;
19155             }
19156             this.black.push(tag);
19157             
19158         }, this);
19159         
19160         
19161         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
19162         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
19163         
19164         this.cwhite = [];
19165         this.cblack = [];
19166         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
19167             if (b.indexOf(tag) > -1) {
19168                 return;
19169             }
19170             this.cwhite.push(tag);
19171             
19172         }, this);
19173         
19174         Roo.each(w, function(tag) {
19175             if (b.indexOf(tag) > -1) {
19176                 return;
19177             }
19178             if (this.cwhite.indexOf(tag) > -1) {
19179                 return;
19180             }
19181             this.cwhite.push(tag);
19182             
19183         }, this);
19184         
19185         
19186         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
19187             if (w.indexOf(tag) > -1) {
19188                 return;
19189             }
19190             this.cblack.push(tag);
19191             
19192         }, this);
19193         
19194         Roo.each(b, function(tag) {
19195             if (w.indexOf(tag) > -1) {
19196                 return;
19197             }
19198             if (this.cblack.indexOf(tag) > -1) {
19199                 return;
19200             }
19201             this.cblack.push(tag);
19202             
19203         }, this);
19204     },
19205     
19206     setStylesheets : function(stylesheets)
19207     {
19208         if(typeof(stylesheets) == 'string'){
19209             Roo.get(this.iframe.contentDocument.head).createChild({
19210                 tag : 'link',
19211                 rel : 'stylesheet',
19212                 type : 'text/css',
19213                 href : stylesheets
19214             });
19215             
19216             return;
19217         }
19218         var _this = this;
19219      
19220         Roo.each(stylesheets, function(s) {
19221             if(!s.length){
19222                 return;
19223             }
19224             
19225             Roo.get(_this.iframe.contentDocument.head).createChild({
19226                 tag : 'link',
19227                 rel : 'stylesheet',
19228                 type : 'text/css',
19229                 href : s
19230             });
19231         });
19232
19233         
19234     },
19235     
19236     removeStylesheets : function()
19237     {
19238         var _this = this;
19239         
19240         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
19241             s.remove();
19242         });
19243     }
19244     
19245     // hide stuff that is not compatible
19246     /**
19247      * @event blur
19248      * @hide
19249      */
19250     /**
19251      * @event change
19252      * @hide
19253      */
19254     /**
19255      * @event focus
19256      * @hide
19257      */
19258     /**
19259      * @event specialkey
19260      * @hide
19261      */
19262     /**
19263      * @cfg {String} fieldClass @hide
19264      */
19265     /**
19266      * @cfg {String} focusClass @hide
19267      */
19268     /**
19269      * @cfg {String} autoCreate @hide
19270      */
19271     /**
19272      * @cfg {String} inputType @hide
19273      */
19274     /**
19275      * @cfg {String} invalidClass @hide
19276      */
19277     /**
19278      * @cfg {String} invalidText @hide
19279      */
19280     /**
19281      * @cfg {String} msgFx @hide
19282      */
19283     /**
19284      * @cfg {String} validateOnBlur @hide
19285      */
19286 });
19287
19288 Roo.HtmlEditorCore.white = [
19289         'area', 'br', 'img', 'input', 'hr', 'wbr',
19290         
19291        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
19292        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
19293        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
19294        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
19295        'table',   'ul',         'xmp', 
19296        
19297        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
19298       'thead',   'tr', 
19299      
19300       'dir', 'menu', 'ol', 'ul', 'dl',
19301        
19302       'embed',  'object'
19303 ];
19304
19305
19306 Roo.HtmlEditorCore.black = [
19307     //    'embed',  'object', // enable - backend responsiblity to clean thiese
19308         'applet', // 
19309         'base',   'basefont', 'bgsound', 'blink',  'body', 
19310         'frame',  'frameset', 'head',    'html',   'ilayer', 
19311         'iframe', 'layer',  'link',     'meta',    'object',   
19312         'script', 'style' ,'title',  'xml' // clean later..
19313 ];
19314 Roo.HtmlEditorCore.clean = [
19315     'script', 'style', 'title', 'xml'
19316 ];
19317 Roo.HtmlEditorCore.remove = [
19318     'font'
19319 ];
19320 // attributes..
19321
19322 Roo.HtmlEditorCore.ablack = [
19323     'on'
19324 ];
19325     
19326 Roo.HtmlEditorCore.aclean = [ 
19327     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
19328 ];
19329
19330 // protocols..
19331 Roo.HtmlEditorCore.pwhite= [
19332         'http',  'https',  'mailto'
19333 ];
19334
19335 // white listed style attributes.
19336 Roo.HtmlEditorCore.cwhite= [
19337       //  'text-align', /// default is to allow most things..
19338       
19339          
19340 //        'font-size'//??
19341 ];
19342
19343 // black listed style attributes.
19344 Roo.HtmlEditorCore.cblack= [
19345       //  'font-size' -- this can be set by the project 
19346 ];
19347
19348
19349 Roo.HtmlEditorCore.swapCodes   =[ 
19350     [    8211, "--" ], 
19351     [    8212, "--" ], 
19352     [    8216,  "'" ],  
19353     [    8217, "'" ],  
19354     [    8220, '"' ],  
19355     [    8221, '"' ],  
19356     [    8226, "*" ],  
19357     [    8230, "..." ]
19358 ]; 
19359
19360     /*
19361  * - LGPL
19362  *
19363  * HtmlEditor
19364  * 
19365  */
19366
19367 /**
19368  * @class Roo.bootstrap.HtmlEditor
19369  * @extends Roo.bootstrap.TextArea
19370  * Bootstrap HtmlEditor class
19371
19372  * @constructor
19373  * Create a new HtmlEditor
19374  * @param {Object} config The config object
19375  */
19376
19377 Roo.bootstrap.HtmlEditor = function(config){
19378     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
19379     if (!this.toolbars) {
19380         this.toolbars = [];
19381     }
19382     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
19383     this.addEvents({
19384             /**
19385              * @event initialize
19386              * Fires when the editor is fully initialized (including the iframe)
19387              * @param {HtmlEditor} this
19388              */
19389             initialize: true,
19390             /**
19391              * @event activate
19392              * Fires when the editor is first receives the focus. Any insertion must wait
19393              * until after this event.
19394              * @param {HtmlEditor} this
19395              */
19396             activate: true,
19397              /**
19398              * @event beforesync
19399              * Fires before the textarea is updated with content from the editor iframe. Return false
19400              * to cancel the sync.
19401              * @param {HtmlEditor} this
19402              * @param {String} html
19403              */
19404             beforesync: true,
19405              /**
19406              * @event beforepush
19407              * Fires before the iframe editor is updated with content from the textarea. Return false
19408              * to cancel the push.
19409              * @param {HtmlEditor} this
19410              * @param {String} html
19411              */
19412             beforepush: true,
19413              /**
19414              * @event sync
19415              * Fires when the textarea is updated with content from the editor iframe.
19416              * @param {HtmlEditor} this
19417              * @param {String} html
19418              */
19419             sync: true,
19420              /**
19421              * @event push
19422              * Fires when the iframe editor is updated with content from the textarea.
19423              * @param {HtmlEditor} this
19424              * @param {String} html
19425              */
19426             push: true,
19427              /**
19428              * @event editmodechange
19429              * Fires when the editor switches edit modes
19430              * @param {HtmlEditor} this
19431              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
19432              */
19433             editmodechange: true,
19434             /**
19435              * @event editorevent
19436              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19437              * @param {HtmlEditor} this
19438              */
19439             editorevent: true,
19440             /**
19441              * @event firstfocus
19442              * Fires when on first focus - needed by toolbars..
19443              * @param {HtmlEditor} this
19444              */
19445             firstfocus: true,
19446             /**
19447              * @event autosave
19448              * Auto save the htmlEditor value as a file into Events
19449              * @param {HtmlEditor} this
19450              */
19451             autosave: true,
19452             /**
19453              * @event savedpreview
19454              * preview the saved version of htmlEditor
19455              * @param {HtmlEditor} this
19456              */
19457             savedpreview: true
19458         });
19459 };
19460
19461
19462 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
19463     
19464     
19465       /**
19466      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
19467      */
19468     toolbars : false,
19469    
19470      /**
19471      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19472      *                        Roo.resizable.
19473      */
19474     resizable : false,
19475      /**
19476      * @cfg {Number} height (in pixels)
19477      */   
19478     height: 300,
19479    /**
19480      * @cfg {Number} width (in pixels)
19481      */   
19482     width: false,
19483     
19484     /**
19485      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19486      * 
19487      */
19488     stylesheets: false,
19489     
19490     // id of frame..
19491     frameId: false,
19492     
19493     // private properties
19494     validationEvent : false,
19495     deferHeight: true,
19496     initialized : false,
19497     activated : false,
19498     
19499     onFocus : Roo.emptyFn,
19500     iframePad:3,
19501     hideMode:'offsets',
19502     
19503     
19504     tbContainer : false,
19505     
19506     toolbarContainer :function() {
19507         return this.wrap.select('.x-html-editor-tb',true).first();
19508     },
19509
19510     /**
19511      * Protected method that will not generally be called directly. It
19512      * is called when the editor creates its toolbar. Override this method if you need to
19513      * add custom toolbar buttons.
19514      * @param {HtmlEditor} editor
19515      */
19516     createToolbar : function(){
19517         
19518         Roo.log("create toolbars");
19519         
19520         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
19521         this.toolbars[0].render(this.toolbarContainer());
19522         
19523         return;
19524         
19525 //        if (!editor.toolbars || !editor.toolbars.length) {
19526 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
19527 //        }
19528 //        
19529 //        for (var i =0 ; i < editor.toolbars.length;i++) {
19530 //            editor.toolbars[i] = Roo.factory(
19531 //                    typeof(editor.toolbars[i]) == 'string' ?
19532 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
19533 //                Roo.bootstrap.HtmlEditor);
19534 //            editor.toolbars[i].init(editor);
19535 //        }
19536     },
19537
19538      
19539     // private
19540     onRender : function(ct, position)
19541     {
19542        // Roo.log("Call onRender: " + this.xtype);
19543         var _t = this;
19544         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
19545       
19546         this.wrap = this.inputEl().wrap({
19547             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
19548         });
19549         
19550         this.editorcore.onRender(ct, position);
19551          
19552         if (this.resizable) {
19553             this.resizeEl = new Roo.Resizable(this.wrap, {
19554                 pinned : true,
19555                 wrap: true,
19556                 dynamic : true,
19557                 minHeight : this.height,
19558                 height: this.height,
19559                 handles : this.resizable,
19560                 width: this.width,
19561                 listeners : {
19562                     resize : function(r, w, h) {
19563                         _t.onResize(w,h); // -something
19564                     }
19565                 }
19566             });
19567             
19568         }
19569         this.createToolbar(this);
19570        
19571         
19572         if(!this.width && this.resizable){
19573             this.setSize(this.wrap.getSize());
19574         }
19575         if (this.resizeEl) {
19576             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
19577             // should trigger onReize..
19578         }
19579         
19580     },
19581
19582     // private
19583     onResize : function(w, h)
19584     {
19585         Roo.log('resize: ' +w + ',' + h );
19586         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
19587         var ew = false;
19588         var eh = false;
19589         
19590         if(this.inputEl() ){
19591             if(typeof w == 'number'){
19592                 var aw = w - this.wrap.getFrameWidth('lr');
19593                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
19594                 ew = aw;
19595             }
19596             if(typeof h == 'number'){
19597                  var tbh = -11;  // fixme it needs to tool bar size!
19598                 for (var i =0; i < this.toolbars.length;i++) {
19599                     // fixme - ask toolbars for heights?
19600                     tbh += this.toolbars[i].el.getHeight();
19601                     //if (this.toolbars[i].footer) {
19602                     //    tbh += this.toolbars[i].footer.el.getHeight();
19603                     //}
19604                 }
19605               
19606                 
19607                 
19608                 
19609                 
19610                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
19611                 ah -= 5; // knock a few pixes off for look..
19612                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
19613                 var eh = ah;
19614             }
19615         }
19616         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
19617         this.editorcore.onResize(ew,eh);
19618         
19619     },
19620
19621     /**
19622      * Toggles the editor between standard and source edit mode.
19623      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19624      */
19625     toggleSourceEdit : function(sourceEditMode)
19626     {
19627         this.editorcore.toggleSourceEdit(sourceEditMode);
19628         
19629         if(this.editorcore.sourceEditMode){
19630             Roo.log('editor - showing textarea');
19631             
19632 //            Roo.log('in');
19633 //            Roo.log(this.syncValue());
19634             this.syncValue();
19635             this.inputEl().removeClass(['hide', 'x-hidden']);
19636             this.inputEl().dom.removeAttribute('tabIndex');
19637             this.inputEl().focus();
19638         }else{
19639             Roo.log('editor - hiding textarea');
19640 //            Roo.log('out')
19641 //            Roo.log(this.pushValue()); 
19642             this.pushValue();
19643             
19644             this.inputEl().addClass(['hide', 'x-hidden']);
19645             this.inputEl().dom.setAttribute('tabIndex', -1);
19646             //this.deferFocus();
19647         }
19648          
19649         if(this.resizable){
19650             this.setSize(this.wrap.getSize());
19651         }
19652         
19653         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
19654     },
19655  
19656     // private (for BoxComponent)
19657     adjustSize : Roo.BoxComponent.prototype.adjustSize,
19658
19659     // private (for BoxComponent)
19660     getResizeEl : function(){
19661         return this.wrap;
19662     },
19663
19664     // private (for BoxComponent)
19665     getPositionEl : function(){
19666         return this.wrap;
19667     },
19668
19669     // private
19670     initEvents : function(){
19671         this.originalValue = this.getValue();
19672     },
19673
19674 //    /**
19675 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19676 //     * @method
19677 //     */
19678 //    markInvalid : Roo.emptyFn,
19679 //    /**
19680 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
19681 //     * @method
19682 //     */
19683 //    clearInvalid : Roo.emptyFn,
19684
19685     setValue : function(v){
19686         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
19687         this.editorcore.pushValue();
19688     },
19689
19690      
19691     // private
19692     deferFocus : function(){
19693         this.focus.defer(10, this);
19694     },
19695
19696     // doc'ed in Field
19697     focus : function(){
19698         this.editorcore.focus();
19699         
19700     },
19701       
19702
19703     // private
19704     onDestroy : function(){
19705         
19706         
19707         
19708         if(this.rendered){
19709             
19710             for (var i =0; i < this.toolbars.length;i++) {
19711                 // fixme - ask toolbars for heights?
19712                 this.toolbars[i].onDestroy();
19713             }
19714             
19715             this.wrap.dom.innerHTML = '';
19716             this.wrap.remove();
19717         }
19718     },
19719
19720     // private
19721     onFirstFocus : function(){
19722         //Roo.log("onFirstFocus");
19723         this.editorcore.onFirstFocus();
19724          for (var i =0; i < this.toolbars.length;i++) {
19725             this.toolbars[i].onFirstFocus();
19726         }
19727         
19728     },
19729     
19730     // private
19731     syncValue : function()
19732     {   
19733         this.editorcore.syncValue();
19734     },
19735     
19736     pushValue : function()
19737     {   
19738         this.editorcore.pushValue();
19739     }
19740      
19741     
19742     // hide stuff that is not compatible
19743     /**
19744      * @event blur
19745      * @hide
19746      */
19747     /**
19748      * @event change
19749      * @hide
19750      */
19751     /**
19752      * @event focus
19753      * @hide
19754      */
19755     /**
19756      * @event specialkey
19757      * @hide
19758      */
19759     /**
19760      * @cfg {String} fieldClass @hide
19761      */
19762     /**
19763      * @cfg {String} focusClass @hide
19764      */
19765     /**
19766      * @cfg {String} autoCreate @hide
19767      */
19768     /**
19769      * @cfg {String} inputType @hide
19770      */
19771     /**
19772      * @cfg {String} invalidClass @hide
19773      */
19774     /**
19775      * @cfg {String} invalidText @hide
19776      */
19777     /**
19778      * @cfg {String} msgFx @hide
19779      */
19780     /**
19781      * @cfg {String} validateOnBlur @hide
19782      */
19783 });
19784  
19785     
19786    
19787    
19788    
19789       
19790 Roo.namespace('Roo.bootstrap.htmleditor');
19791 /**
19792  * @class Roo.bootstrap.HtmlEditorToolbar1
19793  * Basic Toolbar
19794  * 
19795  * Usage:
19796  *
19797  new Roo.bootstrap.HtmlEditor({
19798     ....
19799     toolbars : [
19800         new Roo.bootstrap.HtmlEditorToolbar1({
19801             disable : { fonts: 1 , format: 1, ..., ... , ...],
19802             btns : [ .... ]
19803         })
19804     }
19805      
19806  * 
19807  * @cfg {Object} disable List of elements to disable..
19808  * @cfg {Array} btns List of additional buttons.
19809  * 
19810  * 
19811  * NEEDS Extra CSS? 
19812  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
19813  */
19814  
19815 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
19816 {
19817     
19818     Roo.apply(this, config);
19819     
19820     // default disabled, based on 'good practice'..
19821     this.disable = this.disable || {};
19822     Roo.applyIf(this.disable, {
19823         fontSize : true,
19824         colors : true,
19825         specialElements : true
19826     });
19827     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
19828     
19829     this.editor = config.editor;
19830     this.editorcore = config.editor.editorcore;
19831     
19832     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
19833     
19834     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
19835     // dont call parent... till later.
19836 }
19837 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
19838      
19839     bar : true,
19840     
19841     editor : false,
19842     editorcore : false,
19843     
19844     
19845     formats : [
19846         "p" ,  
19847         "h1","h2","h3","h4","h5","h6", 
19848         "pre", "code", 
19849         "abbr", "acronym", "address", "cite", "samp", "var",
19850         'div','span'
19851     ],
19852     
19853     onRender : function(ct, position)
19854     {
19855        // Roo.log("Call onRender: " + this.xtype);
19856         
19857        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
19858        Roo.log(this.el);
19859        this.el.dom.style.marginBottom = '0';
19860        var _this = this;
19861        var editorcore = this.editorcore;
19862        var editor= this.editor;
19863        
19864        var children = [];
19865        var btn = function(id,cmd , toggle, handler){
19866        
19867             var  event = toggle ? 'toggle' : 'click';
19868        
19869             var a = {
19870                 size : 'sm',
19871                 xtype: 'Button',
19872                 xns: Roo.bootstrap,
19873                 glyphicon : id,
19874                 cmd : id || cmd,
19875                 enableToggle:toggle !== false,
19876                 //html : 'submit'
19877                 pressed : toggle ? false : null,
19878                 listeners : {}
19879             }
19880             a.listeners[toggle ? 'toggle' : 'click'] = function() {
19881                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
19882             }
19883             children.push(a);
19884             return a;
19885        }
19886         
19887         var style = {
19888                 xtype: 'Button',
19889                 size : 'sm',
19890                 xns: Roo.bootstrap,
19891                 glyphicon : 'font',
19892                 //html : 'submit'
19893                 menu : {
19894                     xtype: 'Menu',
19895                     xns: Roo.bootstrap,
19896                     items:  []
19897                 }
19898         };
19899         Roo.each(this.formats, function(f) {
19900             style.menu.items.push({
19901                 xtype :'MenuItem',
19902                 xns: Roo.bootstrap,
19903                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
19904                 tagname : f,
19905                 listeners : {
19906                     click : function()
19907                     {
19908                         editorcore.insertTag(this.tagname);
19909                         editor.focus();
19910                     }
19911                 }
19912                 
19913             });
19914         });
19915          children.push(style);   
19916             
19917             
19918         btn('bold',false,true);
19919         btn('italic',false,true);
19920         btn('align-left', 'justifyleft',true);
19921         btn('align-center', 'justifycenter',true);
19922         btn('align-right' , 'justifyright',true);
19923         btn('link', false, false, function(btn) {
19924             //Roo.log("create link?");
19925             var url = prompt(this.createLinkText, this.defaultLinkValue);
19926             if(url && url != 'http:/'+'/'){
19927                 this.editorcore.relayCmd('createlink', url);
19928             }
19929         }),
19930         btn('list','insertunorderedlist',true);
19931         btn('pencil', false,true, function(btn){
19932                 Roo.log(this);
19933                 
19934                 this.toggleSourceEdit(btn.pressed);
19935         });
19936         /*
19937         var cog = {
19938                 xtype: 'Button',
19939                 size : 'sm',
19940                 xns: Roo.bootstrap,
19941                 glyphicon : 'cog',
19942                 //html : 'submit'
19943                 menu : {
19944                     xtype: 'Menu',
19945                     xns: Roo.bootstrap,
19946                     items:  []
19947                 }
19948         };
19949         
19950         cog.menu.items.push({
19951             xtype :'MenuItem',
19952             xns: Roo.bootstrap,
19953             html : Clean styles,
19954             tagname : f,
19955             listeners : {
19956                 click : function()
19957                 {
19958                     editorcore.insertTag(this.tagname);
19959                     editor.focus();
19960                 }
19961             }
19962             
19963         });
19964        */
19965         
19966          
19967        this.xtype = 'NavSimplebar';
19968         
19969         for(var i=0;i< children.length;i++) {
19970             
19971             this.buttons.add(this.addxtypeChild(children[i]));
19972             
19973         }
19974         
19975         editor.on('editorevent', this.updateToolbar, this);
19976     },
19977     onBtnClick : function(id)
19978     {
19979        this.editorcore.relayCmd(id);
19980        this.editorcore.focus();
19981     },
19982     
19983     /**
19984      * Protected method that will not generally be called directly. It triggers
19985      * a toolbar update by reading the markup state of the current selection in the editor.
19986      */
19987     updateToolbar: function(){
19988
19989         if(!this.editorcore.activated){
19990             this.editor.onFirstFocus(); // is this neeed?
19991             return;
19992         }
19993
19994         var btns = this.buttons; 
19995         var doc = this.editorcore.doc;
19996         btns.get('bold').setActive(doc.queryCommandState('bold'));
19997         btns.get('italic').setActive(doc.queryCommandState('italic'));
19998         //btns.get('underline').setActive(doc.queryCommandState('underline'));
19999         
20000         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
20001         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
20002         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
20003         
20004         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
20005         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
20006          /*
20007         
20008         var ans = this.editorcore.getAllAncestors();
20009         if (this.formatCombo) {
20010             
20011             
20012             var store = this.formatCombo.store;
20013             this.formatCombo.setValue("");
20014             for (var i =0; i < ans.length;i++) {
20015                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
20016                     // select it..
20017                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
20018                     break;
20019                 }
20020             }
20021         }
20022         
20023         
20024         
20025         // hides menus... - so this cant be on a menu...
20026         Roo.bootstrap.MenuMgr.hideAll();
20027         */
20028         Roo.bootstrap.MenuMgr.hideAll();
20029         //this.editorsyncValue();
20030     },
20031     onFirstFocus: function() {
20032         this.buttons.each(function(item){
20033            item.enable();
20034         });
20035     },
20036     toggleSourceEdit : function(sourceEditMode){
20037         
20038           
20039         if(sourceEditMode){
20040             Roo.log("disabling buttons");
20041            this.buttons.each( function(item){
20042                 if(item.cmd != 'pencil'){
20043                     item.disable();
20044                 }
20045             });
20046           
20047         }else{
20048             Roo.log("enabling buttons");
20049             if(this.editorcore.initialized){
20050                 this.buttons.each( function(item){
20051                     item.enable();
20052                 });
20053             }
20054             
20055         }
20056         Roo.log("calling toggole on editor");
20057         // tell the editor that it's been pressed..
20058         this.editor.toggleSourceEdit(sourceEditMode);
20059        
20060     }
20061 });
20062
20063
20064
20065
20066
20067 /**
20068  * @class Roo.bootstrap.Table.AbstractSelectionModel
20069  * @extends Roo.util.Observable
20070  * Abstract base class for grid SelectionModels.  It provides the interface that should be
20071  * implemented by descendant classes.  This class should not be directly instantiated.
20072  * @constructor
20073  */
20074 Roo.bootstrap.Table.AbstractSelectionModel = function(){
20075     this.locked = false;
20076     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
20077 };
20078
20079
20080 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
20081     /** @ignore Called by the grid automatically. Do not call directly. */
20082     init : function(grid){
20083         this.grid = grid;
20084         this.initEvents();
20085     },
20086
20087     /**
20088      * Locks the selections.
20089      */
20090     lock : function(){
20091         this.locked = true;
20092     },
20093
20094     /**
20095      * Unlocks the selections.
20096      */
20097     unlock : function(){
20098         this.locked = false;
20099     },
20100
20101     /**
20102      * Returns true if the selections are locked.
20103      * @return {Boolean}
20104      */
20105     isLocked : function(){
20106         return this.locked;
20107     }
20108 });
20109 /**
20110  * @extends Roo.bootstrap.Table.AbstractSelectionModel
20111  * @class Roo.bootstrap.Table.RowSelectionModel
20112  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
20113  * It supports multiple selections and keyboard selection/navigation. 
20114  * @constructor
20115  * @param {Object} config
20116  */
20117
20118 Roo.bootstrap.Table.RowSelectionModel = function(config){
20119     Roo.apply(this, config);
20120     this.selections = new Roo.util.MixedCollection(false, function(o){
20121         return o.id;
20122     });
20123
20124     this.last = false;
20125     this.lastActive = false;
20126
20127     this.addEvents({
20128         /**
20129              * @event selectionchange
20130              * Fires when the selection changes
20131              * @param {SelectionModel} this
20132              */
20133             "selectionchange" : true,
20134         /**
20135              * @event afterselectionchange
20136              * Fires after the selection changes (eg. by key press or clicking)
20137              * @param {SelectionModel} this
20138              */
20139             "afterselectionchange" : true,
20140         /**
20141              * @event beforerowselect
20142              * Fires when a row is selected being selected, return false to cancel.
20143              * @param {SelectionModel} this
20144              * @param {Number} rowIndex The selected index
20145              * @param {Boolean} keepExisting False if other selections will be cleared
20146              */
20147             "beforerowselect" : true,
20148         /**
20149              * @event rowselect
20150              * Fires when a row is selected.
20151              * @param {SelectionModel} this
20152              * @param {Number} rowIndex The selected index
20153              * @param {Roo.data.Record} r The record
20154              */
20155             "rowselect" : true,
20156         /**
20157              * @event rowdeselect
20158              * Fires when a row is deselected.
20159              * @param {SelectionModel} this
20160              * @param {Number} rowIndex The selected index
20161              */
20162         "rowdeselect" : true
20163     });
20164     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
20165     this.locked = false;
20166 };
20167
20168 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
20169     /**
20170      * @cfg {Boolean} singleSelect
20171      * True to allow selection of only one row at a time (defaults to false)
20172      */
20173     singleSelect : false,
20174
20175     // private
20176     initEvents : function(){
20177
20178         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
20179             this.grid.on("mousedown", this.handleMouseDown, this);
20180         }else{ // allow click to work like normal
20181             this.grid.on("rowclick", this.handleDragableRowClick, this);
20182         }
20183
20184         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
20185             "up" : function(e){
20186                 if(!e.shiftKey){
20187                     this.selectPrevious(e.shiftKey);
20188                 }else if(this.last !== false && this.lastActive !== false){
20189                     var last = this.last;
20190                     this.selectRange(this.last,  this.lastActive-1);
20191                     this.grid.getView().focusRow(this.lastActive);
20192                     if(last !== false){
20193                         this.last = last;
20194                     }
20195                 }else{
20196                     this.selectFirstRow();
20197                 }
20198                 this.fireEvent("afterselectionchange", this);
20199             },
20200             "down" : function(e){
20201                 if(!e.shiftKey){
20202                     this.selectNext(e.shiftKey);
20203                 }else if(this.last !== false && this.lastActive !== false){
20204                     var last = this.last;
20205                     this.selectRange(this.last,  this.lastActive+1);
20206                     this.grid.getView().focusRow(this.lastActive);
20207                     if(last !== false){
20208                         this.last = last;
20209                     }
20210                 }else{
20211                     this.selectFirstRow();
20212                 }
20213                 this.fireEvent("afterselectionchange", this);
20214             },
20215             scope: this
20216         });
20217
20218         var view = this.grid.view;
20219         view.on("refresh", this.onRefresh, this);
20220         view.on("rowupdated", this.onRowUpdated, this);
20221         view.on("rowremoved", this.onRemove, this);
20222     },
20223
20224     // private
20225     onRefresh : function(){
20226         var ds = this.grid.dataSource, i, v = this.grid.view;
20227         var s = this.selections;
20228         s.each(function(r){
20229             if((i = ds.indexOfId(r.id)) != -1){
20230                 v.onRowSelect(i);
20231             }else{
20232                 s.remove(r);
20233             }
20234         });
20235     },
20236
20237     // private
20238     onRemove : function(v, index, r){
20239         this.selections.remove(r);
20240     },
20241
20242     // private
20243     onRowUpdated : function(v, index, r){
20244         if(this.isSelected(r)){
20245             v.onRowSelect(index);
20246         }
20247     },
20248
20249     /**
20250      * Select records.
20251      * @param {Array} records The records to select
20252      * @param {Boolean} keepExisting (optional) True to keep existing selections
20253      */
20254     selectRecords : function(records, keepExisting){
20255         if(!keepExisting){
20256             this.clearSelections();
20257         }
20258         var ds = this.grid.dataSource;
20259         for(var i = 0, len = records.length; i < len; i++){
20260             this.selectRow(ds.indexOf(records[i]), true);
20261         }
20262     },
20263
20264     /**
20265      * Gets the number of selected rows.
20266      * @return {Number}
20267      */
20268     getCount : function(){
20269         return this.selections.length;
20270     },
20271
20272     /**
20273      * Selects the first row in the grid.
20274      */
20275     selectFirstRow : function(){
20276         this.selectRow(0);
20277     },
20278
20279     /**
20280      * Select the last row.
20281      * @param {Boolean} keepExisting (optional) True to keep existing selections
20282      */
20283     selectLastRow : function(keepExisting){
20284         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
20285     },
20286
20287     /**
20288      * Selects the row immediately following the last selected row.
20289      * @param {Boolean} keepExisting (optional) True to keep existing selections
20290      */
20291     selectNext : function(keepExisting){
20292         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
20293             this.selectRow(this.last+1, keepExisting);
20294             this.grid.getView().focusRow(this.last);
20295         }
20296     },
20297
20298     /**
20299      * Selects the row that precedes the last selected row.
20300      * @param {Boolean} keepExisting (optional) True to keep existing selections
20301      */
20302     selectPrevious : function(keepExisting){
20303         if(this.last){
20304             this.selectRow(this.last-1, keepExisting);
20305             this.grid.getView().focusRow(this.last);
20306         }
20307     },
20308
20309     /**
20310      * Returns the selected records
20311      * @return {Array} Array of selected records
20312      */
20313     getSelections : function(){
20314         return [].concat(this.selections.items);
20315     },
20316
20317     /**
20318      * Returns the first selected record.
20319      * @return {Record}
20320      */
20321     getSelected : function(){
20322         return this.selections.itemAt(0);
20323     },
20324
20325
20326     /**
20327      * Clears all selections.
20328      */
20329     clearSelections : function(fast){
20330         if(this.locked) return;
20331         if(fast !== true){
20332             var ds = this.grid.dataSource;
20333             var s = this.selections;
20334             s.each(function(r){
20335                 this.deselectRow(ds.indexOfId(r.id));
20336             }, this);
20337             s.clear();
20338         }else{
20339             this.selections.clear();
20340         }
20341         this.last = false;
20342     },
20343
20344
20345     /**
20346      * Selects all rows.
20347      */
20348     selectAll : function(){
20349         if(this.locked) return;
20350         this.selections.clear();
20351         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
20352             this.selectRow(i, true);
20353         }
20354     },
20355
20356     /**
20357      * Returns True if there is a selection.
20358      * @return {Boolean}
20359      */
20360     hasSelection : function(){
20361         return this.selections.length > 0;
20362     },
20363
20364     /**
20365      * Returns True if the specified row is selected.
20366      * @param {Number/Record} record The record or index of the record to check
20367      * @return {Boolean}
20368      */
20369     isSelected : function(index){
20370         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
20371         return (r && this.selections.key(r.id) ? true : false);
20372     },
20373
20374     /**
20375      * Returns True if the specified record id is selected.
20376      * @param {String} id The id of record to check
20377      * @return {Boolean}
20378      */
20379     isIdSelected : function(id){
20380         return (this.selections.key(id) ? true : false);
20381     },
20382
20383     // private
20384     handleMouseDown : function(e, t){
20385         var view = this.grid.getView(), rowIndex;
20386         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
20387             return;
20388         };
20389         if(e.shiftKey && this.last !== false){
20390             var last = this.last;
20391             this.selectRange(last, rowIndex, e.ctrlKey);
20392             this.last = last; // reset the last
20393             view.focusRow(rowIndex);
20394         }else{
20395             var isSelected = this.isSelected(rowIndex);
20396             if(e.button !== 0 && isSelected){
20397                 view.focusRow(rowIndex);
20398             }else if(e.ctrlKey && isSelected){
20399                 this.deselectRow(rowIndex);
20400             }else if(!isSelected){
20401                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
20402                 view.focusRow(rowIndex);
20403             }
20404         }
20405         this.fireEvent("afterselectionchange", this);
20406     },
20407     // private
20408     handleDragableRowClick :  function(grid, rowIndex, e) 
20409     {
20410         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
20411             this.selectRow(rowIndex, false);
20412             grid.view.focusRow(rowIndex);
20413              this.fireEvent("afterselectionchange", this);
20414         }
20415     },
20416     
20417     /**
20418      * Selects multiple rows.
20419      * @param {Array} rows Array of the indexes of the row to select
20420      * @param {Boolean} keepExisting (optional) True to keep existing selections
20421      */
20422     selectRows : function(rows, keepExisting){
20423         if(!keepExisting){
20424             this.clearSelections();
20425         }
20426         for(var i = 0, len = rows.length; i < len; i++){
20427             this.selectRow(rows[i], true);
20428         }
20429     },
20430
20431     /**
20432      * Selects a range of rows. All rows in between startRow and endRow are also selected.
20433      * @param {Number} startRow The index of the first row in the range
20434      * @param {Number} endRow The index of the last row in the range
20435      * @param {Boolean} keepExisting (optional) True to retain existing selections
20436      */
20437     selectRange : function(startRow, endRow, keepExisting){
20438         if(this.locked) return;
20439         if(!keepExisting){
20440             this.clearSelections();
20441         }
20442         if(startRow <= endRow){
20443             for(var i = startRow; i <= endRow; i++){
20444                 this.selectRow(i, true);
20445             }
20446         }else{
20447             for(var i = startRow; i >= endRow; i--){
20448                 this.selectRow(i, true);
20449             }
20450         }
20451     },
20452
20453     /**
20454      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
20455      * @param {Number} startRow The index of the first row in the range
20456      * @param {Number} endRow The index of the last row in the range
20457      */
20458     deselectRange : function(startRow, endRow, preventViewNotify){
20459         if(this.locked) return;
20460         for(var i = startRow; i <= endRow; i++){
20461             this.deselectRow(i, preventViewNotify);
20462         }
20463     },
20464
20465     /**
20466      * Selects a row.
20467      * @param {Number} row The index of the row to select
20468      * @param {Boolean} keepExisting (optional) True to keep existing selections
20469      */
20470     selectRow : function(index, keepExisting, preventViewNotify){
20471         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
20472         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
20473             if(!keepExisting || this.singleSelect){
20474                 this.clearSelections();
20475             }
20476             var r = this.grid.dataSource.getAt(index);
20477             this.selections.add(r);
20478             this.last = this.lastActive = index;
20479             if(!preventViewNotify){
20480                 this.grid.getView().onRowSelect(index);
20481             }
20482             this.fireEvent("rowselect", this, index, r);
20483             this.fireEvent("selectionchange", this);
20484         }
20485     },
20486
20487     /**
20488      * Deselects a row.
20489      * @param {Number} row The index of the row to deselect
20490      */
20491     deselectRow : function(index, preventViewNotify){
20492         if(this.locked) return;
20493         if(this.last == index){
20494             this.last = false;
20495         }
20496         if(this.lastActive == index){
20497             this.lastActive = false;
20498         }
20499         var r = this.grid.dataSource.getAt(index);
20500         this.selections.remove(r);
20501         if(!preventViewNotify){
20502             this.grid.getView().onRowDeselect(index);
20503         }
20504         this.fireEvent("rowdeselect", this, index);
20505         this.fireEvent("selectionchange", this);
20506     },
20507
20508     // private
20509     restoreLast : function(){
20510         if(this._last){
20511             this.last = this._last;
20512         }
20513     },
20514
20515     // private
20516     acceptsNav : function(row, col, cm){
20517         return !cm.isHidden(col) && cm.isCellEditable(col, row);
20518     },
20519
20520     // private
20521     onEditorKey : function(field, e){
20522         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
20523         if(k == e.TAB){
20524             e.stopEvent();
20525             ed.completeEdit();
20526             if(e.shiftKey){
20527                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
20528             }else{
20529                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
20530             }
20531         }else if(k == e.ENTER && !e.ctrlKey){
20532             e.stopEvent();
20533             ed.completeEdit();
20534             if(e.shiftKey){
20535                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
20536             }else{
20537                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
20538             }
20539         }else if(k == e.ESC){
20540             ed.cancelEdit();
20541         }
20542         if(newCell){
20543             g.startEditing(newCell[0], newCell[1]);
20544         }
20545     }
20546 });/*
20547  * Based on:
20548  * Ext JS Library 1.1.1
20549  * Copyright(c) 2006-2007, Ext JS, LLC.
20550  *
20551  * Originally Released Under LGPL - original licence link has changed is not relivant.
20552  *
20553  * Fork - LGPL
20554  * <script type="text/javascript">
20555  */
20556  
20557 /**
20558  * @class Roo.bootstrap.PagingToolbar
20559  * @extends Roo.Row
20560  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
20561  * @constructor
20562  * Create a new PagingToolbar
20563  * @param {Object} config The config object
20564  */
20565 Roo.bootstrap.PagingToolbar = function(config)
20566 {
20567     // old args format still supported... - xtype is prefered..
20568         // created from xtype...
20569     var ds = config.dataSource;
20570     this.toolbarItems = [];
20571     if (config.items) {
20572         this.toolbarItems = config.items;
20573 //        config.items = [];
20574     }
20575     
20576     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
20577     this.ds = ds;
20578     this.cursor = 0;
20579     if (ds) { 
20580         this.bind(ds);
20581     }
20582     
20583     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
20584     
20585 };
20586
20587 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
20588     /**
20589      * @cfg {Roo.data.Store} dataSource
20590      * The underlying data store providing the paged data
20591      */
20592     /**
20593      * @cfg {String/HTMLElement/Element} container
20594      * container The id or element that will contain the toolbar
20595      */
20596     /**
20597      * @cfg {Boolean} displayInfo
20598      * True to display the displayMsg (defaults to false)
20599      */
20600     /**
20601      * @cfg {Number} pageSize
20602      * The number of records to display per page (defaults to 20)
20603      */
20604     pageSize: 20,
20605     /**
20606      * @cfg {String} displayMsg
20607      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
20608      */
20609     displayMsg : 'Displaying {0} - {1} of {2}',
20610     /**
20611      * @cfg {String} emptyMsg
20612      * The message to display when no records are found (defaults to "No data to display")
20613      */
20614     emptyMsg : 'No data to display',
20615     /**
20616      * Customizable piece of the default paging text (defaults to "Page")
20617      * @type String
20618      */
20619     beforePageText : "Page",
20620     /**
20621      * Customizable piece of the default paging text (defaults to "of %0")
20622      * @type String
20623      */
20624     afterPageText : "of {0}",
20625     /**
20626      * Customizable piece of the default paging text (defaults to "First Page")
20627      * @type String
20628      */
20629     firstText : "First Page",
20630     /**
20631      * Customizable piece of the default paging text (defaults to "Previous Page")
20632      * @type String
20633      */
20634     prevText : "Previous Page",
20635     /**
20636      * Customizable piece of the default paging text (defaults to "Next Page")
20637      * @type String
20638      */
20639     nextText : "Next Page",
20640     /**
20641      * Customizable piece of the default paging text (defaults to "Last Page")
20642      * @type String
20643      */
20644     lastText : "Last Page",
20645     /**
20646      * Customizable piece of the default paging text (defaults to "Refresh")
20647      * @type String
20648      */
20649     refreshText : "Refresh",
20650
20651     buttons : false,
20652     // private
20653     onRender : function(ct, position) 
20654     {
20655         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
20656         this.navgroup.parentId = this.id;
20657         this.navgroup.onRender(this.el, null);
20658         // add the buttons to the navgroup
20659         
20660         if(this.displayInfo){
20661             Roo.log(this.el.select('ul.navbar-nav',true).first());
20662             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
20663             this.displayEl = this.el.select('.x-paging-info', true).first();
20664 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
20665 //            this.displayEl = navel.el.select('span',true).first();
20666         }
20667         
20668         var _this = this;
20669         
20670         if(this.buttons){
20671             Roo.each(_this.buttons, function(e){
20672                Roo.factory(e).onRender(_this.el, null);
20673             });
20674         }
20675             
20676         Roo.each(_this.toolbarItems, function(e) {
20677             _this.navgroup.addItem(e);
20678         });
20679         
20680         
20681         this.first = this.navgroup.addItem({
20682             tooltip: this.firstText,
20683             cls: "prev",
20684             icon : 'fa fa-backward',
20685             disabled: true,
20686             preventDefault: true,
20687             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
20688         });
20689         
20690         this.prev =  this.navgroup.addItem({
20691             tooltip: this.prevText,
20692             cls: "prev",
20693             icon : 'fa fa-step-backward',
20694             disabled: true,
20695             preventDefault: true,
20696             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
20697         });
20698     //this.addSeparator();
20699         
20700         
20701         var field = this.navgroup.addItem( {
20702             tagtype : 'span',
20703             cls : 'x-paging-position',
20704             
20705             html : this.beforePageText  +
20706                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
20707                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
20708          } ); //?? escaped?
20709         
20710         this.field = field.el.select('input', true).first();
20711         this.field.on("keydown", this.onPagingKeydown, this);
20712         this.field.on("focus", function(){this.dom.select();});
20713     
20714     
20715         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
20716         //this.field.setHeight(18);
20717         //this.addSeparator();
20718         this.next = this.navgroup.addItem({
20719             tooltip: this.nextText,
20720             cls: "next",
20721             html : ' <i class="fa fa-step-forward">',
20722             disabled: true,
20723             preventDefault: true,
20724             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
20725         });
20726         this.last = this.navgroup.addItem({
20727             tooltip: this.lastText,
20728             icon : 'fa fa-forward',
20729             cls: "next",
20730             disabled: true,
20731             preventDefault: true,
20732             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
20733         });
20734     //this.addSeparator();
20735         this.loading = this.navgroup.addItem({
20736             tooltip: this.refreshText,
20737             icon: 'fa fa-refresh',
20738             preventDefault: true,
20739             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
20740         });
20741
20742     },
20743
20744     // private
20745     updateInfo : function(){
20746         if(this.displayEl){
20747             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
20748             var msg = count == 0 ?
20749                 this.emptyMsg :
20750                 String.format(
20751                     this.displayMsg,
20752                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
20753                 );
20754             this.displayEl.update(msg);
20755         }
20756     },
20757
20758     // private
20759     onLoad : function(ds, r, o){
20760        this.cursor = o.params ? o.params.start : 0;
20761        var d = this.getPageData(),
20762             ap = d.activePage,
20763             ps = d.pages;
20764         
20765        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
20766        this.field.dom.value = ap;
20767        this.first.setDisabled(ap == 1);
20768        this.prev.setDisabled(ap == 1);
20769        this.next.setDisabled(ap == ps);
20770        this.last.setDisabled(ap == ps);
20771        this.loading.enable();
20772        this.updateInfo();
20773     },
20774
20775     // private
20776     getPageData : function(){
20777         var total = this.ds.getTotalCount();
20778         return {
20779             total : total,
20780             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
20781             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
20782         };
20783     },
20784
20785     // private
20786     onLoadError : function(){
20787         this.loading.enable();
20788     },
20789
20790     // private
20791     onPagingKeydown : function(e){
20792         var k = e.getKey();
20793         var d = this.getPageData();
20794         if(k == e.RETURN){
20795             var v = this.field.dom.value, pageNum;
20796             if(!v || isNaN(pageNum = parseInt(v, 10))){
20797                 this.field.dom.value = d.activePage;
20798                 return;
20799             }
20800             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
20801             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20802             e.stopEvent();
20803         }
20804         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))
20805         {
20806           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
20807           this.field.dom.value = pageNum;
20808           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
20809           e.stopEvent();
20810         }
20811         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20812         {
20813           var v = this.field.dom.value, pageNum; 
20814           var increment = (e.shiftKey) ? 10 : 1;
20815           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
20816             increment *= -1;
20817           if(!v || isNaN(pageNum = parseInt(v, 10))) {
20818             this.field.dom.value = d.activePage;
20819             return;
20820           }
20821           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
20822           {
20823             this.field.dom.value = parseInt(v, 10) + increment;
20824             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
20825             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
20826           }
20827           e.stopEvent();
20828         }
20829     },
20830
20831     // private
20832     beforeLoad : function(){
20833         if(this.loading){
20834             this.loading.disable();
20835         }
20836     },
20837
20838     // private
20839     onClick : function(which){
20840         
20841         var ds = this.ds;
20842         if (!ds) {
20843             return;
20844         }
20845         
20846         switch(which){
20847             case "first":
20848                 ds.load({params:{start: 0, limit: this.pageSize}});
20849             break;
20850             case "prev":
20851                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
20852             break;
20853             case "next":
20854                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
20855             break;
20856             case "last":
20857                 var total = ds.getTotalCount();
20858                 var extra = total % this.pageSize;
20859                 var lastStart = extra ? (total - extra) : total-this.pageSize;
20860                 ds.load({params:{start: lastStart, limit: this.pageSize}});
20861             break;
20862             case "refresh":
20863                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
20864             break;
20865         }
20866     },
20867
20868     /**
20869      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
20870      * @param {Roo.data.Store} store The data store to unbind
20871      */
20872     unbind : function(ds){
20873         ds.un("beforeload", this.beforeLoad, this);
20874         ds.un("load", this.onLoad, this);
20875         ds.un("loadexception", this.onLoadError, this);
20876         ds.un("remove", this.updateInfo, this);
20877         ds.un("add", this.updateInfo, this);
20878         this.ds = undefined;
20879     },
20880
20881     /**
20882      * Binds the paging toolbar to the specified {@link Roo.data.Store}
20883      * @param {Roo.data.Store} store The data store to bind
20884      */
20885     bind : function(ds){
20886         ds.on("beforeload", this.beforeLoad, this);
20887         ds.on("load", this.onLoad, this);
20888         ds.on("loadexception", this.onLoadError, this);
20889         ds.on("remove", this.updateInfo, this);
20890         ds.on("add", this.updateInfo, this);
20891         this.ds = ds;
20892     }
20893 });/*
20894  * - LGPL
20895  *
20896  * element
20897  * 
20898  */
20899
20900 /**
20901  * @class Roo.bootstrap.MessageBar
20902  * @extends Roo.bootstrap.Component
20903  * Bootstrap MessageBar class
20904  * @cfg {String} html contents of the MessageBar
20905  * @cfg {String} weight (info | success | warning | danger) default info
20906  * @cfg {String} beforeClass insert the bar before the given class
20907  * @cfg {Boolean} closable (true | false) default false
20908  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
20909  * 
20910  * @constructor
20911  * Create a new Element
20912  * @param {Object} config The config object
20913  */
20914
20915 Roo.bootstrap.MessageBar = function(config){
20916     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
20917 };
20918
20919 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
20920     
20921     html: '',
20922     weight: 'info',
20923     closable: false,
20924     fixed: false,
20925     beforeClass: 'bootstrap-sticky-wrap',
20926     
20927     getAutoCreate : function(){
20928         
20929         var cfg = {
20930             tag: 'div',
20931             cls: 'alert alert-dismissable alert-' + this.weight,
20932             cn: [
20933                 {
20934                     tag: 'span',
20935                     cls: 'message',
20936                     html: this.html || ''
20937                 }
20938             ]
20939         }
20940         
20941         if(this.fixed){
20942             cfg.cls += ' alert-messages-fixed';
20943         }
20944         
20945         if(this.closable){
20946             cfg.cn.push({
20947                 tag: 'button',
20948                 cls: 'close',
20949                 html: 'x'
20950             });
20951         }
20952         
20953         return cfg;
20954     },
20955     
20956     onRender : function(ct, position)
20957     {
20958         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20959         
20960         if(!this.el){
20961             var cfg = Roo.apply({},  this.getAutoCreate());
20962             cfg.id = Roo.id();
20963             
20964             if (this.cls) {
20965                 cfg.cls += ' ' + this.cls;
20966             }
20967             if (this.style) {
20968                 cfg.style = this.style;
20969             }
20970             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
20971             
20972             this.el.setVisibilityMode(Roo.Element.DISPLAY);
20973         }
20974         
20975         this.el.select('>button.close').on('click', this.hide, this);
20976         
20977     },
20978     
20979     show : function()
20980     {
20981         if (!this.rendered) {
20982             this.render();
20983         }
20984         
20985         this.el.show();
20986         
20987         this.fireEvent('show', this);
20988         
20989     },
20990     
20991     hide : function()
20992     {
20993         if (!this.rendered) {
20994             this.render();
20995         }
20996         
20997         this.el.hide();
20998         
20999         this.fireEvent('hide', this);
21000     },
21001     
21002     update : function()
21003     {
21004 //        var e = this.el.dom.firstChild;
21005 //        
21006 //        if(this.closable){
21007 //            e = e.nextSibling;
21008 //        }
21009 //        
21010 //        e.data = this.html || '';
21011
21012         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
21013     }
21014    
21015 });
21016
21017  
21018
21019      /*
21020  * - LGPL
21021  *
21022  * Graph
21023  * 
21024  */
21025
21026
21027 /**
21028  * @class Roo.bootstrap.Graph
21029  * @extends Roo.bootstrap.Component
21030  * Bootstrap Graph class
21031 > Prameters
21032  -sm {number} sm 4
21033  -md {number} md 5
21034  @cfg {String} graphtype  bar | vbar | pie
21035  @cfg {number} g_x coodinator | centre x (pie)
21036  @cfg {number} g_y coodinator | centre y (pie)
21037  @cfg {number} g_r radius (pie)
21038  @cfg {number} g_height height of the chart (respected by all elements in the set)
21039  @cfg {number} g_width width of the chart (respected by all elements in the set)
21040  @cfg {Object} title The title of the chart
21041     
21042  -{Array}  values
21043  -opts (object) options for the chart 
21044      o {
21045      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
21046      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
21047      o vgutter (number)
21048      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.
21049      o stacked (boolean) whether or not to tread values as in a stacked bar chart
21050      o to
21051      o stretch (boolean)
21052      o }
21053  -opts (object) options for the pie
21054      o{
21055      o cut
21056      o startAngle (number)
21057      o endAngle (number)
21058      } 
21059  *
21060  * @constructor
21061  * Create a new Input
21062  * @param {Object} config The config object
21063  */
21064
21065 Roo.bootstrap.Graph = function(config){
21066     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
21067     
21068     this.addEvents({
21069         // img events
21070         /**
21071          * @event click
21072          * The img click event for the img.
21073          * @param {Roo.EventObject} e
21074          */
21075         "click" : true
21076     });
21077 };
21078
21079 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
21080     
21081     sm: 4,
21082     md: 5,
21083     graphtype: 'bar',
21084     g_height: 250,
21085     g_width: 400,
21086     g_x: 50,
21087     g_y: 50,
21088     g_r: 30,
21089     opts:{
21090         //g_colors: this.colors,
21091         g_type: 'soft',
21092         g_gutter: '20%'
21093
21094     },
21095     title : false,
21096
21097     getAutoCreate : function(){
21098         
21099         var cfg = {
21100             tag: 'div',
21101             html : null
21102         }
21103         
21104         
21105         return  cfg;
21106     },
21107
21108     onRender : function(ct,position){
21109         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
21110         this.raphael = Raphael(this.el.dom);
21111         
21112                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21113                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21114                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
21115                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
21116                 /*
21117                 r.text(160, 10, "Single Series Chart").attr(txtattr);
21118                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
21119                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
21120                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
21121                 
21122                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
21123                 r.barchart(330, 10, 300, 220, data1);
21124                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
21125                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
21126                 */
21127                 
21128                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21129                 // r.barchart(30, 30, 560, 250,  xdata, {
21130                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
21131                 //     axis : "0 0 1 1",
21132                 //     axisxlabels :  xdata
21133                 //     //yvalues : cols,
21134                    
21135                 // });
21136 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
21137 //        
21138 //        this.load(null,xdata,{
21139 //                axis : "0 0 1 1",
21140 //                axisxlabels :  xdata
21141 //                });
21142
21143     },
21144
21145     load : function(graphtype,xdata,opts){
21146         this.raphael.clear();
21147         if(!graphtype) {
21148             graphtype = this.graphtype;
21149         }
21150         if(!opts){
21151             opts = this.opts;
21152         }
21153         var r = this.raphael,
21154             fin = function () {
21155                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
21156             },
21157             fout = function () {
21158                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
21159             },
21160             pfin = function() {
21161                 this.sector.stop();
21162                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
21163
21164                 if (this.label) {
21165                     this.label[0].stop();
21166                     this.label[0].attr({ r: 7.5 });
21167                     this.label[1].attr({ "font-weight": 800 });
21168                 }
21169             },
21170             pfout = function() {
21171                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
21172
21173                 if (this.label) {
21174                     this.label[0].animate({ r: 5 }, 500, "bounce");
21175                     this.label[1].attr({ "font-weight": 400 });
21176                 }
21177             };
21178
21179         switch(graphtype){
21180             case 'bar':
21181                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21182                 break;
21183             case 'hbar':
21184                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
21185                 break;
21186             case 'pie':
21187 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
21188 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
21189 //            
21190                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
21191                 
21192                 break;
21193
21194         }
21195         
21196         if(this.title){
21197             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
21198         }
21199         
21200     },
21201     
21202     setTitle: function(o)
21203     {
21204         this.title = o;
21205     },
21206     
21207     initEvents: function() {
21208         
21209         if(!this.href){
21210             this.el.on('click', this.onClick, this);
21211         }
21212     },
21213     
21214     onClick : function(e)
21215     {
21216         Roo.log('img onclick');
21217         this.fireEvent('click', this, e);
21218     }
21219    
21220 });
21221
21222  
21223 /*
21224  * - LGPL
21225  *
21226  * numberBox
21227  * 
21228  */
21229 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21230
21231 /**
21232  * @class Roo.bootstrap.dash.NumberBox
21233  * @extends Roo.bootstrap.Component
21234  * Bootstrap NumberBox class
21235  * @cfg {String} headline Box headline
21236  * @cfg {String} content Box content
21237  * @cfg {String} icon Box icon
21238  * @cfg {String} footer Footer text
21239  * @cfg {String} fhref Footer href
21240  * 
21241  * @constructor
21242  * Create a new NumberBox
21243  * @param {Object} config The config object
21244  */
21245
21246
21247 Roo.bootstrap.dash.NumberBox = function(config){
21248     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
21249     
21250 };
21251
21252 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
21253     
21254     headline : '',
21255     content : '',
21256     icon : '',
21257     footer : '',
21258     fhref : '',
21259     ficon : '',
21260     
21261     getAutoCreate : function(){
21262         
21263         var cfg = {
21264             tag : 'div',
21265             cls : 'small-box ',
21266             cn : [
21267                 {
21268                     tag : 'div',
21269                     cls : 'inner',
21270                     cn :[
21271                         {
21272                             tag : 'h3',
21273                             cls : 'roo-headline',
21274                             html : this.headline
21275                         },
21276                         {
21277                             tag : 'p',
21278                             cls : 'roo-content',
21279                             html : this.content
21280                         }
21281                     ]
21282                 }
21283             ]
21284         }
21285         
21286         if(this.icon){
21287             cfg.cn.push({
21288                 tag : 'div',
21289                 cls : 'icon',
21290                 cn :[
21291                     {
21292                         tag : 'i',
21293                         cls : 'ion ' + this.icon
21294                     }
21295                 ]
21296             });
21297         }
21298         
21299         if(this.footer){
21300             var footer = {
21301                 tag : 'a',
21302                 cls : 'small-box-footer',
21303                 href : this.fhref || '#',
21304                 html : this.footer
21305             };
21306             
21307             cfg.cn.push(footer);
21308             
21309         }
21310         
21311         return  cfg;
21312     },
21313
21314     onRender : function(ct,position){
21315         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
21316
21317
21318        
21319                 
21320     },
21321
21322     setHeadline: function (value)
21323     {
21324         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
21325     },
21326     
21327     setFooter: function (value, href)
21328     {
21329         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
21330         
21331         if(href){
21332             this.el.select('a.small-box-footer',true).first().attr('href', href);
21333         }
21334         
21335     },
21336
21337     setContent: function (value)
21338     {
21339         this.el.select('.roo-content',true).first().dom.innerHTML = value;
21340     },
21341
21342     initEvents: function() 
21343     {   
21344         
21345     }
21346     
21347 });
21348
21349  
21350 /*
21351  * - LGPL
21352  *
21353  * TabBox
21354  * 
21355  */
21356 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21357
21358 /**
21359  * @class Roo.bootstrap.dash.TabBox
21360  * @extends Roo.bootstrap.Component
21361  * Bootstrap TabBox class
21362  * @cfg {String} title Title of the TabBox
21363  * @cfg {String} icon Icon of the TabBox
21364  * @cfg {Boolean} showtabs (true|false) show the tabs default true
21365  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
21366  * 
21367  * @constructor
21368  * Create a new TabBox
21369  * @param {Object} config The config object
21370  */
21371
21372
21373 Roo.bootstrap.dash.TabBox = function(config){
21374     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
21375     this.addEvents({
21376         // raw events
21377         /**
21378          * @event addpane
21379          * When a pane is added
21380          * @param {Roo.bootstrap.dash.TabPane} pane
21381          */
21382         "addpane" : true,
21383         /**
21384          * @event activatepane
21385          * When a pane is activated
21386          * @param {Roo.bootstrap.dash.TabPane} pane
21387          */
21388         "activatepane" : true
21389         
21390          
21391     });
21392     
21393     this.panes = [];
21394 };
21395
21396 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
21397
21398     title : '',
21399     icon : false,
21400     showtabs : true,
21401     tabScrollable : false,
21402     
21403     getChildContainer : function()
21404     {
21405         return this.el.select('.tab-content', true).first();
21406     },
21407     
21408     getAutoCreate : function(){
21409         
21410         var header = {
21411             tag: 'li',
21412             cls: 'pull-left header',
21413             html: this.title,
21414             cn : []
21415         };
21416         
21417         if(this.icon){
21418             header.cn.push({
21419                 tag: 'i',
21420                 cls: 'fa ' + this.icon
21421             });
21422         }
21423         
21424         var h = {
21425             tag: 'ul',
21426             cls: 'nav nav-tabs pull-right',
21427             cn: [
21428                 header
21429             ]
21430         };
21431         
21432         if(this.tabScrollable){
21433             h = {
21434                 tag: 'div',
21435                 cls: 'tab-header',
21436                 cn: [
21437                     {
21438                         tag: 'ul',
21439                         cls: 'nav nav-tabs pull-right',
21440                         cn: [
21441                             header
21442                         ]
21443                     }
21444                 ]
21445             }
21446         }
21447         
21448         var cfg = {
21449             tag: 'div',
21450             cls: 'nav-tabs-custom',
21451             cn: [
21452                 h,
21453                 {
21454                     tag: 'div',
21455                     cls: 'tab-content no-padding',
21456                     cn: []
21457                 }
21458             ]
21459         }
21460
21461         return  cfg;
21462     },
21463     initEvents : function()
21464     {
21465         //Roo.log('add add pane handler');
21466         this.on('addpane', this.onAddPane, this);
21467     },
21468      /**
21469      * Updates the box title
21470      * @param {String} html to set the title to.
21471      */
21472     setTitle : function(value)
21473     {
21474         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
21475     },
21476     onAddPane : function(pane)
21477     {
21478         this.panes.push(pane);
21479         //Roo.log('addpane');
21480         //Roo.log(pane);
21481         // tabs are rendere left to right..
21482         if(!this.showtabs){
21483             return;
21484         }
21485         
21486         var ctr = this.el.select('.nav-tabs', true).first();
21487          
21488          
21489         var existing = ctr.select('.nav-tab',true);
21490         var qty = existing.getCount();;
21491         
21492         
21493         var tab = ctr.createChild({
21494             tag : 'li',
21495             cls : 'nav-tab' + (qty ? '' : ' active'),
21496             cn : [
21497                 {
21498                     tag : 'a',
21499                     href:'#',
21500                     html : pane.title
21501                 }
21502             ]
21503         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
21504         pane.tab = tab;
21505         
21506         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
21507         if (!qty) {
21508             pane.el.addClass('active');
21509         }
21510         
21511                 
21512     },
21513     onTabClick : function(ev,un,ob,pane)
21514     {
21515         //Roo.log('tab - prev default');
21516         ev.preventDefault();
21517         
21518         
21519         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
21520         pane.tab.addClass('active');
21521         //Roo.log(pane.title);
21522         this.getChildContainer().select('.tab-pane',true).removeClass('active');
21523         // technically we should have a deactivate event.. but maybe add later.
21524         // and it should not de-activate the selected tab...
21525         this.fireEvent('activatepane', pane);
21526         pane.el.addClass('active');
21527         pane.fireEvent('activate');
21528         
21529         
21530     },
21531     
21532     getActivePane : function()
21533     {
21534         var r = false;
21535         Roo.each(this.panes, function(p) {
21536             if(p.el.hasClass('active')){
21537                 r = p;
21538                 return false;
21539             }
21540             
21541             return;
21542         });
21543         
21544         return r;
21545     }
21546     
21547     
21548 });
21549
21550  
21551 /*
21552  * - LGPL
21553  *
21554  * Tab pane
21555  * 
21556  */
21557 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
21558 /**
21559  * @class Roo.bootstrap.TabPane
21560  * @extends Roo.bootstrap.Component
21561  * Bootstrap TabPane class
21562  * @cfg {Boolean} active (false | true) Default false
21563  * @cfg {String} title title of panel
21564
21565  * 
21566  * @constructor
21567  * Create a new TabPane
21568  * @param {Object} config The config object
21569  */
21570
21571 Roo.bootstrap.dash.TabPane = function(config){
21572     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
21573     
21574     this.addEvents({
21575         // raw events
21576         /**
21577          * @event activate
21578          * When a pane is activated
21579          * @param {Roo.bootstrap.dash.TabPane} pane
21580          */
21581         "activate" : true
21582          
21583     });
21584 };
21585
21586 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
21587     
21588     active : false,
21589     title : '',
21590     
21591     // the tabBox that this is attached to.
21592     tab : false,
21593      
21594     getAutoCreate : function() 
21595     {
21596         var cfg = {
21597             tag: 'div',
21598             cls: 'tab-pane'
21599         }
21600         
21601         if(this.active){
21602             cfg.cls += ' active';
21603         }
21604         
21605         return cfg;
21606     },
21607     initEvents  : function()
21608     {
21609         //Roo.log('trigger add pane handler');
21610         this.parent().fireEvent('addpane', this)
21611     },
21612     
21613      /**
21614      * Updates the tab title 
21615      * @param {String} html to set the title to.
21616      */
21617     setTitle: function(str)
21618     {
21619         if (!this.tab) {
21620             return;
21621         }
21622         this.title = str;
21623         this.tab.select('a', true).first().dom.innerHTML = str;
21624         
21625     }
21626     
21627     
21628     
21629 });
21630
21631  
21632
21633
21634  /*
21635  * - LGPL
21636  *
21637  * menu
21638  * 
21639  */
21640 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21641
21642 /**
21643  * @class Roo.bootstrap.menu.Menu
21644  * @extends Roo.bootstrap.Component
21645  * Bootstrap Menu class - container for Menu
21646  * @cfg {String} html Text of the menu
21647  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
21648  * @cfg {String} icon Font awesome icon
21649  * @cfg {String} pos Menu align to (top | bottom) default bottom
21650  * 
21651  * 
21652  * @constructor
21653  * Create a new Menu
21654  * @param {Object} config The config object
21655  */
21656
21657
21658 Roo.bootstrap.menu.Menu = function(config){
21659     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
21660     
21661     this.addEvents({
21662         /**
21663          * @event beforeshow
21664          * Fires before this menu is displayed
21665          * @param {Roo.bootstrap.menu.Menu} this
21666          */
21667         beforeshow : true,
21668         /**
21669          * @event beforehide
21670          * Fires before this menu is hidden
21671          * @param {Roo.bootstrap.menu.Menu} this
21672          */
21673         beforehide : true,
21674         /**
21675          * @event show
21676          * Fires after this menu is displayed
21677          * @param {Roo.bootstrap.menu.Menu} this
21678          */
21679         show : true,
21680         /**
21681          * @event hide
21682          * Fires after this menu is hidden
21683          * @param {Roo.bootstrap.menu.Menu} this
21684          */
21685         hide : true,
21686         /**
21687          * @event click
21688          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
21689          * @param {Roo.bootstrap.menu.Menu} this
21690          * @param {Roo.EventObject} e
21691          */
21692         click : true
21693     });
21694     
21695 };
21696
21697 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
21698     
21699     submenu : false,
21700     html : '',
21701     weight : 'default',
21702     icon : false,
21703     pos : 'bottom',
21704     
21705     
21706     getChildContainer : function() {
21707         if(this.isSubMenu){
21708             return this.el;
21709         }
21710         
21711         return this.el.select('ul.dropdown-menu', true).first();  
21712     },
21713     
21714     getAutoCreate : function()
21715     {
21716         var text = [
21717             {
21718                 tag : 'span',
21719                 cls : 'roo-menu-text',
21720                 html : this.html
21721             }
21722         ];
21723         
21724         if(this.icon){
21725             text.unshift({
21726                 tag : 'i',
21727                 cls : 'fa ' + this.icon
21728             })
21729         }
21730         
21731         
21732         var cfg = {
21733             tag : 'div',
21734             cls : 'btn-group',
21735             cn : [
21736                 {
21737                     tag : 'button',
21738                     cls : 'dropdown-button btn btn-' + this.weight,
21739                     cn : text
21740                 },
21741                 {
21742                     tag : 'button',
21743                     cls : 'dropdown-toggle btn btn-' + this.weight,
21744                     cn : [
21745                         {
21746                             tag : 'span',
21747                             cls : 'caret'
21748                         }
21749                     ]
21750                 },
21751                 {
21752                     tag : 'ul',
21753                     cls : 'dropdown-menu'
21754                 }
21755             ]
21756             
21757         };
21758         
21759         if(this.pos == 'top'){
21760             cfg.cls += ' dropup';
21761         }
21762         
21763         if(this.isSubMenu){
21764             cfg = {
21765                 tag : 'ul',
21766                 cls : 'dropdown-menu'
21767             }
21768         }
21769         
21770         return cfg;
21771     },
21772     
21773     onRender : function(ct, position)
21774     {
21775         this.isSubMenu = ct.hasClass('dropdown-submenu');
21776         
21777         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
21778     },
21779     
21780     initEvents : function() 
21781     {
21782         if(this.isSubMenu){
21783             return;
21784         }
21785         
21786         this.hidden = true;
21787         
21788         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
21789         this.triggerEl.on('click', this.onTriggerPress, this);
21790         
21791         this.buttonEl = this.el.select('button.dropdown-button', true).first();
21792         this.buttonEl.on('click', this.onClick, this);
21793         
21794     },
21795     
21796     list : function()
21797     {
21798         if(this.isSubMenu){
21799             return this.el;
21800         }
21801         
21802         return this.el.select('ul.dropdown-menu', true).first();
21803     },
21804     
21805     onClick : function(e)
21806     {
21807         this.fireEvent("click", this, e);
21808     },
21809     
21810     onTriggerPress  : function(e)
21811     {   
21812         if (this.isVisible()) {
21813             this.hide();
21814         } else {
21815             this.show();
21816         }
21817     },
21818     
21819     isVisible : function(){
21820         return !this.hidden;
21821     },
21822     
21823     show : function()
21824     {
21825         this.fireEvent("beforeshow", this);
21826         
21827         this.hidden = false;
21828         this.el.addClass('open');
21829         
21830         Roo.get(document).on("mouseup", this.onMouseUp, this);
21831         
21832         this.fireEvent("show", this);
21833         
21834         
21835     },
21836     
21837     hide : function()
21838     {
21839         this.fireEvent("beforehide", this);
21840         
21841         this.hidden = true;
21842         this.el.removeClass('open');
21843         
21844         Roo.get(document).un("mouseup", this.onMouseUp);
21845         
21846         this.fireEvent("hide", this);
21847     },
21848     
21849     onMouseUp : function()
21850     {
21851         this.hide();
21852     }
21853     
21854 });
21855
21856  
21857  /*
21858  * - LGPL
21859  *
21860  * menu item
21861  * 
21862  */
21863 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
21864
21865 /**
21866  * @class Roo.bootstrap.menu.Item
21867  * @extends Roo.bootstrap.Component
21868  * Bootstrap MenuItem class
21869  * @cfg {Boolean} submenu (true | false) default false
21870  * @cfg {String} html text of the item
21871  * @cfg {String} href the link
21872  * @cfg {Boolean} disable (true | false) default false
21873  * @cfg {Boolean} preventDefault (true | false) default true
21874  * @cfg {String} icon Font awesome icon
21875  * @cfg {String} pos Submenu align to (left | right) default right 
21876  * 
21877  * 
21878  * @constructor
21879  * Create a new Item
21880  * @param {Object} config The config object
21881  */
21882
21883
21884 Roo.bootstrap.menu.Item = function(config){
21885     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
21886     this.addEvents({
21887         /**
21888          * @event mouseover
21889          * Fires when the mouse is hovering over this menu
21890          * @param {Roo.bootstrap.menu.Item} this
21891          * @param {Roo.EventObject} e
21892          */
21893         mouseover : true,
21894         /**
21895          * @event mouseout
21896          * Fires when the mouse exits this menu
21897          * @param {Roo.bootstrap.menu.Item} this
21898          * @param {Roo.EventObject} e
21899          */
21900         mouseout : true,
21901         // raw events
21902         /**
21903          * @event click
21904          * The raw click event for the entire grid.
21905          * @param {Roo.EventObject} e
21906          */
21907         click : true
21908     });
21909 };
21910
21911 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
21912     
21913     submenu : false,
21914     href : '',
21915     html : '',
21916     preventDefault: true,
21917     disable : false,
21918     icon : false,
21919     pos : 'right',
21920     
21921     getAutoCreate : function()
21922     {
21923         var text = [
21924             {
21925                 tag : 'span',
21926                 cls : 'roo-menu-item-text',
21927                 html : this.html
21928             }
21929         ];
21930         
21931         if(this.icon){
21932             text.unshift({
21933                 tag : 'i',
21934                 cls : 'fa ' + this.icon
21935             })
21936         }
21937         
21938         var cfg = {
21939             tag : 'li',
21940             cn : [
21941                 {
21942                     tag : 'a',
21943                     href : this.href || '#',
21944                     cn : text
21945                 }
21946             ]
21947         };
21948         
21949         if(this.disable){
21950             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
21951         }
21952         
21953         if(this.submenu){
21954             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
21955             
21956             if(this.pos == 'left'){
21957                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
21958             }
21959         }
21960         
21961         return cfg;
21962     },
21963     
21964     initEvents : function() 
21965     {
21966         this.el.on('mouseover', this.onMouseOver, this);
21967         this.el.on('mouseout', this.onMouseOut, this);
21968         
21969         this.el.select('a', true).first().on('click', this.onClick, this);
21970         
21971     },
21972     
21973     onClick : function(e)
21974     {
21975         if(this.preventDefault){
21976             e.preventDefault();
21977         }
21978         
21979         this.fireEvent("click", this, e);
21980     },
21981     
21982     onMouseOver : function(e)
21983     {
21984         if(this.submenu && this.pos == 'left'){
21985             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
21986         }
21987         
21988         this.fireEvent("mouseover", this, e);
21989     },
21990     
21991     onMouseOut : function(e)
21992     {
21993         this.fireEvent("mouseout", this, e);
21994     }
21995 });
21996
21997  
21998
21999  /*
22000  * - LGPL
22001  *
22002  * menu separator
22003  * 
22004  */
22005 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22006
22007 /**
22008  * @class Roo.bootstrap.menu.Separator
22009  * @extends Roo.bootstrap.Component
22010  * Bootstrap Separator class
22011  * 
22012  * @constructor
22013  * Create a new Separator
22014  * @param {Object} config The config object
22015  */
22016
22017
22018 Roo.bootstrap.menu.Separator = function(config){
22019     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
22020 };
22021
22022 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
22023     
22024     getAutoCreate : function(){
22025         var cfg = {
22026             tag : 'li',
22027             cls: 'divider'
22028         };
22029         
22030         return cfg;
22031     }
22032    
22033 });
22034
22035  
22036
22037  /*
22038  * - LGPL
22039  *
22040  * Tooltip
22041  * 
22042  */
22043
22044 /**
22045  * @class Roo.bootstrap.Tooltip
22046  * Bootstrap Tooltip class
22047  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
22048  * to determine which dom element triggers the tooltip.
22049  * 
22050  * It needs to add support for additional attributes like tooltip-position
22051  * 
22052  * @constructor
22053  * Create a new Toolti
22054  * @param {Object} config The config object
22055  */
22056
22057 Roo.bootstrap.Tooltip = function(config){
22058     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
22059 };
22060
22061 Roo.apply(Roo.bootstrap.Tooltip, {
22062     /**
22063      * @function init initialize tooltip monitoring.
22064      * @static
22065      */
22066     currentEl : false,
22067     currentTip : false,
22068     currentRegion : false,
22069     
22070     //  init : delay?
22071     
22072     init : function()
22073     {
22074         Roo.get(document).on('mouseover', this.enter ,this);
22075         Roo.get(document).on('mouseout', this.leave, this);
22076          
22077         
22078         this.currentTip = new Roo.bootstrap.Tooltip();
22079     },
22080     
22081     enter : function(ev)
22082     {
22083         var dom = ev.getTarget();
22084         
22085         //Roo.log(['enter',dom]);
22086         var el = Roo.fly(dom);
22087         if (this.currentEl) {
22088             //Roo.log(dom);
22089             //Roo.log(this.currentEl);
22090             //Roo.log(this.currentEl.contains(dom));
22091             if (this.currentEl == el) {
22092                 return;
22093             }
22094             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
22095                 return;
22096             }
22097
22098         }
22099         
22100         
22101         
22102         if (this.currentTip.el) {
22103             this.currentTip.el.hide(); // force hiding...
22104         }    
22105         //Roo.log(ev);
22106         var bindEl = el;
22107         
22108         // you can not look for children, as if el is the body.. then everythign is the child..
22109         if (!el.attr('tooltip')) { //
22110             if (!el.select("[tooltip]").elements.length) {
22111                 return;
22112             }
22113             // is the mouse over this child...?
22114             bindEl = el.select("[tooltip]").first();
22115             var xy = ev.getXY();
22116             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
22117                 //Roo.log("not in region.");
22118                 return;
22119             }
22120             //Roo.log("child element over..");
22121             
22122         }
22123         this.currentEl = bindEl;
22124         this.currentTip.bind(bindEl);
22125         this.currentRegion = Roo.lib.Region.getRegion(dom);
22126         this.currentTip.enter();
22127         
22128     },
22129     leave : function(ev)
22130     {
22131         var dom = ev.getTarget();
22132         //Roo.log(['leave',dom]);
22133         if (!this.currentEl) {
22134             return;
22135         }
22136         
22137         
22138         if (dom != this.currentEl.dom) {
22139             return;
22140         }
22141         var xy = ev.getXY();
22142         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
22143             return;
22144         }
22145         // only activate leave if mouse cursor is outside... bounding box..
22146         
22147         
22148         
22149         
22150         if (this.currentTip) {
22151             this.currentTip.leave();
22152         }
22153         //Roo.log('clear currentEl');
22154         this.currentEl = false;
22155         
22156         
22157     },
22158     alignment : {
22159         'left' : ['r-l', [-2,0], 'right'],
22160         'right' : ['l-r', [2,0], 'left'],
22161         'bottom' : ['t-b', [0,2], 'top'],
22162         'top' : [ 'b-t', [0,-2], 'bottom']
22163     }
22164     
22165 });
22166
22167
22168 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
22169     
22170     
22171     bindEl : false,
22172     
22173     delay : null, // can be { show : 300 , hide: 500}
22174     
22175     timeout : null,
22176     
22177     hoverState : null, //???
22178     
22179     placement : 'bottom', 
22180     
22181     getAutoCreate : function(){
22182     
22183         var cfg = {
22184            cls : 'tooltip',
22185            role : 'tooltip',
22186            cn : [
22187                 {
22188                     cls : 'tooltip-arrow'
22189                 },
22190                 {
22191                     cls : 'tooltip-inner'
22192                 }
22193            ]
22194         };
22195         
22196         return cfg;
22197     },
22198     bind : function(el)
22199     {
22200         this.bindEl = el;
22201     },
22202       
22203     
22204     enter : function () {
22205        
22206         if (this.timeout != null) {
22207             clearTimeout(this.timeout);
22208         }
22209         
22210         this.hoverState = 'in';
22211          //Roo.log("enter - show");
22212         if (!this.delay || !this.delay.show) {
22213             this.show();
22214             return;
22215         }
22216         var _t = this;
22217         this.timeout = setTimeout(function () {
22218             if (_t.hoverState == 'in') {
22219                 _t.show();
22220             }
22221         }, this.delay.show);
22222     },
22223     leave : function()
22224     {
22225         clearTimeout(this.timeout);
22226     
22227         this.hoverState = 'out';
22228          if (!this.delay || !this.delay.hide) {
22229             this.hide();
22230             return;
22231         }
22232        
22233         var _t = this;
22234         this.timeout = setTimeout(function () {
22235             //Roo.log("leave - timeout");
22236             
22237             if (_t.hoverState == 'out') {
22238                 _t.hide();
22239                 Roo.bootstrap.Tooltip.currentEl = false;
22240             }
22241         }, delay);
22242     },
22243     
22244     show : function ()
22245     {
22246         if (!this.el) {
22247             this.render(document.body);
22248         }
22249         // set content.
22250         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
22251         
22252         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
22253         
22254         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
22255         
22256         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
22257         
22258         var placement = typeof this.placement == 'function' ?
22259             this.placement.call(this, this.el, on_el) :
22260             this.placement;
22261             
22262         var autoToken = /\s?auto?\s?/i;
22263         var autoPlace = autoToken.test(placement);
22264         if (autoPlace) {
22265             placement = placement.replace(autoToken, '') || 'top';
22266         }
22267         
22268         //this.el.detach()
22269         //this.el.setXY([0,0]);
22270         this.el.show();
22271         //this.el.dom.style.display='block';
22272         this.el.addClass(placement);
22273         
22274         //this.el.appendTo(on_el);
22275         
22276         var p = this.getPosition();
22277         var box = this.el.getBox();
22278         
22279         if (autoPlace) {
22280             // fixme..
22281         }
22282         var align = Roo.bootstrap.Tooltip.alignment[placement];
22283         this.el.alignTo(this.bindEl, align[0],align[1]);
22284         //var arrow = this.el.select('.arrow',true).first();
22285         //arrow.set(align[2], 
22286         
22287         this.el.addClass('in fade');
22288         this.hoverState = null;
22289         
22290         if (this.el.hasClass('fade')) {
22291             // fade it?
22292         }
22293         
22294     },
22295     hide : function()
22296     {
22297          
22298         if (!this.el) {
22299             return;
22300         }
22301         //this.el.setXY([0,0]);
22302         this.el.removeClass('in');
22303         //this.el.hide();
22304         
22305     }
22306     
22307 });
22308  
22309
22310  /*
22311  * - LGPL
22312  *
22313  * Location Picker
22314  * 
22315  */
22316
22317 /**
22318  * @class Roo.bootstrap.LocationPicker
22319  * @extends Roo.bootstrap.Component
22320  * Bootstrap LocationPicker class
22321  * @cfg {Number} latitude Position when init default 0
22322  * @cfg {Number} longitude Position when init default 0
22323  * @cfg {Number} zoom default 15
22324  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
22325  * @cfg {Boolean} mapTypeControl default false
22326  * @cfg {Boolean} disableDoubleClickZoom default false
22327  * @cfg {Boolean} scrollwheel default true
22328  * @cfg {Boolean} streetViewControl default false
22329  * @cfg {Number} radius default 0
22330  * @cfg {String} locationName
22331  * @cfg {Boolean} draggable default true
22332  * @cfg {Boolean} enableAutocomplete default false
22333  * @cfg {Boolean} enableReverseGeocode default true
22334  * @cfg {String} markerTitle
22335  * 
22336  * @constructor
22337  * Create a new LocationPicker
22338  * @param {Object} config The config object
22339  */
22340
22341
22342 Roo.bootstrap.LocationPicker = function(config){
22343     
22344     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
22345     
22346     this.addEvents({
22347         /**
22348          * @event initial
22349          * Fires when the picker initialized.
22350          * @param {Roo.bootstrap.LocationPicker} this
22351          * @param {Google Location} location
22352          */
22353         initial : true,
22354         /**
22355          * @event positionchanged
22356          * Fires when the picker position changed.
22357          * @param {Roo.bootstrap.LocationPicker} this
22358          * @param {Google Location} location
22359          */
22360         positionchanged : true,
22361         /**
22362          * @event resize
22363          * Fires when the map resize.
22364          * @param {Roo.bootstrap.LocationPicker} this
22365          */
22366         resize : true,
22367         /**
22368          * @event show
22369          * Fires when the map show.
22370          * @param {Roo.bootstrap.LocationPicker} this
22371          */
22372         show : true,
22373         /**
22374          * @event hide
22375          * Fires when the map hide.
22376          * @param {Roo.bootstrap.LocationPicker} this
22377          */
22378         hide : true,
22379         /**
22380          * @event mapClick
22381          * Fires when click the map.
22382          * @param {Roo.bootstrap.LocationPicker} this
22383          * @param {Map event} e
22384          */
22385         mapClick : true,
22386         /**
22387          * @event mapRightClick
22388          * Fires when right click the map.
22389          * @param {Roo.bootstrap.LocationPicker} this
22390          * @param {Map event} e
22391          */
22392         mapRightClick : true,
22393         /**
22394          * @event markerClick
22395          * Fires when click the marker.
22396          * @param {Roo.bootstrap.LocationPicker} this
22397          * @param {Map event} e
22398          */
22399         markerClick : true,
22400         /**
22401          * @event markerRightClick
22402          * Fires when right click the marker.
22403          * @param {Roo.bootstrap.LocationPicker} this
22404          * @param {Map event} e
22405          */
22406         markerRightClick : true,
22407         /**
22408          * @event OverlayViewDraw
22409          * Fires when OverlayView Draw
22410          * @param {Roo.bootstrap.LocationPicker} this
22411          */
22412         OverlayViewDraw : true,
22413         /**
22414          * @event OverlayViewOnAdd
22415          * Fires when OverlayView Draw
22416          * @param {Roo.bootstrap.LocationPicker} this
22417          */
22418         OverlayViewOnAdd : true,
22419         /**
22420          * @event OverlayViewOnRemove
22421          * Fires when OverlayView Draw
22422          * @param {Roo.bootstrap.LocationPicker} this
22423          */
22424         OverlayViewOnRemove : true,
22425         /**
22426          * @event OverlayViewShow
22427          * Fires when OverlayView Draw
22428          * @param {Roo.bootstrap.LocationPicker} this
22429          * @param {Pixel} cpx
22430          */
22431         OverlayViewShow : true,
22432         /**
22433          * @event OverlayViewHide
22434          * Fires when OverlayView Draw
22435          * @param {Roo.bootstrap.LocationPicker} this
22436          */
22437         OverlayViewHide : true
22438     });
22439         
22440 };
22441
22442 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
22443     
22444     gMapContext: false,
22445     
22446     latitude: 0,
22447     longitude: 0,
22448     zoom: 15,
22449     mapTypeId: false,
22450     mapTypeControl: false,
22451     disableDoubleClickZoom: false,
22452     scrollwheel: true,
22453     streetViewControl: false,
22454     radius: 0,
22455     locationName: '',
22456     draggable: true,
22457     enableAutocomplete: false,
22458     enableReverseGeocode: true,
22459     markerTitle: '',
22460     
22461     getAutoCreate: function()
22462     {
22463
22464         var cfg = {
22465             tag: 'div',
22466             cls: 'roo-location-picker'
22467         };
22468         
22469         return cfg
22470     },
22471     
22472     initEvents: function(ct, position)
22473     {       
22474         if(!this.el.getWidth() || this.isApplied()){
22475             return;
22476         }
22477         
22478         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22479         
22480         this.initial();
22481     },
22482     
22483     initial: function()
22484     {
22485         if(!this.mapTypeId){
22486             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
22487         }
22488         
22489         this.gMapContext = this.GMapContext();
22490         
22491         this.initOverlayView();
22492         
22493         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
22494         
22495         var _this = this;
22496                 
22497         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
22498             _this.setPosition(_this.gMapContext.marker.position);
22499         });
22500         
22501         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
22502             _this.fireEvent('mapClick', this, event);
22503             
22504         });
22505
22506         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
22507             _this.fireEvent('mapRightClick', this, event);
22508             
22509         });
22510         
22511         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
22512             _this.fireEvent('markerClick', this, event);
22513             
22514         });
22515
22516         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
22517             _this.fireEvent('markerRightClick', this, event);
22518             
22519         });
22520         
22521         this.setPosition(this.gMapContext.location);
22522         
22523         this.fireEvent('initial', this, this.gMapContext.location);
22524     },
22525     
22526     initOverlayView: function()
22527     {
22528         var _this = this;
22529         
22530         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
22531             
22532             draw: function()
22533             {
22534                 _this.fireEvent('OverlayViewDraw', _this);
22535             },
22536             
22537             onAdd: function()
22538             {
22539                 _this.fireEvent('OverlayViewOnAdd', _this);
22540             },
22541             
22542             onRemove: function()
22543             {
22544                 _this.fireEvent('OverlayViewOnRemove', _this);
22545             },
22546             
22547             show: function(cpx)
22548             {
22549                 _this.fireEvent('OverlayViewShow', _this, cpx);
22550             },
22551             
22552             hide: function()
22553             {
22554                 _this.fireEvent('OverlayViewHide', _this);
22555             }
22556             
22557         });
22558     },
22559     
22560     fromLatLngToContainerPixel: function(event)
22561     {
22562         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
22563     },
22564     
22565     isApplied: function() 
22566     {
22567         return this.getGmapContext() == false ? false : true;
22568     },
22569     
22570     getGmapContext: function() 
22571     {
22572         return this.gMapContext
22573     },
22574     
22575     GMapContext: function() 
22576     {
22577         var position = new google.maps.LatLng(this.latitude, this.longitude);
22578         
22579         var _map = new google.maps.Map(this.el.dom, {
22580             center: position,
22581             zoom: this.zoom,
22582             mapTypeId: this.mapTypeId,
22583             mapTypeControl: this.mapTypeControl,
22584             disableDoubleClickZoom: this.disableDoubleClickZoom,
22585             scrollwheel: this.scrollwheel,
22586             streetViewControl: this.streetViewControl,
22587             locationName: this.locationName,
22588             draggable: this.draggable,
22589             enableAutocomplete: this.enableAutocomplete,
22590             enableReverseGeocode: this.enableReverseGeocode
22591         });
22592         
22593         var _marker = new google.maps.Marker({
22594             position: position,
22595             map: _map,
22596             title: this.markerTitle,
22597             draggable: this.draggable
22598         });
22599         
22600         return {
22601             map: _map,
22602             marker: _marker,
22603             circle: null,
22604             location: position,
22605             radius: this.radius,
22606             locationName: this.locationName,
22607             addressComponents: {
22608                 formatted_address: null,
22609                 addressLine1: null,
22610                 addressLine2: null,
22611                 streetName: null,
22612                 streetNumber: null,
22613                 city: null,
22614                 district: null,
22615                 state: null,
22616                 stateOrProvince: null
22617             },
22618             settings: this,
22619             domContainer: this.el.dom,
22620             geodecoder: new google.maps.Geocoder()
22621         };
22622     },
22623     
22624     drawCircle: function(center, radius, options) 
22625     {
22626         if (this.gMapContext.circle != null) {
22627             this.gMapContext.circle.setMap(null);
22628         }
22629         if (radius > 0) {
22630             radius *= 1;
22631             options = Roo.apply({}, options, {
22632                 strokeColor: "#0000FF",
22633                 strokeOpacity: .35,
22634                 strokeWeight: 2,
22635                 fillColor: "#0000FF",
22636                 fillOpacity: .2
22637             });
22638             
22639             options.map = this.gMapContext.map;
22640             options.radius = radius;
22641             options.center = center;
22642             this.gMapContext.circle = new google.maps.Circle(options);
22643             return this.gMapContext.circle;
22644         }
22645         
22646         return null;
22647     },
22648     
22649     setPosition: function(location) 
22650     {
22651         this.gMapContext.location = location;
22652         this.gMapContext.marker.setPosition(location);
22653         this.gMapContext.map.panTo(location);
22654         this.drawCircle(location, this.gMapContext.radius, {});
22655         
22656         var _this = this;
22657         
22658         if (this.gMapContext.settings.enableReverseGeocode) {
22659             this.gMapContext.geodecoder.geocode({
22660                 latLng: this.gMapContext.location
22661             }, function(results, status) {
22662                 
22663                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
22664                     _this.gMapContext.locationName = results[0].formatted_address;
22665                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
22666                     
22667                     _this.fireEvent('positionchanged', this, location);
22668                 }
22669             });
22670             
22671             return;
22672         }
22673         
22674         this.fireEvent('positionchanged', this, location);
22675     },
22676     
22677     resize: function()
22678     {
22679         google.maps.event.trigger(this.gMapContext.map, "resize");
22680         
22681         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
22682         
22683         this.fireEvent('resize', this);
22684     },
22685     
22686     setPositionByLatLng: function(latitude, longitude)
22687     {
22688         this.setPosition(new google.maps.LatLng(latitude, longitude));
22689     },
22690     
22691     getCurrentPosition: function() 
22692     {
22693         return {
22694             latitude: this.gMapContext.location.lat(),
22695             longitude: this.gMapContext.location.lng()
22696         };
22697     },
22698     
22699     getAddressName: function() 
22700     {
22701         return this.gMapContext.locationName;
22702     },
22703     
22704     getAddressComponents: function() 
22705     {
22706         return this.gMapContext.addressComponents;
22707     },
22708     
22709     address_component_from_google_geocode: function(address_components) 
22710     {
22711         var result = {};
22712         
22713         for (var i = 0; i < address_components.length; i++) {
22714             var component = address_components[i];
22715             if (component.types.indexOf("postal_code") >= 0) {
22716                 result.postalCode = component.short_name;
22717             } else if (component.types.indexOf("street_number") >= 0) {
22718                 result.streetNumber = component.short_name;
22719             } else if (component.types.indexOf("route") >= 0) {
22720                 result.streetName = component.short_name;
22721             } else if (component.types.indexOf("neighborhood") >= 0) {
22722                 result.city = component.short_name;
22723             } else if (component.types.indexOf("locality") >= 0) {
22724                 result.city = component.short_name;
22725             } else if (component.types.indexOf("sublocality") >= 0) {
22726                 result.district = component.short_name;
22727             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
22728                 result.stateOrProvince = component.short_name;
22729             } else if (component.types.indexOf("country") >= 0) {
22730                 result.country = component.short_name;
22731             }
22732         }
22733         
22734         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
22735         result.addressLine2 = "";
22736         return result;
22737     },
22738     
22739     setZoomLevel: function(zoom)
22740     {
22741         this.gMapContext.map.setZoom(zoom);
22742     },
22743     
22744     show: function()
22745     {
22746         if(!this.el){
22747             return;
22748         }
22749         
22750         this.el.show();
22751         
22752         this.resize();
22753         
22754         this.fireEvent('show', this);
22755     },
22756     
22757     hide: function()
22758     {
22759         if(!this.el){
22760             return;
22761         }
22762         
22763         this.el.hide();
22764         
22765         this.fireEvent('hide', this);
22766     }
22767     
22768 });
22769
22770 Roo.apply(Roo.bootstrap.LocationPicker, {
22771     
22772     OverlayView : function(map, options)
22773     {
22774         options = options || {};
22775         
22776         this.setMap(map);
22777     }
22778     
22779     
22780 });/*
22781  * - LGPL
22782  *
22783  * Alert
22784  * 
22785  */
22786
22787 /**
22788  * @class Roo.bootstrap.Alert
22789  * @extends Roo.bootstrap.Component
22790  * Bootstrap Alert class
22791  * @cfg {String} title The title of alert
22792  * @cfg {String} html The content of alert
22793  * @cfg {String} weight (  success | info | warning | danger )
22794  * @cfg {String} faicon font-awesomeicon
22795  * 
22796  * @constructor
22797  * Create a new alert
22798  * @param {Object} config The config object
22799  */
22800
22801
22802 Roo.bootstrap.Alert = function(config){
22803     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
22804     
22805 };
22806
22807 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
22808     
22809     title: '',
22810     html: '',
22811     weight: false,
22812     faicon: false,
22813     
22814     getAutoCreate : function()
22815     {
22816         
22817         var cfg = {
22818             tag : 'div',
22819             cls : 'alert',
22820             cn : [
22821                 {
22822                     tag : 'i',
22823                     cls : 'roo-alert-icon'
22824                     
22825                 },
22826                 {
22827                     tag : 'b',
22828                     cls : 'roo-alert-title',
22829                     html : this.title
22830                 },
22831                 {
22832                     tag : 'span',
22833                     cls : 'roo-alert-text',
22834                     html : this.html
22835                 }
22836             ]
22837         };
22838         
22839         if(this.faicon){
22840             cfg.cn[0].cls += ' fa ' + this.faicon;
22841         }
22842         
22843         if(this.weight){
22844             cfg.cls += ' alert-' + this.weight;
22845         }
22846         
22847         return cfg;
22848     },
22849     
22850     initEvents: function() 
22851     {
22852         this.el.setVisibilityMode(Roo.Element.DISPLAY);
22853     },
22854     
22855     setTitle : function(str)
22856     {
22857         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
22858     },
22859     
22860     setText : function(str)
22861     {
22862         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
22863     },
22864     
22865     setWeight : function(weight)
22866     {
22867         if(this.weight){
22868             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
22869         }
22870         
22871         this.weight = weight;
22872         
22873         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
22874     },
22875     
22876     setIcon : function(icon)
22877     {
22878         if(this.faicon){
22879             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
22880         }
22881         
22882         this.faicon = icon
22883         
22884         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
22885     },
22886     
22887     hide: function() 
22888     {
22889         this.el.hide();   
22890     },
22891     
22892     show: function() 
22893     {  
22894         this.el.show();   
22895     }
22896     
22897 });
22898
22899