ca7f4e600f308230c1f252d5f8de887ec2e64ea7
[philo.git] / contrib / gilbert / static / gilbert / lib / ui / forms.js
1 Ext.ns('Gilbert.lib.ui.forms');
2
3
4 Gilbert.lib.ui.forms.ClearableComboBox = Ext.extend(Ext.form.ComboBox, {
5         
6         initComponent: function () {
7                 Gilbert.lib.ui.forms.ClearableComboBox.superclass.initComponent.call(this);
8                 
9                 this.triggerConfig = {
10                         tag: 'span',
11                         cls: 'x-form-twin-triggers',
12                         cn: [
13                                 {
14                                         tag: 'img',
15                                         src: Ext.BLANK_IMAGE_URL,
16                                         alt: '',
17                                         cls: 'x-form-trigger x-form-clear-trigger', 
18                                 },
19                                 {
20                                         tag: 'img',
21                                         src: Ext.BLANK_IMAGE_URL,
22                                         alt: '',
23                                         cls: 'x-form-trigger ' + this.triggerClass,
24                                 },
25                         ],
26                 };
27         },
28         
29         afterRender: function () {
30                 Gilbert.lib.ui.forms.ClearableComboBox.superclass.afterRender.call(this);
31                 
32                 if (this.value && this.allowBlank) {
33                         this.triggers[0].show();
34                 } else {
35                         this.triggers[0].hide();
36                 }
37         },
38         
39         initTrigger: function () {
40                 Ext.form.TwinTriggerField.prototype.initTrigger.call(this);
41         },
42         
43         getTriggerWidth: function () {
44                 Ext.form.TwinTriggerField.prototype.getTriggerWidth.call(this);
45         },
46         
47         onTrigger2Click: function () {
48                 this.onTriggerClick();
49         },
50         
51         onTrigger1Click: function () {
52                 this.clearValue();
53                 this.triggers[0].hide();
54         },
55         
56         setValue: function (v) {
57                 Gilbert.lib.ui.forms.ClearableComboBox.superclass.setValue.call(this, v);
58                 
59                 if (this.value && this.allowBlank) {
60                         this.triggers[0].show();
61                 } else {
62                         this.triggers[0].hide();
63                 }
64         },
65         
66         onDestroy: function () {
67                 Ext.destroy(this.triggers);
68                 
69                 Gilbert.lib.ui.forms.ClearableComboBox.superclass.onDestroy.call(this);
70         },
71         
72 });
73 Ext.reg('gilbertclearablecombo', Gilbert.lib.ui.forms.ClearableComboBox);
74
75
76 Gilbert.lib.ui.forms.ModelChoiceField = Ext.extend(Gilbert.lib.ui.forms.ClearableComboBox, {
77         
78         model_app_label: undefined,
79         
80         model_name: undefined,
81         
82         model_filters: {},
83         
84         initComponent: function () {
85                 if (!this.model) {
86                         this.model = Gilbert.get_model(this.model_app_label, this.model_name);
87                 }
88                 if (!this.store) {
89                         if (!this.model && this.backup_store) {
90                                 this.store = this.backup_store;
91                         } else if (this.model) {
92                                 this.store = this.model.create_store({
93                                         baseParams: {
94                                                 filters: this.model_filters,
95                                         },
96                                 });
97                                 this.valueField = 'pk';
98                                 this.displayField = '__unicode__';
99                         
100                                 this.on('beforequery', function () {
101                                         delete this.lastQuery;
102                                 }, this);
103                                 this.store.on('load', function (store, records, options) {
104                                         this.store_loaded = true;
105                                 }, this, {single: true});
106                                 this.store.load();
107                                 
108                                 this.on('render', function () {
109                                         var outer = this;
110                                         this.dropTarget = new Ext.dd.DropTarget(this.el, {
111                                                 ddGroup: outer.model.drag_drop_group,
112                                                 notifyEnter: function (source, e, data) {
113                                                         outer.el.highlight();
114                                                         return Ext.dd.DropTarget.prototype.notifyEnter.call(this);
115                                                 },
116                                                 notifyDrop: function (source, e, data) {
117                                                         outer.setValue(data.selections[0].id);
118                                                         return true;
119                                                 },
120                                         });
121                                 }, this);
122                         }
123                 }
124                 Gilbert.lib.ui.forms.ModelChoiceField.superclass.initComponent.call(this);
125         },
126         
127         setValue: function (v) {
128                 if (this.model && !this.store_loaded) {
129                         this.el.dom.value = this.loadingText;
130                         this.store.on('load', this.setValue.createDelegate(this, [v]), null, {single: true});
131                         return;
132                 }
133                 return Gilbert.lib.ui.forms.ModelChoiceField.superclass.setValue.call(this, v);
134         }
135         
136 });
137 Ext.reg('gilbertmodelchoicefield', Gilbert.lib.ui.forms.ModelChoiceField);
138
139
140 Gilbert.lib.ui.forms.MultipleChoiceField = Ext.extend(Ext.ux.form.SuperBoxSelect, {});
141 Ext.reg('gilbertmultiplechoicefield', Gilbert.lib.ui.forms.MultipleChoiceField);
142
143
144 Gilbert.lib.ui.forms.ModelMultipleChoiceField = Ext.extend(Gilbert.lib.ui.forms.MultipleChoiceField, {});
145 Ext.reg('gilbertmodelmultiplechoicefield', Gilbert.lib.ui.forms.ModelMultipleChoiceField);
146
147
148 /*
149 Gilbert.lib.ui.DateTimeField is derived from revision 813 of Ext.ux.form.DateTime by Ing. Jozef Sakáloš as posted at http://extjs.com/forum/showthread.php?t=22661. 
150 It, and the original, is licensed under the GNU LGPL version 3.0 (http://www.gnu.org/licenses/lgpl.html).
151 */
152
153 /**
154  * Creates new DateTimeField
155  * @constructor
156  * @param {Object} config A config object
157  */
158 Gilbert.lib.ui.DateTimeField = Ext.extend(Ext.form.Field, {
159         /**
160          * @cfg {Function} dateValidator A custom validation function to be called during date field
161          * validation (defaults to null)
162          */
163         dateValidator: null
164         /**
165          * @cfg {String/Object} defaultAutoCreate DomHelper element spec
166          * Let superclass to create hidden field instead of textbox. Hidden will be submittend to server
167          */
168         ,
169         defaultAutoCreate: {
170                 tag: 'input',
171                 type: 'hidden'
172         }
173         /**
174          * @cfg {String} dtSeparator Date - Time separator. Used to split date and time (defaults to ' ' (space))
175          */
176         ,
177         dtSeparator: ' '
178         /**
179          * @cfg {String} hiddenFormat Format of datetime used to store value in hidden field
180          * and submitted to server (defaults to 'Y-m-d H:i:s' that is mysql format)
181          */
182         ,
183         hiddenFormat: 'Y-m-d H:i:s'
184         /**
185          * @cfg {Boolean} otherToNow Set other field to now() if not explicly filled in (defaults to true)
186          */
187         ,
188         otherToNow: true
189         /**
190          * @cfg {Boolean} emptyToNow Set field value to now on attempt to set empty value.
191          * If it is true then setValue() sets value of field to current date and time (defaults to false)
192          */
193         /**
194          * @cfg {String} timePosition Where the time field should be rendered. 'right' is suitable for forms
195          * and 'below' is suitable if the field is used as the grid editor (defaults to 'right')
196          */
197         ,
198         timePosition: 'right'
199         // valid values:'below', 'right'
200         /**
201          * @cfg {Function} timeValidator A custom validation function to be called during time field
202          * validation (defaults to null)
203          */
204         ,
205         timeValidator: null
206         /**
207          * @cfg {Number} timeWidth Width of time field in pixels (defaults to 100)
208          */
209         ,
210         timeWidth: 100
211         /**
212          * @cfg {String} dateFormat Format of DateField. Can be localized. (defaults to 'm/y/d')
213          */
214         ,
215         dateFormat: 'm/d/y'
216         /**
217          * @cfg {String} timeFormat Format of TimeField. Can be localized. (defaults to 'g:i A')
218          */
219         ,
220         timeFormat: 'g:i A'
221         /**
222          * @cfg {Object} dateConfig Config for DateField constructor.
223          */
224         /**
225          * @cfg {Object} timeConfig Config for TimeField constructor.
226          */
227
228         // {{{
229         /**
230          * @private
231          * creates DateField and TimeField and installs the necessary event handlers
232          */
233         ,
234         initComponent: function() {
235                 // call parent initComponent
236                 Gilbert.lib.ui.DateTimeField.superclass.initComponent.call(this);
237                 
238                 // create DateField
239                 var dateConfig = Ext.apply({},
240                 {
241                         id: this.id + '-date'
242                         ,
243                         format: this.dateFormat || Ext.form.DateField.prototype.format
244                         ,
245                         width: this.timeWidth
246                         ,
247                         selectOnFocus: this.selectOnFocus
248                         ,
249                         validator: this.dateValidator
250                         ,
251                         listeners: {
252                                 blur: {
253                                         scope: this,
254                                         fn: this.onBlur
255                                 }
256                                 ,
257                                 focus: {
258                                         scope: this,
259                                         fn: this.onFocus
260                                 }
261                         }
262                 },
263                 this.dateConfig);
264                 this.df = new Ext.form.DateField(dateConfig);
265                 this.df.ownerCt = this;
266                 delete(this.dateFormat);
267                 
268                 // create TimeField
269                 var timeConfig = Ext.apply({},
270                 {
271                         id: this.id + '-time'
272                         ,
273                         format: this.timeFormat || Ext.form.TimeField.prototype.format
274                         ,
275                         width: this.timeWidth
276                         ,
277                         selectOnFocus: this.selectOnFocus
278                         ,
279                         validator: this.timeValidator
280                         ,
281                         listeners: {
282                                 blur: {
283                                         scope: this,
284                                         fn: this.onBlur
285                                 }
286                                 ,
287                                 focus: {
288                                         scope: this,
289                                         fn: this.onFocus
290                                 }
291                         }
292                 },
293                 this.timeConfig);
294                 this.tf = new Ext.form.TimeField(timeConfig);
295                 this.tf.ownerCt = this;
296                 delete(this.timeFormat);
297                 
298                 // relay events
299                 this.relayEvents(this.df, ['focus', 'specialkey', 'invalid', 'valid']);
300                 this.relayEvents(this.tf, ['focus', 'specialkey', 'invalid', 'valid']);
301                 
302                 this.on('specialkey', this.onSpecialKey, this);
303                 
304         }
305         // eo function initComponent
306         // }}}
307         // {{{
308         /**
309          * @private
310          * Renders underlying DateField and TimeField and provides a workaround for side error icon bug
311          */
312         ,
313         onRender: function(ct, position) {
314                 // don't run more than once
315                 if (this.isRendered) {
316                         return;
317                 }
318                 
319                 // render underlying hidden field
320                 Gilbert.lib.ui.DateTimeField.superclass.onRender.call(this, ct, position);
321                 
322                 // render DateField and TimeField
323                 // create bounding table
324                 var t;
325                 if ('below' === this.timePosition) {
326                         t = Ext.DomHelper.append(ct, {
327                                 tag: 'table',
328                                 style: 'border-collapse:collapse',
329                                 children: [
330                                 {
331                                         tag: 'tr',
332                                         children: [{
333                                                 tag: 'td',
334                                                 style: 'padding-bottom:1px',
335                                                 cls: 'ux-datetime-date'
336                                         }]
337                                 }
338                                 ,
339                                 {
340                                         tag: 'tr',
341                                         children: [{
342                                                 tag: 'td',
343                                                 cls: 'ux-datetime-time'
344                                         }]
345                                 }
346                                 ]
347                         },
348                         true);
349                 }
350                 else {
351                         t = Ext.DomHelper.append(ct, {
352                                 tag: 'table',
353                                 style: 'border-collapse:collapse',
354                                 children: [
355                                 {
356                                         tag: 'tr',
357                                         children: [
358                                         {
359                                                 tag: 'td',
360                                                 style: 'padding-right:4px',
361                                                 cls: 'ux-datetime-date'
362                                         },
363                                         {
364                                                 tag: 'td',
365                                                 cls: 'ux-datetime-time'
366                                         }
367                                         ]
368                                 }
369                                 ]
370                         },
371                         true);
372                 }
373                 
374                 this.tableEl = t;
375                 this.wrap = t.wrap({
376                         cls: 'x-form-field-wrap'
377                 });
378                 //                this.wrap = t.wrap();
379                 this.wrap.on("mousedown", this.onMouseDown, this, {
380                         delay: 10
381                 });
382                 
383                 // render DateField & TimeField
384                 this.df.render(t.child('td.ux-datetime-date'));
385                 this.tf.render(t.child('td.ux-datetime-time'));
386                 
387                 // workaround for IE trigger misalignment bug
388                 // see http://extjs.com/forum/showthread.php?p=341075#post341075
389                 //                if(Ext.isIE && Ext.isStrict) {
390                 //                        t.select('input').applyStyles({top:0});
391                 //                }
392                 this.df.el.swallowEvent(['keydown', 'keypress']);
393                 this.tf.el.swallowEvent(['keydown', 'keypress']);
394                 
395                 // create icon for side invalid errorIcon
396                 if ('side' === this.msgTarget) {
397                         var elp = this.el.findParent('.x-form-element', 10, true);
398                         if (elp) {
399                                 this.errorIcon = elp.createChild({
400                                         cls: 'x-form-invalid-icon'
401                                 });
402                         }
403                         
404                         var o = {
405                                 errorIcon: this.errorIcon
406                                 ,
407                                 msgTarget: 'side'
408                                 ,
409                                 alignErrorIcon: this.alignErrorIcon.createDelegate(this)
410                         };
411                         Ext.apply(this.df, o);
412                         Ext.apply(this.tf, o);
413                         //                        this.df.errorIcon = this.errorIcon;
414                         //                        this.tf.errorIcon = this.errorIcon;
415                 }
416                 
417                 // setup name for submit
418                 this.el.dom.name = this.hiddenName || this.name || this.id;
419                 
420                 // prevent helper fields from being submitted
421                 this.df.el.dom.removeAttribute("name");
422                 this.tf.el.dom.removeAttribute("name");
423                 
424                 // we're rendered flag
425                 this.isRendered = true;
426                 
427                 // update hidden field
428                 this.updateHidden();
429                 
430         }
431         // eo function onRender
432         // }}}
433         // {{{
434         /**
435          * @private
436          */
437         ,
438         adjustSize: Ext.BoxComponent.prototype.adjustSize
439         // }}}
440         // {{{
441         /**
442          * @private
443          */
444         ,
445         alignErrorIcon: function() {
446                 this.errorIcon.alignTo(this.tableEl, 'tl-tr', [2, 0]);
447         }
448         // }}}
449         // {{{
450         /**
451          * @private initializes internal dateValue
452          */
453         ,
454         initDateValue: function() {
455                 this.dateValue = this.otherToNow ? new Date() : new Date(1970, 0, 1, 0, 0, 0);
456         }
457         // }}}
458         // {{{
459         /**
460          * Calls clearInvalid on the DateField and TimeField
461          */
462         ,
463         clearInvalid: function() {
464                 this.df.clearInvalid();
465                 this.tf.clearInvalid();
466         }
467         // eo function clearInvalid
468         // }}}
469         // {{{
470         /**
471          * Calls markInvalid on both DateField and TimeField
472          * @param {String} msg Invalid message to display
473          */
474         ,
475         markInvalid: function(msg) {
476                 this.df.markInvalid(msg);
477                 this.tf.markInvalid(msg);
478         }
479         // eo function markInvalid
480         // }}}
481         // {{{
482         /**
483          * @private
484          * called from Component::destroy. 
485          * Destroys all elements and removes all listeners we've created.
486          */
487         ,
488         beforeDestroy: function() {
489                 if (this.isRendered) {
490                         //                        this.removeAllListeners();
491                         this.wrap.removeAllListeners();
492                         this.wrap.remove();
493                         this.tableEl.remove();
494                         this.df.destroy();
495                         this.tf.destroy();
496                 }
497         }
498         // eo function beforeDestroy
499         // }}}
500         // {{{
501         /**
502          * Disable this component.
503          * @return {Ext.Component} this
504          */
505         ,
506         disable: function() {
507                 if (this.isRendered) {
508                         this.df.disabled = this.disabled;
509                         this.df.onDisable();
510                         this.tf.onDisable();
511                 }
512                 this.disabled = true;
513                 this.df.disabled = true;
514                 this.tf.disabled = true;
515                 this.fireEvent("disable", this);
516                 return this;
517         }
518         // eo function disable
519         // }}}
520         // {{{
521         /**
522          * Enable this component.
523          * @return {Ext.Component} this
524          */
525         ,
526         enable: function() {
527                 if (this.rendered) {
528                         this.df.onEnable();
529                         this.tf.onEnable();
530                 }
531                 this.disabled = false;
532                 this.df.disabled = false;
533                 this.tf.disabled = false;
534                 this.fireEvent("enable", this);
535                 return this;
536         }
537         // eo function enable
538         // }}}
539         // {{{
540         /**
541          * @private Focus date filed
542          */
543         ,
544         focus: function() {
545                 this.df.focus();
546         }
547         // eo function focus
548         // }}}
549         // {{{
550         /**
551          * @private
552          */
553         ,
554         getPositionEl: function() {
555                 return this.wrap;
556         }
557         // }}}
558         // {{{
559         /**
560          * @private
561          */
562         ,
563         getResizeEl: function() {
564                 return this.wrap;
565         }
566         // }}}
567         // {{{
568         /**
569          * @return {Date/String} Returns value of this field
570          */
571         ,
572         getValue: function() {
573                 // create new instance of date
574                 return this.dateValue ? new Date(this.dateValue) : '';
575         }
576         // eo function getValue
577         // }}}
578         // {{{
579         /**
580          * @return {Boolean} true = valid, false = invalid
581          * @private Calls isValid methods of underlying DateField and TimeField and returns the result
582          */
583         ,
584         isValid: function() {
585                 var valid = true;
586                 var msg = "Both fields must be supplied."
587                 if (!(this.df.isValid() && this.tf.isValid())) valid = false;
588                 if (Boolean(this.df.getValue()) != Boolean(this.tf.getValue())) {
589                         valid = false;
590                         if (!this.df.getValue()){
591                                 if (!(this.tf.hasFocus || this.df.hasFocus)) this.df.markInvalid(msg);
592                         } else {
593                                 if (!(this.tf.hasFocus || this.df.hasFocus)) this.tf.markInvalid(msg);
594                         };
595                 };
596                 return valid;
597         }
598         // eo function isValid
599         // }}}
600         // {{{
601         /**
602          * Returns true if this component is visible
603          * @return {boolean} 
604          */
605         ,
606         isVisible: function() {
607                 return this.df.rendered && this.df.getActionEl().isVisible();
608         }
609         // eo function isVisible
610         // }}}
611         // {{{
612         /** 
613          * @private Handles blur event
614          */
615         ,
616         onBlur: function(f) {
617                 // called by both DateField and TimeField blur events
618                 // revert focus to previous field if clicked in between
619                 if (this.wrapClick) {
620                         f.focus();
621                         this.wrapClick = false;
622                 }
623                 
624                 // update underlying value
625                 if (f === this.df) {
626                         this.updateDate();
627                 }
628                 else {
629                         this.updateTime();
630                 }
631                 this.updateHidden();
632                 
633                 this.validate();
634                 
635                 // fire events later
636                 (function() {
637                         if (!this.df.hasFocus && !this.tf.hasFocus) {
638                                 var v = this.getValue();
639                                 if (String(v) !== String(this.startValue)) {
640                                         this.fireEvent("change", this, v, this.startValue);
641                                 }
642                                 this.hasFocus = false;
643                                 this.fireEvent('blur', this);
644                         }
645                 }).defer(100, this);
646                 
647         }
648         // eo function onBlur
649         // }}}
650         // {{{
651         /**
652          * @private Handles focus event
653          */
654         ,
655         onFocus: function() {
656                 if (!this.hasFocus) {
657                         this.hasFocus = true;
658                         this.startValue = this.getValue();
659                         this.fireEvent("focus", this);
660                 }
661         }
662         // }}}
663         // {{{
664         /**
665          * @private Just to prevent blur event when clicked in the middle of fields
666          */
667         ,
668         onMouseDown: function(e) {
669                 if (!this.disabled) {
670                         this.wrapClick = 'td' === e.target.nodeName.toLowerCase();
671                 }
672         }
673         // }}}
674         // {{{
675         /**
676          * @private
677          * Handles Tab and Shift-Tab events
678          */
679         ,
680         onSpecialKey: function(t, e) {
681                 var key = e.getKey();
682                 if (key === e.TAB) {
683                         if (t === this.df && !e.shiftKey) {
684                                 e.stopEvent();
685                                 this.tf.focus();
686                         }
687                         if (t === this.tf && e.shiftKey) {
688                                 e.stopEvent();
689                                 this.df.focus();
690                         }
691                         this.updateValue();
692                 }
693                 // otherwise it misbehaves in editor grid
694                 if (key === e.ENTER) {
695                         this.updateValue();
696                 }
697                 
698         }
699         // eo function onSpecialKey
700         // }}}
701         // {{{
702         /**
703          * Resets the current field value to the originally loaded value 
704          * and clears any validation messages. See Ext.form.BasicForm.trackResetOnLoad
705          */
706         ,
707         reset: function() {
708                 this.df.setValue(this.originalValue);
709                 this.tf.setValue(this.originalValue);
710         }
711         // eo function reset
712         // }}}
713         // {{{
714         /**
715          * @private Sets the value of DateField
716          */
717         ,
718         setDate: function(date) {
719                 this.df.setValue(date);
720         }
721         // eo function setDate
722         // }}}
723         // {{{
724         /** 
725          * @private Sets the value of TimeField
726          */
727         ,
728         setTime: function(date) {
729                 this.tf.setValue(date);
730         }
731         // eo function setTime
732         // }}}
733         // {{{
734         /**
735          * @private
736          * Sets correct sizes of underlying DateField and TimeField
737          * With workarounds for IE bugs
738          */
739         ,
740         setSize: function(w, h) {
741                 if (!w) {
742                         return;
743                 }
744                 if ('below' === this.timePosition) {
745                         this.df.setSize(w, h);
746                         this.tf.setSize(w, h);
747                         if (Ext.isIE) {
748                                 this.df.el.up('td').setWidth(w);
749                                 this.tf.el.up('td').setWidth(w);
750                         }
751                 }
752                 else {
753                         this.df.setSize(w - this.timeWidth - 4, h);
754                         this.tf.setSize(this.timeWidth, h);
755                         
756                         if (Ext.isIE) {
757                                 this.df.el.up('td').setWidth(w - this.timeWidth - 4);
758                                 this.tf.el.up('td').setWidth(this.timeWidth);
759                         }
760                 }
761         }
762         // eo function setSize
763         // }}}
764         // {{{
765         /**
766          * @param {Mixed} val Value to set
767          * Sets the value of this field
768          */
769         ,
770         setValue: function(val) {
771                 if (!val && true === this.emptyToNow) {
772                         this.setValue(new Date());
773                         return;
774                 }
775                 else if (!val) {
776                         this.setDate('');
777                         this.setTime('');
778                         this.updateValue();
779                         return;
780                 }
781                 if ('number' === typeof val) {
782                         val = new Date(val);
783                 }
784                 else if ('string' === typeof val && this.hiddenFormat) {
785                         val = Date.parseDate(val, this.hiddenFormat);
786                 }
787                 val = val ? val: new Date(1970, 0, 1, 0, 0, 0);
788                 var da;
789                 if (val instanceof Date) {
790                         this.setDate(val);
791                         this.setTime(val);
792                         this.dateValue = new Date(Ext.isIE ? val.getTime() : val);
793                 }
794                 else {
795                         da = val.split(this.dtSeparator);
796                         this.setDate(da[0]);
797                         if (da[1]) {
798                                 if (da[2]) {
799                                         // add am/pm part back to time
800                                         da[1] += da[2];
801                                 }
802                                 this.setTime(da[1]);
803                         }
804                 }
805                 this.updateValue();
806         }
807         // eo function setValue
808         // }}}
809         // {{{
810         /**
811          * Hide or show this component by boolean
812          * @return {Ext.Component} this
813          */
814         ,
815         setVisible: function(visible) {
816                 if (visible) {
817                         this.df.show();
818                         this.tf.show();
819                 } else {
820                         this.df.hide();
821                         this.tf.hide();
822                 }
823                 return this;
824         }
825         // eo function setVisible
826         // }}}
827         //{{{
828         ,
829         show: function() {
830                 return this.setVisible(true);
831         }
832         // eo function show
833         //}}}
834         //{{{
835         ,
836         hide: function() {
837                 return this.setVisible(false);
838         }
839         // eo function hide
840         //}}}
841         // {{{
842         /**
843          * @private Updates the date part
844          */
845         ,
846         updateDate: function() {
847                 
848                 var d = this.df.getValue();
849                 if (d) {
850                         if (! (this.dateValue instanceof Date)) {
851                                 this.initDateValue();
852                         }
853                         this.dateValue.setMonth(0);
854                         // because of leap years
855                         this.dateValue.setFullYear(d.getFullYear());
856                         this.dateValue.setMonth(d.getMonth(), d.getDate());
857                         //                        this.dateValue.setDate(d.getDate());
858                 }
859                 else {
860                         this.dateValue = '';
861                 }
862         }
863         // eo function updateDate
864         // }}}
865         // {{{
866         /**
867          * @private
868          * Updates the time part
869          */
870         ,
871         updateTime: function() {
872                 var t = this.tf.getValue();
873                 if (t && !(t instanceof Date)) {
874                         t = Date.parseDate(t, this.tf.format);
875                 }
876                 if (t && !this.df.getValue()) {
877                         this.initDateValue();
878                 }
879                 if (this.dateValue instanceof Date) {
880                         if (t) {
881                                 this.dateValue.setHours(t.getHours());
882                                 this.dateValue.setMinutes(t.getMinutes());
883                                 this.dateValue.setSeconds(t.getSeconds());
884                         }
885                         else {
886                                 this.dateValue.setHours(0);
887                                 this.dateValue.setMinutes(0);
888                                 this.dateValue.setSeconds(0);
889                         }
890                 }
891         }
892         // eo function updateTime
893         // }}}
894         // {{{
895         /**
896          * @private Updates the underlying hidden field value
897          */
898         ,
899         updateHidden: function() {
900                 if (this.isRendered) {
901                         var value = this.dateValue instanceof Date ? this.dateValue.format(this.hiddenFormat) : '';
902                         this.el.dom.value = value;
903                 }
904         }
905         // }}}
906         // {{{
907         /**
908          * @private Updates all of Date, Time and Hidden
909          */
910         ,
911         updateValue: function() {
912                 
913                 this.updateDate();
914                 this.updateTime();
915                 this.updateHidden();
916                 
917                 return;
918         }
919         // eo function updateValue
920         // }}}
921         // {{{
922         /**
923          * @return {Boolean} true = valid, false = invalid
924          * calls validate methods of DateField and TimeField
925          */
926         ,
927         validate: function() {
928                 var valid = true;
929                 var msg = "Both fields must be supplied."
930                 if (!(this.df.validate() && this.tf.validate())) valid = false;
931                 if (Boolean(this.df.getValue()) != Boolean(this.tf.getValue())) {
932                         valid = false;
933                         if (!this.df.getValue()){
934                                 if (!(this.tf.hasFocus || this.df.hasFocus)) this.df.markInvalid(msg);
935                         } else {
936                                 if (!(this.tf.hasFocus || this.df.hasFocus)) this.tf.markInvalid(msg);
937                         };
938                 };
939                 return valid;
940         }
941         // eo function validate
942         // }}}
943         // {{{
944         /**
945          * Returns renderer suitable to render this field
946          * @param {Object} Column model config
947          */
948         ,
949         renderer: function(field) {
950                 var format = field.editor.dateFormat || Gilbert.lib.ui.DateTimeField.prototype.dateFormat;
951                 format += ' ' + (field.editor.timeFormat || Gilbert.lib.ui.DateTimeField.prototype.timeFormat);
952                 var renderer = function(val) {
953                         var retval = Ext.util.Format.date(val, format);
954                         return retval;
955                 };
956                 return renderer;
957         }
958         // eo function renderer
959         // }}}
960 });
961 // eo extend
962 // register xtype
963 Ext.reg('gilbertdatetimefield', Gilbert.lib.ui.DateTimeField);