1 Ext.ns('Gilbert.lib.ui.forms');
4 Gilbert.lib.ui.forms.ClearableComboBox = Ext.extend(Ext.form.ComboBox, {
6 initComponent: function () {
7 Gilbert.lib.ui.forms.ClearableComboBox.superclass.initComponent.call(this);
11 cls: 'x-form-twin-triggers',
15 src: Ext.BLANK_IMAGE_URL,
17 cls: 'x-form-trigger x-form-clear-trigger',
21 src: Ext.BLANK_IMAGE_URL,
23 cls: 'x-form-trigger ' + this.triggerClass,
29 afterRender: function () {
30 Gilbert.lib.ui.forms.ClearableComboBox.superclass.afterRender.call(this);
32 if (this.value && this.allowBlank) {
33 this.triggers[0].show();
35 this.triggers[0].hide();
39 initTrigger: function () {
40 Ext.form.TwinTriggerField.prototype.initTrigger.call(this);
43 getTriggerWidth: function () {
44 Ext.form.TwinTriggerField.prototype.getTriggerWidth.call(this);
47 onTrigger2Click: function () {
48 this.onTriggerClick();
51 onTrigger1Click: function () {
53 this.triggers[0].hide();
56 setValue: function (v) {
57 Gilbert.lib.ui.forms.ClearableComboBox.superclass.setValue.call(this, v);
59 if (this.value && this.allowBlank) {
60 this.triggers[0].show();
62 this.triggers[0].hide();
66 onDestroy: function () {
67 Ext.destroy(this.triggers);
69 Gilbert.lib.ui.forms.ClearableComboBox.superclass.onDestroy.call(this);
73 Ext.reg('gilbertclearablecombo', Gilbert.lib.ui.forms.ClearableComboBox);
76 Gilbert.lib.ui.forms.ModelChoiceField = Ext.extend(Gilbert.lib.ui.forms.ClearableComboBox, {
78 model_app_label: undefined,
80 model_name: undefined,
84 initComponent: function () {
86 this.model = Gilbert.get_model(this.model_app_label, this.model_name);
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({
94 filters: this.model_filters,
97 this.valueField = 'pk';
98 this.displayField = '__unicode__';
100 this.on('beforequery', function () {
101 delete this.lastQuery;
103 this.store.on('load', function (store, records, options) {
104 this.store_loaded = true;
105 }, this, {single: true});
108 this.on('render', function () {
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);
116 notifyDrop: function (source, e, data) {
117 outer.setValue(data.selections[0].id);
124 Gilbert.lib.ui.forms.ModelChoiceField.superclass.initComponent.call(this);
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});
133 return Gilbert.lib.ui.forms.ModelChoiceField.superclass.setValue.call(this, v);
137 Ext.reg('gilbertmodelchoicefield', Gilbert.lib.ui.forms.ModelChoiceField);
140 Gilbert.lib.ui.forms.MultipleChoiceField = Ext.extend(Ext.ux.form.SuperBoxSelect, {});
141 Ext.reg('gilbertmultiplechoicefield', Gilbert.lib.ui.forms.MultipleChoiceField);
144 Gilbert.lib.ui.forms.ModelMultipleChoiceField = Ext.extend(Gilbert.lib.ui.forms.MultipleChoiceField, {});
145 Ext.reg('gilbertmodelmultiplechoicefield', Gilbert.lib.ui.forms.ModelMultipleChoiceField);
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).
154 * Creates new DateTimeField
156 * @param {Object} config A config object
158 Gilbert.lib.ui.DateTimeField = Ext.extend(Ext.form.Field, {
160 * @cfg {Function} dateValidator A custom validation function to be called during date field
161 * validation (defaults to null)
165 * @cfg {String/Object} defaultAutoCreate DomHelper element spec
166 * Let superclass to create hidden field instead of textbox. Hidden will be submittend to server
174 * @cfg {String} dtSeparator Date - Time separator. Used to split date and time (defaults to ' ' (space))
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)
183 hiddenFormat: 'Y-m-d H:i:s'
185 * @cfg {Boolean} otherToNow Set other field to now() if not explicly filled in (defaults to true)
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)
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')
198 timePosition: 'right'
199 // valid values:'below', 'right'
201 * @cfg {Function} timeValidator A custom validation function to be called during time field
202 * validation (defaults to null)
207 * @cfg {Number} timeWidth Width of time field in pixels (defaults to 100)
212 * @cfg {String} dateFormat Format of DateField. Can be localized. (defaults to 'm/y/d')
217 * @cfg {String} timeFormat Format of TimeField. Can be localized. (defaults to 'g:i A')
222 * @cfg {Object} dateConfig Config for DateField constructor.
225 * @cfg {Object} timeConfig Config for TimeField constructor.
231 * creates DateField and TimeField and installs the necessary event handlers
234 initComponent: function() {
235 // call parent initComponent
236 Gilbert.lib.ui.DateTimeField.superclass.initComponent.call(this);
239 var dateConfig = Ext.apply({},
241 id: this.id + '-date'
243 format: this.dateFormat || Ext.form.DateField.prototype.format
245 width: this.timeWidth
247 selectOnFocus: this.selectOnFocus
249 validator: this.dateValidator
264 this.df = new Ext.form.DateField(dateConfig);
265 this.df.ownerCt = this;
266 delete(this.dateFormat);
269 var timeConfig = Ext.apply({},
271 id: this.id + '-time'
273 format: this.timeFormat || Ext.form.TimeField.prototype.format
275 width: this.timeWidth
277 selectOnFocus: this.selectOnFocus
279 validator: this.timeValidator
294 this.tf = new Ext.form.TimeField(timeConfig);
295 this.tf.ownerCt = this;
296 delete(this.timeFormat);
299 this.relayEvents(this.df, ['focus', 'specialkey', 'invalid', 'valid']);
300 this.relayEvents(this.tf, ['focus', 'specialkey', 'invalid', 'valid']);
302 this.on('specialkey', this.onSpecialKey, this);
305 // eo function initComponent
310 * Renders underlying DateField and TimeField and provides a workaround for side error icon bug
313 onRender: function(ct, position) {
314 // don't run more than once
315 if (this.isRendered) {
319 // render underlying hidden field
320 Gilbert.lib.ui.DateTimeField.superclass.onRender.call(this, ct, position);
322 // render DateField and TimeField
323 // create bounding table
325 if ('below' === this.timePosition || 'bellow' === this.timePosition) {
326 t = Ext.DomHelper.append(ct, {
328 style: 'border-collapse:collapse',
334 style: 'padding-bottom:1px',
335 cls: 'ux-datetime-date'
343 cls: 'ux-datetime-time'
351 t = Ext.DomHelper.append(ct, {
353 style: 'border-collapse:collapse',
360 style: 'padding-right:4px',
361 cls: 'ux-datetime-date'
365 cls: 'ux-datetime-time'
376 cls: 'x-form-field-wrap'
378 // this.wrap = t.wrap();
379 this.wrap.on("mousedown", this.onMouseDown, this, {
383 // render DateField & TimeField
384 this.df.render(t.child('td.ux-datetime-date'));
385 this.tf.render(t.child('td.ux-datetime-time'));
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});
392 this.df.el.swallowEvent(['keydown', 'keypress']);
393 this.tf.el.swallowEvent(['keydown', 'keypress']);
395 // create icon for side invalid errorIcon
396 if ('side' === this.msgTarget) {
397 var elp = this.el.findParent('.x-form-element', 10, true);
399 this.errorIcon = elp.createChild({
400 cls: 'x-form-invalid-icon'
405 errorIcon: this.errorIcon
409 alignErrorIcon: this.alignErrorIcon.createDelegate(this)
411 Ext.apply(this.df, o);
412 Ext.apply(this.tf, o);
413 // this.df.errorIcon = this.errorIcon;
414 // this.tf.errorIcon = this.errorIcon;
417 // setup name for submit
418 this.el.dom.name = this.hiddenName || this.name || this.id;
420 // prevent helper fields from being submitted
421 this.df.el.dom.removeAttribute("name");
422 this.tf.el.dom.removeAttribute("name");
424 // we're rendered flag
425 this.isRendered = true;
427 // update hidden field
431 // eo function onRender
438 adjustSize: Ext.BoxComponent.prototype.adjustSize
445 alignErrorIcon: function() {
446 this.errorIcon.alignTo(this.tableEl, 'tl-tr', [2, 0]);
451 * @private initializes internal dateValue
454 initDateValue: function() {
455 this.dateValue = this.otherToNow ? new Date() : new Date(1970, 0, 1, 0, 0, 0);
460 * Calls clearInvalid on the DateField and TimeField
463 clearInvalid: function() {
464 this.df.clearInvalid();
465 this.tf.clearInvalid();
467 // eo function clearInvalid
471 * Calls markInvalid on both DateField and TimeField
472 * @param {String} msg Invalid message to display
475 markInvalid: function(msg) {
476 this.df.markInvalid(msg);
477 this.tf.markInvalid(msg);
479 // eo function markInvalid
484 * called from Component::destroy.
485 * Destroys all elements and removes all listeners we've created.
488 beforeDestroy: function() {
489 if (this.isRendered) {
490 // this.removeAllListeners();
491 this.wrap.removeAllListeners();
493 this.tableEl.remove();
498 // eo function beforeDestroy
502 * Disable this component.
503 * @return {Ext.Component} this
506 disable: function() {
507 if (this.isRendered) {
508 this.df.disabled = this.disabled;
512 this.disabled = true;
513 this.df.disabled = true;
514 this.tf.disabled = true;
515 this.fireEvent("disable", this);
518 // eo function disable
522 * Enable this component.
523 * @return {Ext.Component} this
531 this.disabled = false;
532 this.df.disabled = false;
533 this.tf.disabled = false;
534 this.fireEvent("enable", this);
537 // eo function enable
541 * @private Focus date filed
554 getPositionEl: function() {
563 getResizeEl: function() {
569 * @return {Date/String} Returns value of this field
572 getValue: function() {
573 // create new instance of date
574 return this.dateValue ? new Date(this.dateValue) : '';
576 // eo function getValue
580 * @return {Boolean} true = valid, false = invalid
581 * @private Calls isValid methods of underlying DateField and TimeField and returns the result
584 isValid: function() {
585 return this.df.isValid() && this.tf.isValid();
587 // eo function isValid
591 * Returns true if this component is visible
595 isVisible: function() {
596 return this.df.rendered && this.df.getActionEl().isVisible();
598 // eo function isVisible
602 * @private Handles blur event
605 onBlur: function(f) {
606 // called by both DateField and TimeField blur events
607 // revert focus to previous field if clicked in between
608 if (this.wrapClick) {
610 this.wrapClick = false;
613 // update underlying value
626 if (!this.df.hasFocus && !this.tf.hasFocus) {
627 var v = this.getValue();
628 if (String(v) !== String(this.startValue)) {
629 this.fireEvent("change", this, v, this.startValue);
631 this.hasFocus = false;
632 this.fireEvent('blur', this);
637 // eo function onBlur
641 * @private Handles focus event
644 onFocus: function() {
645 if (!this.hasFocus) {
646 this.hasFocus = true;
647 this.startValue = this.getValue();
648 this.fireEvent("focus", this);
654 * @private Just to prevent blur event when clicked in the middle of fields
657 onMouseDown: function(e) {
658 if (!this.disabled) {
659 this.wrapClick = 'td' === e.target.nodeName.toLowerCase();
666 * Handles Tab and Shift-Tab events
669 onSpecialKey: function(t, e) {
670 var key = e.getKey();
672 if (t === this.df && !e.shiftKey) {
676 if (t === this.tf && e.shiftKey) {
682 // otherwise it misbehaves in editor grid
683 if (key === e.ENTER) {
688 // eo function onSpecialKey
692 * Resets the current field value to the originally loaded value
693 * and clears any validation messages. See Ext.form.BasicForm.trackResetOnLoad
697 this.df.setValue(this.originalValue);
698 this.tf.setValue(this.originalValue);
704 * @private Sets the value of DateField
707 setDate: function(date) {
708 this.df.setValue(date);
710 // eo function setDate
714 * @private Sets the value of TimeField
717 setTime: function(date) {
718 this.tf.setValue(date);
720 // eo function setTime
725 * Sets correct sizes of underlying DateField and TimeField
726 * With workarounds for IE bugs
729 setSize: function(w, h) {
733 if ('below' === this.timePosition) {
734 this.df.setSize(w, h);
735 this.tf.setSize(w, h);
737 this.df.el.up('td').setWidth(w);
738 this.tf.el.up('td').setWidth(w);
742 this.df.setSize(w - this.timeWidth - 4, h);
743 this.tf.setSize(this.timeWidth, h);
746 this.df.el.up('td').setWidth(w - this.timeWidth - 4);
747 this.tf.el.up('td').setWidth(this.timeWidth);
751 // eo function setSize
755 * @param {Mixed} val Value to set
756 * Sets the value of this field
759 setValue: function(val) {
760 if (!val && true === this.emptyToNow) {
761 this.setValue(new Date());
770 if ('number' === typeof val) {
773 else if ('string' === typeof val && this.hiddenFormat) {
774 val = Date.parseDate(val, this.hiddenFormat);
776 val = val ? val: new Date(1970, 0, 1, 0, 0, 0);
778 if (val instanceof Date) {
781 this.dateValue = new Date(Ext.isIE ? val.getTime() : val);
784 da = val.split(this.dtSeparator);
788 // add am/pm part back to time
796 // eo function setValue
800 * Hide or show this component by boolean
801 * @return {Ext.Component} this
804 setVisible: function(visible) {
814 // eo function setVisible
819 return this.setVisible(true);
826 return this.setVisible(false);
832 * @private Updates the date part
835 updateDate: function() {
837 var d = this.df.getValue();
839 if (! (this.dateValue instanceof Date)) {
840 this.initDateValue();
841 if (!this.tf.getValue()) {
842 this.setTime(this.dateValue);
845 this.dateValue.setMonth(0);
846 // because of leap years
847 this.dateValue.setFullYear(d.getFullYear());
848 this.dateValue.setMonth(d.getMonth(), d.getDate());
849 // this.dateValue.setDate(d.getDate());
856 // eo function updateDate
861 * Updates the time part
864 updateTime: function() {
865 var t = this.tf.getValue();
866 if (t && !(t instanceof Date)) {
867 t = Date.parseDate(t, this.tf.format);
869 if (t && !this.df.getValue()) {
870 this.initDateValue();
871 this.setDate(this.dateValue);
873 if (this.dateValue instanceof Date) {
875 this.dateValue.setHours(t.getHours());
876 this.dateValue.setMinutes(t.getMinutes());
877 this.dateValue.setSeconds(t.getSeconds());
880 this.dateValue.setHours(0);
881 this.dateValue.setMinutes(0);
882 this.dateValue.setSeconds(0);
886 // eo function updateTime
890 * @private Updates the underlying hidden field value
893 updateHidden: function() {
894 if (this.isRendered) {
895 var value = this.dateValue instanceof Date ? this.dateValue.format(this.hiddenFormat) : '';
896 this.el.dom.value = value;
902 * @private Updates all of Date, Time and Hidden
905 updateValue: function() {
913 // eo function updateValue
917 * @return {Boolean} true = valid, false = invalid
918 * calls validate methods of DateField and TimeField
921 validate: function() {
922 return this.df.validate() && this.tf.validate();
924 // eo function validate
928 * Returns renderer suitable to render this field
929 * @param {Object} Column model config
932 renderer: function(field) {
933 var format = field.editor.dateFormat || Gilbert.lib.ui.DateTimeField.prototype.dateFormat;
934 format += ' ' + (field.editor.timeFormat || Gilbert.lib.ui.DateTimeField.prototype.timeFormat);
935 var renderer = function(val) {
936 var retval = Ext.util.Format.date(val, format);
941 // eo function renderer
946 Ext.reg('gilbertdatetimefield', Gilbert.lib.ui.DateTimeField);