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) {
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() {
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())) {
590 if (!this.df.getValue()){
591 if (!(this.tf.hasFocus || this.df.hasFocus)) this.df.markInvalid(msg);
593 if (!(this.tf.hasFocus || this.df.hasFocus)) this.tf.markInvalid(msg);
598 // eo function isValid
602 * Returns true if this component is visible
606 isVisible: function() {
607 return this.df.rendered && this.df.getActionEl().isVisible();
609 // eo function isVisible
613 * @private Handles blur event
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) {
621 this.wrapClick = false;
624 // update underlying value
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);
642 this.hasFocus = false;
643 this.fireEvent('blur', this);
648 // eo function onBlur
652 * @private Handles focus event
655 onFocus: function() {
656 if (!this.hasFocus) {
657 this.hasFocus = true;
658 this.startValue = this.getValue();
659 this.fireEvent("focus", this);
665 * @private Just to prevent blur event when clicked in the middle of fields
668 onMouseDown: function(e) {
669 if (!this.disabled) {
670 this.wrapClick = 'td' === e.target.nodeName.toLowerCase();
677 * Handles Tab and Shift-Tab events
680 onSpecialKey: function(t, e) {
681 var key = e.getKey();
683 if (t === this.df && !e.shiftKey) {
687 if (t === this.tf && e.shiftKey) {
693 // otherwise it misbehaves in editor grid
694 if (key === e.ENTER) {
699 // eo function onSpecialKey
703 * Resets the current field value to the originally loaded value
704 * and clears any validation messages. See Ext.form.BasicForm.trackResetOnLoad
708 this.df.setValue(this.originalValue);
709 this.tf.setValue(this.originalValue);
715 * @private Sets the value of DateField
718 setDate: function(date) {
719 this.df.setValue(date);
721 // eo function setDate
725 * @private Sets the value of TimeField
728 setTime: function(date) {
729 this.tf.setValue(date);
731 // eo function setTime
736 * Sets correct sizes of underlying DateField and TimeField
737 * With workarounds for IE bugs
740 setSize: function(w, h) {
744 if ('below' === this.timePosition) {
745 this.df.setSize(w, h);
746 this.tf.setSize(w, h);
748 this.df.el.up('td').setWidth(w);
749 this.tf.el.up('td').setWidth(w);
753 this.df.setSize(w - this.timeWidth - 4, h);
754 this.tf.setSize(this.timeWidth, h);
757 this.df.el.up('td').setWidth(w - this.timeWidth - 4);
758 this.tf.el.up('td').setWidth(this.timeWidth);
762 // eo function setSize
766 * @param {Mixed} val Value to set
767 * Sets the value of this field
770 setValue: function(val) {
771 if (!val && true === this.emptyToNow) {
772 this.setValue(new Date());
781 if ('number' === typeof val) {
784 else if ('string' === typeof val && this.hiddenFormat) {
785 val = Date.parseDate(val, this.hiddenFormat);
787 val = val ? val: new Date(1970, 0, 1, 0, 0, 0);
789 if (val instanceof Date) {
792 this.dateValue = new Date(Ext.isIE ? val.getTime() : val);
795 da = val.split(this.dtSeparator);
799 // add am/pm part back to time
807 // eo function setValue
811 * Hide or show this component by boolean
812 * @return {Ext.Component} this
815 setVisible: function(visible) {
825 // eo function setVisible
830 return this.setVisible(true);
837 return this.setVisible(false);
843 * @private Updates the date part
846 updateDate: function() {
848 var d = this.df.getValue();
850 if (! (this.dateValue instanceof Date)) {
851 this.initDateValue();
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());
863 // eo function updateDate
868 * Updates the time part
871 updateTime: function() {
872 var t = this.tf.getValue();
873 if (t && !(t instanceof Date)) {
874 t = Date.parseDate(t, this.tf.format);
876 if (t && !this.df.getValue()) {
877 this.initDateValue();
879 if (this.dateValue instanceof Date) {
881 this.dateValue.setHours(t.getHours());
882 this.dateValue.setMinutes(t.getMinutes());
883 this.dateValue.setSeconds(t.getSeconds());
886 this.dateValue.setHours(0);
887 this.dateValue.setMinutes(0);
888 this.dateValue.setSeconds(0);
892 // eo function updateTime
896 * @private Updates the underlying hidden field value
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;
908 * @private Updates all of Date, Time and Hidden
911 updateValue: function() {
919 // eo function updateValue
923 * @return {Boolean} true = valid, false = invalid
924 * calls validate methods of DateField and TimeField
927 validate: function() {
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())) {
933 if (!this.df.getValue()){
934 if (!(this.tf.hasFocus || this.df.hasFocus)) this.df.markInvalid(msg);
936 if (!(this.tf.hasFocus || this.df.hasFocus)) this.tf.markInvalid(msg);
941 // eo function validate
945 * Returns renderer suitable to render this field
946 * @param {Object} Column model config
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);
958 // eo function renderer
963 Ext.reg('gilbertdatetimefield', Gilbert.lib.ui.DateTimeField);