All of my work from commits: dd4a194, 692644a, 4a60203, 5de46bc, 152042d, 64a2d4e...
[philo.git] / contrib / gilbert / media / gilbert / lib / ui / forms.js
diff --git a/contrib/gilbert/media/gilbert/lib/ui/forms.js b/contrib/gilbert/media/gilbert/lib/ui/forms.js
new file mode 100644 (file)
index 0000000..719b7b1
--- /dev/null
@@ -0,0 +1,946 @@
+Ext.ns('Gilbert.lib.ui.forms');
+
+
+Gilbert.lib.ui.forms.ClearableComboBox = Ext.extend(Ext.form.ComboBox, {
+       
+       initComponent: function () {
+               Gilbert.lib.ui.forms.ClearableComboBox.superclass.initComponent.call(this);
+               
+               this.triggerConfig = {
+                       tag: 'span',
+                       cls: 'x-form-twin-triggers',
+                       cn: [
+                               {
+                                       tag: 'img',
+                                       src: Ext.BLANK_IMAGE_URL,
+                                       alt: '',
+                                       cls: 'x-form-trigger x-form-clear-trigger', 
+                               },
+                               {
+                                       tag: 'img',
+                                       src: Ext.BLANK_IMAGE_URL,
+                                       alt: '',
+                                       cls: 'x-form-trigger ' + this.triggerClass,
+                               },
+                       ],
+               };
+       },
+       
+       afterRender: function () {
+               Gilbert.lib.ui.forms.ClearableComboBox.superclass.afterRender.call(this);
+               
+               if (this.value && this.allowBlank) {
+                       this.triggers[0].show();
+               } else {
+                       this.triggers[0].hide();
+               }
+       },
+       
+       initTrigger: function () {
+               Ext.form.TwinTriggerField.prototype.initTrigger.call(this);
+       },
+       
+       getTriggerWidth: function () {
+               Ext.form.TwinTriggerField.prototype.getTriggerWidth.call(this);
+       },
+       
+       onTrigger2Click: function () {
+               this.onTriggerClick();
+       },
+       
+       onTrigger1Click: function () {
+               this.clearValue();
+               this.triggers[0].hide();
+       },
+       
+       setValue: function (v) {
+               Gilbert.lib.ui.forms.ClearableComboBox.superclass.setValue.call(this, v);
+               
+               if (this.value && this.allowBlank) {
+                       this.triggers[0].show();
+               } else {
+                       this.triggers[0].hide();
+               }
+       },
+       
+       onDestroy: function () {
+               Ext.destroy(this.triggers);
+               
+               Gilbert.lib.ui.forms.ClearableComboBox.superclass.onDestroy.call(this);
+       },
+       
+});
+Ext.reg('gilbertclearablecombo', Gilbert.lib.ui.forms.ClearableComboBox);
+
+
+Gilbert.lib.ui.forms.ModelChoiceField = Ext.extend(Gilbert.lib.ui.forms.ClearableComboBox, {
+       
+       model_app_label: undefined,
+       
+       model_name: undefined,
+       
+       model_filters: {},
+       
+       initComponent: function () {
+               if (!this.model) {
+                       this.model = Gilbert.get_model(this.model_app_label, this.model_name);
+               }
+               if (!this.store) {
+                       if (!this.model && this.backup_store) {
+                               this.store = this.backup_store;
+                       } else if (this.model) {
+                               this.store = this.model.create_store({
+                                       baseParams: {
+                                               filters: this.model_filters,
+                                       },
+                               });
+                               this.valueField = 'pk';
+                               this.displayField = '__unicode__';
+                       
+                               this.on('beforequery', function () {
+                                       delete this.lastQuery;
+                               }, this);
+                               this.store.on('load', function (store, records, options) {
+                                       this.store_loaded = true;
+                               }, this, {single: true});
+                               this.store.load();
+                               
+                               this.on('render', function () {
+                                       var outer = this;
+                                       this.dropTarget = new Ext.dd.DropTarget(this.el, {
+                                               ddGroup: outer.model.drag_drop_group,
+                                               notifyEnter: function (source, e, data) {
+                                                       outer.el.highlight();
+                                                       return Ext.dd.DropTarget.prototype.notifyEnter.call(this);
+                                               },
+                                               notifyDrop: function (source, e, data) {
+                                                       outer.setValue(data.selections[0].id);
+                                                       return true;
+                                               },
+                                       });
+                               }, this);
+                       }
+               }
+               Gilbert.lib.ui.forms.ModelChoiceField.superclass.initComponent.call(this);
+       },
+       
+       setValue: function (v) {
+               if (this.model && !this.store_loaded) {
+                       this.el.dom.value = this.loadingText;
+                       this.store.on('load', this.setValue.createDelegate(this, [v]), null, {single: true});
+                       return;
+               }
+               return Gilbert.lib.ui.forms.ModelChoiceField.superclass.setValue.call(this, v);
+       }
+       
+});
+Ext.reg('gilbertmodelchoicefield', Gilbert.lib.ui.forms.ModelChoiceField);
+
+
+Gilbert.lib.ui.forms.MultipleChoiceField = Ext.extend(Ext.ux.form.SuperBoxSelect, {});
+Ext.reg('gilbertmultiplechoicefield', Gilbert.lib.ui.forms.MultipleChoiceField);
+
+
+Gilbert.lib.ui.forms.ModelMultipleChoiceField = Ext.extend(Gilbert.lib.ui.forms.MultipleChoiceField, {});
+Ext.reg('gilbertmodelmultiplechoicefield', Gilbert.lib.ui.forms.ModelMultipleChoiceField);
+
+
+/*
+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. 
+It, and the original, is licensed under the GNU LGPL version 3.0 (http://www.gnu.org/licenses/lgpl.html).
+*/
+
+/**
+ * Creates new DateTimeField
+ * @constructor
+ * @param {Object} config A config object
+ */
+Gilbert.lib.ui.DateTimeField = Ext.extend(Ext.form.Field, {
+       /**
+        * @cfg {Function} dateValidator A custom validation function to be called during date field
+        * validation (defaults to null)
+        */
+       dateValidator: null
+       /**
+        * @cfg {String/Object} defaultAutoCreate DomHelper element spec
+        * Let superclass to create hidden field instead of textbox. Hidden will be submittend to server
+        */
+       ,
+       defaultAutoCreate: {
+               tag: 'input',
+               type: 'hidden'
+       }
+       /**
+        * @cfg {String} dtSeparator Date - Time separator. Used to split date and time (defaults to ' ' (space))
+        */
+       ,
+       dtSeparator: ' '
+       /**
+        * @cfg {String} hiddenFormat Format of datetime used to store value in hidden field
+        * and submitted to server (defaults to 'Y-m-d H:i:s' that is mysql format)
+        */
+       ,
+       hiddenFormat: 'Y-m-d H:i:s'
+       /**
+        * @cfg {Boolean} otherToNow Set other field to now() if not explicly filled in (defaults to true)
+        */
+       ,
+       otherToNow: true
+       /**
+        * @cfg {Boolean} emptyToNow Set field value to now on attempt to set empty value.
+        * If it is true then setValue() sets value of field to current date and time (defaults to false)
+        */
+       /**
+        * @cfg {String} timePosition Where the time field should be rendered. 'right' is suitable for forms
+        * and 'below' is suitable if the field is used as the grid editor (defaults to 'right')
+        */
+       ,
+       timePosition: 'right'
+       // valid values:'below', 'right'
+       /**
+        * @cfg {Function} timeValidator A custom validation function to be called during time field
+        * validation (defaults to null)
+        */
+       ,
+       timeValidator: null
+       /**
+        * @cfg {Number} timeWidth Width of time field in pixels (defaults to 100)
+        */
+       ,
+       timeWidth: 100
+       /**
+        * @cfg {String} dateFormat Format of DateField. Can be localized. (defaults to 'm/y/d')
+        */
+       ,
+       dateFormat: 'm/d/y'
+       /**
+        * @cfg {String} timeFormat Format of TimeField. Can be localized. (defaults to 'g:i A')
+        */
+       ,
+       timeFormat: 'g:i A'
+       /**
+        * @cfg {Object} dateConfig Config for DateField constructor.
+        */
+       /**
+        * @cfg {Object} timeConfig Config for TimeField constructor.
+        */
+
+       // {{{
+       /**
+        * @private
+        * creates DateField and TimeField and installs the necessary event handlers
+        */
+       ,
+       initComponent: function() {
+               // call parent initComponent
+               Gilbert.lib.ui.DateTimeField.superclass.initComponent.call(this);
+               
+               // create DateField
+               var dateConfig = Ext.apply({},
+               {
+                       id: this.id + '-date'
+                       ,
+                       format: this.dateFormat || Ext.form.DateField.prototype.format
+                       ,
+                       width: this.timeWidth
+                       ,
+                       selectOnFocus: this.selectOnFocus
+                       ,
+                       validator: this.dateValidator
+                       ,
+                       listeners: {
+                               blur: {
+                                       scope: this,
+                                       fn: this.onBlur
+                               }
+                               ,
+                               focus: {
+                                       scope: this,
+                                       fn: this.onFocus
+                               }
+                       }
+               },
+               this.dateConfig);
+               this.df = new Ext.form.DateField(dateConfig);
+               this.df.ownerCt = this;
+               delete(this.dateFormat);
+               
+               // create TimeField
+               var timeConfig = Ext.apply({},
+               {
+                       id: this.id + '-time'
+                       ,
+                       format: this.timeFormat || Ext.form.TimeField.prototype.format
+                       ,
+                       width: this.timeWidth
+                       ,
+                       selectOnFocus: this.selectOnFocus
+                       ,
+                       validator: this.timeValidator
+                       ,
+                       listeners: {
+                               blur: {
+                                       scope: this,
+                                       fn: this.onBlur
+                               }
+                               ,
+                               focus: {
+                                       scope: this,
+                                       fn: this.onFocus
+                               }
+                       }
+               },
+               this.timeConfig);
+               this.tf = new Ext.form.TimeField(timeConfig);
+               this.tf.ownerCt = this;
+               delete(this.timeFormat);
+               
+               // relay events
+               this.relayEvents(this.df, ['focus', 'specialkey', 'invalid', 'valid']);
+               this.relayEvents(this.tf, ['focus', 'specialkey', 'invalid', 'valid']);
+               
+               this.on('specialkey', this.onSpecialKey, this);
+               
+       }
+       // eo function initComponent
+       // }}}
+       // {{{
+       /**
+        * @private
+        * Renders underlying DateField and TimeField and provides a workaround for side error icon bug
+        */
+       ,
+       onRender: function(ct, position) {
+               // don't run more than once
+               if (this.isRendered) {
+                       return;
+               }
+               
+               // render underlying hidden field
+               Gilbert.lib.ui.DateTimeField.superclass.onRender.call(this, ct, position);
+               
+               // render DateField and TimeField
+               // create bounding table
+               var t;
+               if ('below' === this.timePosition || 'bellow' === this.timePosition) {
+                       t = Ext.DomHelper.append(ct, {
+                               tag: 'table',
+                               style: 'border-collapse:collapse',
+                               children: [
+                               {
+                                       tag: 'tr',
+                                       children: [{
+                                               tag: 'td',
+                                               style: 'padding-bottom:1px',
+                                               cls: 'ux-datetime-date'
+                                       }]
+                               }
+                               ,
+                               {
+                                       tag: 'tr',
+                                       children: [{
+                                               tag: 'td',
+                                               cls: 'ux-datetime-time'
+                                       }]
+                               }
+                               ]
+                       },
+                       true);
+               }
+               else {
+                       t = Ext.DomHelper.append(ct, {
+                               tag: 'table',
+                               style: 'border-collapse:collapse',
+                               children: [
+                               {
+                                       tag: 'tr',
+                                       children: [
+                                       {
+                                               tag: 'td',
+                                               style: 'padding-right:4px',
+                                               cls: 'ux-datetime-date'
+                                       },
+                                       {
+                                               tag: 'td',
+                                               cls: 'ux-datetime-time'
+                                       }
+                                       ]
+                               }
+                               ]
+                       },
+                       true);
+               }
+               
+               this.tableEl = t;
+               this.wrap = t.wrap({
+                       cls: 'x-form-field-wrap'
+               });
+               //                this.wrap = t.wrap();
+               this.wrap.on("mousedown", this.onMouseDown, this, {
+                       delay: 10
+               });
+               
+               // render DateField & TimeField
+               this.df.render(t.child('td.ux-datetime-date'));
+               this.tf.render(t.child('td.ux-datetime-time'));
+               
+               // workaround for IE trigger misalignment bug
+               // see http://extjs.com/forum/showthread.php?p=341075#post341075
+               //                if(Ext.isIE && Ext.isStrict) {
+               //                        t.select('input').applyStyles({top:0});
+               //                }
+               this.df.el.swallowEvent(['keydown', 'keypress']);
+               this.tf.el.swallowEvent(['keydown', 'keypress']);
+               
+               // create icon for side invalid errorIcon
+               if ('side' === this.msgTarget) {
+                       var elp = this.el.findParent('.x-form-element', 10, true);
+                       if (elp) {
+                               this.errorIcon = elp.createChild({
+                                       cls: 'x-form-invalid-icon'
+                               });
+                       }
+                       
+                       var o = {
+                               errorIcon: this.errorIcon
+                               ,
+                               msgTarget: 'side'
+                               ,
+                               alignErrorIcon: this.alignErrorIcon.createDelegate(this)
+                       };
+                       Ext.apply(this.df, o);
+                       Ext.apply(this.tf, o);
+                       //                        this.df.errorIcon = this.errorIcon;
+                       //                        this.tf.errorIcon = this.errorIcon;
+               }
+               
+               // setup name for submit
+               this.el.dom.name = this.hiddenName || this.name || this.id;
+               
+               // prevent helper fields from being submitted
+               this.df.el.dom.removeAttribute("name");
+               this.tf.el.dom.removeAttribute("name");
+               
+               // we're rendered flag
+               this.isRendered = true;
+               
+               // update hidden field
+               this.updateHidden();
+               
+       }
+       // eo function onRender
+       // }}}
+       // {{{
+       /**
+        * @private
+        */
+       ,
+       adjustSize: Ext.BoxComponent.prototype.adjustSize
+       // }}}
+       // {{{
+       /**
+        * @private
+        */
+       ,
+       alignErrorIcon: function() {
+               this.errorIcon.alignTo(this.tableEl, 'tl-tr', [2, 0]);
+       }
+       // }}}
+       // {{{
+       /**
+        * @private initializes internal dateValue
+        */
+       ,
+       initDateValue: function() {
+               this.dateValue = this.otherToNow ? new Date() : new Date(1970, 0, 1, 0, 0, 0);
+       }
+       // }}}
+       // {{{
+       /**
+        * Calls clearInvalid on the DateField and TimeField
+        */
+       ,
+       clearInvalid: function() {
+               this.df.clearInvalid();
+               this.tf.clearInvalid();
+       }
+       // eo function clearInvalid
+       // }}}
+       // {{{
+       /**
+        * Calls markInvalid on both DateField and TimeField
+        * @param {String} msg Invalid message to display
+        */
+       ,
+       markInvalid: function(msg) {
+               this.df.markInvalid(msg);
+               this.tf.markInvalid(msg);
+       }
+       // eo function markInvalid
+       // }}}
+       // {{{
+       /**
+        * @private
+        * called from Component::destroy. 
+        * Destroys all elements and removes all listeners we've created.
+        */
+       ,
+       beforeDestroy: function() {
+               if (this.isRendered) {
+                       //                        this.removeAllListeners();
+                       this.wrap.removeAllListeners();
+                       this.wrap.remove();
+                       this.tableEl.remove();
+                       this.df.destroy();
+                       this.tf.destroy();
+               }
+       }
+       // eo function beforeDestroy
+       // }}}
+       // {{{
+       /**
+        * Disable this component.
+        * @return {Ext.Component} this
+        */
+       ,
+       disable: function() {
+               if (this.isRendered) {
+                       this.df.disabled = this.disabled;
+                       this.df.onDisable();
+                       this.tf.onDisable();
+               }
+               this.disabled = true;
+               this.df.disabled = true;
+               this.tf.disabled = true;
+               this.fireEvent("disable", this);
+               return this;
+       }
+       // eo function disable
+       // }}}
+       // {{{
+       /**
+        * Enable this component.
+        * @return {Ext.Component} this
+        */
+       ,
+       enable: function() {
+               if (this.rendered) {
+                       this.df.onEnable();
+                       this.tf.onEnable();
+               }
+               this.disabled = false;
+               this.df.disabled = false;
+               this.tf.disabled = false;
+               this.fireEvent("enable", this);
+               return this;
+       }
+       // eo function enable
+       // }}}
+       // {{{
+       /**
+        * @private Focus date filed
+        */
+       ,
+       focus: function() {
+               this.df.focus();
+       }
+       // eo function focus
+       // }}}
+       // {{{
+       /**
+        * @private
+        */
+       ,
+       getPositionEl: function() {
+               return this.wrap;
+       }
+       // }}}
+       // {{{
+       /**
+        * @private
+        */
+       ,
+       getResizeEl: function() {
+               return this.wrap;
+       }
+       // }}}
+       // {{{
+       /**
+        * @return {Date/String} Returns value of this field
+        */
+       ,
+       getValue: function() {
+               // create new instance of date
+               return this.dateValue ? new Date(this.dateValue) : '';
+       }
+       // eo function getValue
+       // }}}
+       // {{{
+       /**
+        * @return {Boolean} true = valid, false = invalid
+        * @private Calls isValid methods of underlying DateField and TimeField and returns the result
+        */
+       ,
+       isValid: function() {
+               return this.df.isValid() && this.tf.isValid();
+       }
+       // eo function isValid
+       // }}}
+       // {{{
+       /**
+        * Returns true if this component is visible
+        * @return {boolean} 
+        */
+       ,
+       isVisible: function() {
+               return this.df.rendered && this.df.getActionEl().isVisible();
+       }
+       // eo function isVisible
+       // }}}
+       // {{{
+       /** 
+        * @private Handles blur event
+        */
+       ,
+       onBlur: function(f) {
+               // called by both DateField and TimeField blur events
+               // revert focus to previous field if clicked in between
+               if (this.wrapClick) {
+                       f.focus();
+                       this.wrapClick = false;
+               }
+               
+               // update underlying value
+               if (f === this.df) {
+                       this.updateDate();
+               }
+               else {
+                       this.updateTime();
+               }
+               this.updateHidden();
+               
+               this.validate();
+               
+               // fire events later
+               (function() {
+                       if (!this.df.hasFocus && !this.tf.hasFocus) {
+                               var v = this.getValue();
+                               if (String(v) !== String(this.startValue)) {
+                                       this.fireEvent("change", this, v, this.startValue);
+                               }
+                               this.hasFocus = false;
+                               this.fireEvent('blur', this);
+                       }
+               }).defer(100, this);
+               
+       }
+       // eo function onBlur
+       // }}}
+       // {{{
+       /**
+        * @private Handles focus event
+        */
+       ,
+       onFocus: function() {
+               if (!this.hasFocus) {
+                       this.hasFocus = true;
+                       this.startValue = this.getValue();
+                       this.fireEvent("focus", this);
+               }
+       }
+       // }}}
+       // {{{
+       /**
+        * @private Just to prevent blur event when clicked in the middle of fields
+        */
+       ,
+       onMouseDown: function(e) {
+               if (!this.disabled) {
+                       this.wrapClick = 'td' === e.target.nodeName.toLowerCase();
+               }
+       }
+       // }}}
+       // {{{
+       /**
+        * @private
+        * Handles Tab and Shift-Tab events
+        */
+       ,
+       onSpecialKey: function(t, e) {
+               var key = e.getKey();
+               if (key === e.TAB) {
+                       if (t === this.df && !e.shiftKey) {
+                               e.stopEvent();
+                               this.tf.focus();
+                       }
+                       if (t === this.tf && e.shiftKey) {
+                               e.stopEvent();
+                               this.df.focus();
+                       }
+                       this.updateValue();
+               }
+               // otherwise it misbehaves in editor grid
+               if (key === e.ENTER) {
+                       this.updateValue();
+               }
+               
+       }
+       // eo function onSpecialKey
+       // }}}
+       // {{{
+       /**
+        * Resets the current field value to the originally loaded value 
+        * and clears any validation messages. See Ext.form.BasicForm.trackResetOnLoad
+        */
+       ,
+       reset: function() {
+               this.df.setValue(this.originalValue);
+               this.tf.setValue(this.originalValue);
+       }
+       // eo function reset
+       // }}}
+       // {{{
+       /**
+        * @private Sets the value of DateField
+        */
+       ,
+       setDate: function(date) {
+               this.df.setValue(date);
+       }
+       // eo function setDate
+       // }}}
+       // {{{
+       /** 
+        * @private Sets the value of TimeField
+        */
+       ,
+       setTime: function(date) {
+               this.tf.setValue(date);
+       }
+       // eo function setTime
+       // }}}
+       // {{{
+       /**
+        * @private
+        * Sets correct sizes of underlying DateField and TimeField
+        * With workarounds for IE bugs
+        */
+       ,
+       setSize: function(w, h) {
+               if (!w) {
+                       return;
+               }
+               if ('below' === this.timePosition) {
+                       this.df.setSize(w, h);
+                       this.tf.setSize(w, h);
+                       if (Ext.isIE) {
+                               this.df.el.up('td').setWidth(w);
+                               this.tf.el.up('td').setWidth(w);
+                       }
+               }
+               else {
+                       this.df.setSize(w - this.timeWidth - 4, h);
+                       this.tf.setSize(this.timeWidth, h);
+                       
+                       if (Ext.isIE) {
+                               this.df.el.up('td').setWidth(w - this.timeWidth - 4);
+                               this.tf.el.up('td').setWidth(this.timeWidth);
+                       }
+               }
+       }
+       // eo function setSize
+       // }}}
+       // {{{
+       /**
+        * @param {Mixed} val Value to set
+        * Sets the value of this field
+        */
+       ,
+       setValue: function(val) {
+               if (!val && true === this.emptyToNow) {
+                       this.setValue(new Date());
+                       return;
+               }
+               else if (!val) {
+                       this.setDate('');
+                       this.setTime('');
+                       this.updateValue();
+                       return;
+               }
+               if ('number' === typeof val) {
+                       val = new Date(val);
+               }
+               else if ('string' === typeof val && this.hiddenFormat) {
+                       val = Date.parseDate(val, this.hiddenFormat);
+               }
+               val = val ? val: new Date(1970, 0, 1, 0, 0, 0);
+               var da;
+               if (val instanceof Date) {
+                       this.setDate(val);
+                       this.setTime(val);
+                       this.dateValue = new Date(Ext.isIE ? val.getTime() : val);
+               }
+               else {
+                       da = val.split(this.dtSeparator);
+                       this.setDate(da[0]);
+                       if (da[1]) {
+                               if (da[2]) {
+                                       // add am/pm part back to time
+                                       da[1] += da[2];
+                               }
+                               this.setTime(da[1]);
+                       }
+               }
+               this.updateValue();
+       }
+       // eo function setValue
+       // }}}
+       // {{{
+       /**
+        * Hide or show this component by boolean
+        * @return {Ext.Component} this
+        */
+       ,
+       setVisible: function(visible) {
+               if (visible) {
+                       this.df.show();
+                       this.tf.show();
+               } else {
+                       this.df.hide();
+                       this.tf.hide();
+               }
+               return this;
+       }
+       // eo function setVisible
+       // }}}
+       //{{{
+       ,
+       show: function() {
+               return this.setVisible(true);
+       }
+       // eo function show
+       //}}}
+       //{{{
+       ,
+       hide: function() {
+               return this.setVisible(false);
+       }
+       // eo function hide
+       //}}}
+       // {{{
+       /**
+        * @private Updates the date part
+        */
+       ,
+       updateDate: function() {
+               
+               var d = this.df.getValue();
+               if (d) {
+                       if (! (this.dateValue instanceof Date)) {
+                               this.initDateValue();
+                               if (!this.tf.getValue()) {
+                                       this.setTime(this.dateValue);
+                               }
+                       }
+                       this.dateValue.setMonth(0);
+                       // because of leap years
+                       this.dateValue.setFullYear(d.getFullYear());
+                       this.dateValue.setMonth(d.getMonth(), d.getDate());
+                       //                        this.dateValue.setDate(d.getDate());
+               }
+               else {
+                       this.dateValue = '';
+                       this.setTime('');
+               }
+       }
+       // eo function updateDate
+       // }}}
+       // {{{
+       /**
+        * @private
+        * Updates the time part
+        */
+       ,
+       updateTime: function() {
+               var t = this.tf.getValue();
+               if (t && !(t instanceof Date)) {
+                       t = Date.parseDate(t, this.tf.format);
+               }
+               if (t && !this.df.getValue()) {
+                       this.initDateValue();
+                       this.setDate(this.dateValue);
+               }
+               if (this.dateValue instanceof Date) {
+                       if (t) {
+                               this.dateValue.setHours(t.getHours());
+                               this.dateValue.setMinutes(t.getMinutes());
+                               this.dateValue.setSeconds(t.getSeconds());
+                       }
+                       else {
+                               this.dateValue.setHours(0);
+                               this.dateValue.setMinutes(0);
+                               this.dateValue.setSeconds(0);
+                       }
+               }
+       }
+       // eo function updateTime
+       // }}}
+       // {{{
+       /**
+        * @private Updates the underlying hidden field value
+        */
+       ,
+       updateHidden: function() {
+               if (this.isRendered) {
+                       var value = this.dateValue instanceof Date ? this.dateValue.format(this.hiddenFormat) : '';
+                       this.el.dom.value = value;
+               }
+       }
+       // }}}
+       // {{{
+       /**
+        * @private Updates all of Date, Time and Hidden
+        */
+       ,
+       updateValue: function() {
+               
+               this.updateDate();
+               this.updateTime();
+               this.updateHidden();
+               
+               return;
+       }
+       // eo function updateValue
+       // }}}
+       // {{{
+       /**
+        * @return {Boolean} true = valid, false = invalid
+        * calls validate methods of DateField and TimeField
+        */
+       ,
+       validate: function() {
+               return this.df.validate() && this.tf.validate();
+       }
+       // eo function validate
+       // }}}
+       // {{{
+       /**
+        * Returns renderer suitable to render this field
+        * @param {Object} Column model config
+        */
+       ,
+       renderer: function(field) {
+               var format = field.editor.dateFormat || Gilbert.lib.ui.DateTimeField.prototype.dateFormat;
+               format += ' ' + (field.editor.timeFormat || Gilbert.lib.ui.DateTimeField.prototype.timeFormat);
+               var renderer = function(val) {
+                       var retval = Ext.util.Format.date(val, format);
+                       return retval;
+               };
+               return renderer;
+       }
+       // eo function renderer
+       // }}}
+});
+// eo extend
+// register xtype
+Ext.reg('gilbertdatetimefield', Gilbert.lib.ui.DateTimeField);