@qooxdoo/framework
Version:
The JS Framework for Coders
610 lines (495 loc) • 15.6 kB
JavaScript
/* ************************************************************************
qooxdoo - the new era of web development
http://qooxdoo.org
Copyright:
2004-2008 1&1 Internet AG, Germany, http://www.1und1.de
License:
MIT: https://opensource.org/licenses/MIT
See the LICENSE file in the project's top-level directory for details.
Authors:
* Martin Wittemann (martinwittemann)
************************************************************************ */
/**
* A *date field* is like a combo box with the date as popup. As button to
* open the calendar a calendar icon is shown at the right to the textfield.
*
* To be conform with all form widgets, the {@link qx.ui.form.IForm} interface
* is implemented.
*
* The following example creates a date field and sets the current
* date as selected.
*
* <pre class='javascript'>
* var dateField = new qx.ui.form.DateField();
* this.getRoot().add(dateField, {top: 20, left: 20});
* dateField.setValue(new Date());
* </pre>
*
* @childControl list {qx.ui.control.DateChooser} date chooser component
* @childControl popup {qx.ui.popup.Popup} popup which shows the list control
* @childControl textfield {qx.ui.form.TextField} text field for manual date entry
* @childControl button {qx.ui.form.Button} button that opens the list control
*/
qx.Class.define("qx.ui.form.DateField",
{
extend : qx.ui.core.Widget,
include : [
qx.ui.core.MRemoteChildrenHandling,
qx.ui.form.MForm
],
implement : [
qx.ui.form.IForm,
qx.ui.form.IDateForm
],
/*
*****************************************************************************
CONSTRUCTOR
*****************************************************************************
*/
construct : function()
{
this.base(arguments);
// set the layout
var layout = new qx.ui.layout.HBox();
this._setLayout(layout);
layout.setAlignY("middle");
// text field
var textField = this._createChildControl("textfield");
this._createChildControl("button");
// register listeners
this.addListener("tap", this._onTap, this);
this.addListener("blur", this._onBlur, this);
// forward the focusin and focusout events to the textfield. The textfield
// is not focusable so the events need to be forwarded manually.
this.addListener("focusin", function(e) {
textField.fireNonBubblingEvent("focusin", qx.event.type.Focus);
}, this);
this.addListener("focusout", function(e) {
textField.fireNonBubblingEvent("focusout", qx.event.type.Focus);
}, this);
// initializes the DateField with the default format
this._setDefaultDateFormat();
// adds a locale change listener
this._addLocaleChangeListener();
},
/*
*****************************************************************************
EVENTS
*****************************************************************************
*/
events :
{
/** Whenever the value is changed this event is fired
*
* Event data: The new text value of the field.
*/
"changeValue" : "qx.event.type.Data"
},
/*
*****************************************************************************
PROPERTIES
*****************************************************************************
*/
properties :
{
/** The formatter, which converts the selected date to a string. **/
dateFormat :
{
check : "qx.util.format.DateFormat",
apply : "_applyDateFormat"
},
/**
* String value which will be shown as a hint if the field is all of:
* unset, unfocused and enabled. Set to null to not show a placeholder
* text.
*/
placeholder :
{
check : "String",
nullable : true,
apply : "_applyPlaceholder"
},
// overridden
appearance :
{
refine : true,
init : "datefield"
},
// overridden
focusable :
{
refine : true,
init : true
},
// overridden
width :
{
refine : true,
init : 120
}
},
/*
*****************************************************************************
MEMBERS
*****************************************************************************
*/
statics :
{
__dateFormat : null,
__formatter : null,
/**
* Get the shared default date formatter
*
* @return {qx.util.format.DateFormat} The shared date formatter
*/
getDefaultDateFormatter : function()
{
var format = qx.locale.Date.getDateFormat("medium").toString();
if (format == this.__dateFormat) {
return this.__formatter;
}
if (this.__formatter) {
this.__formatter.dispose();
}
this.__formatter = new qx.util.format.DateFormat(format, qx.locale.Manager.getInstance().getLocale());
this.__dateFormat = format;
return this.__formatter;
}
},
/*
*****************************************************************************
MEMBERS
*****************************************************************************
*/
members :
{
__localeListenerId : null,
/**
* @lint ignoreReferenceField(_forwardStates)
*/
_forwardStates : {
focused : true,
invalid : true
},
/*
---------------------------------------------------------------------------
PROTECTED METHODS
---------------------------------------------------------------------------
*/
/**
* Sets the default date format which is returned by
* {@link #getDefaultDateFormatter}. You can override this method to
* define your own default format.
*/
_setDefaultDateFormat : function() {
this.setDateFormat(qx.ui.form.DateField.getDefaultDateFormatter());
},
/**
* Checks for "qx.dynlocale" and adds a listener to the locale changes.
* On every change, {@link #_setDefaultDateFormat} is called to reinitialize
* the format. You can easily override that method to prevent that behavior.
*/
_addLocaleChangeListener : function() {
// listen for locale changes
if (qx.core.Environment.get("qx.dynlocale"))
{
this.__localeListenerId =
qx.locale.Manager.getInstance().addListener("changeLocale", function() {
this._setDefaultDateFormat();
}, this);
}
},
/*
---------------------------------------------------------------------------
PUBLIC METHODS
---------------------------------------------------------------------------
*/
/**
* This method sets the date, which will be formatted according to
* #dateFormat to the date field. It will also select the date in the
* calendar popup.
*
* @param value {Date} The date to set.
*/
setValue : function(value)
{
// set the date to the textfield
var textField = this.getChildControl("textfield");
textField.setValue(this.getDateFormat().format(value));
// set the date in the datechooser
var dateChooser = this.getChildControl("list");
dateChooser.setValue(value);
},
/**
* Returns the current set date, parsed from the input-field
* corresponding to the {@link #dateFormat}.
* If the given text could not be parsed, <code>null</code> will be returned.
*
* @return {Date} The currently set date.
*/
getValue : function()
{
// get the value of the textfield
var textfieldValue = this.getChildControl("textfield").getValue();
// return the parsed date
try {
if (textfieldValue == null || textfieldValue.length == 0) {
return null;
}
return this.getDateFormat().parse(textfieldValue);
} catch (ex) {
return null;
}
},
/**
* Resets the DateField. The textfield will be empty and the datechooser
* will also have no selection.
*/
resetValue: function()
{
// set the date to the textfield
var textField = this.getChildControl("textfield");
textField.setValue("");
// set the date in the datechooser
var dateChooser = this.getChildControl("list");
dateChooser.setValue(null);
},
/*
---------------------------------------------------------------------------
LIST STUFF
---------------------------------------------------------------------------
*/
/**
* Shows the date chooser popup.
*/
open : function()
{
var popup = this.getChildControl("popup");
popup.placeToWidget(this, true);
popup.show();
},
/**
* Hides the date chooser popup.
*/
close : function() {
this.getChildControl("popup").hide();
},
/**
* Toggles the date chooser popup visibility.
*/
toggle : function()
{
var isListOpen = this.getChildControl("popup").isVisible();
if (isListOpen) {
this.close();
} else {
this.open();
}
},
/*
---------------------------------------------------------------------------
PROPERTY APPLY METHODS
---------------------------------------------------------------------------
*/
// property apply routine
_applyDateFormat : function(value, old)
{
// if old is undefined or null do nothing
if (!old) {
return;
}
// get the date with the old date format
try
{
var textfield = this.getChildControl("textfield");
var dateStr = textfield.getValue();
if (dateStr != null) {
var currentDate = old.parse(dateStr);
textfield.setValue(value.format(currentDate));
}
}
catch (ex) {
// do nothing if the former date could not be parsed
}
},
// property apply routine
_applyPlaceholder : function(value, old) {
this.getChildControl("textfield").setPlaceholder(value);
},
/*
---------------------------------------------------------------------------
WIDGET API
---------------------------------------------------------------------------
*/
// overridden
_createChildControlImpl : function(id, hash)
{
var control;
switch(id)
{
case "textfield":
control = new qx.ui.form.TextField();
control.setFocusable(false);
control.addState("inner");
control.addListener("changeValue", this._onTextFieldChangeValue, this);
control.addListener("blur", this.close, this);
this._add(control, {flex: 1});
break;
case "button":
control = new qx.ui.form.Button();
control.setFocusable(false);
control.setKeepActive(true);
control.addState("inner");
control.addListener("execute", this.toggle, this);
this._add(control);
break;
case "list":
control = new qx.ui.control.DateChooser();
control.setFocusable(false);
control.setKeepFocus(true);
control.addListener("execute", this._onChangeDate, this);
break;
case "popup":
control = new qx.ui.popup.Popup(new qx.ui.layout.VBox);
control.setAutoHide(false);
control.add(this.getChildControl("list"));
control.addListener("pointerup", this._onChangeDate, this);
control.addListener("changeVisibility", this._onPopupChangeVisibility, this);
break;
}
return control || this.base(arguments, id);
},
/*
---------------------------------------------------------------------------
EVENT LISTENERS
---------------------------------------------------------------------------
*/
/**
* Handler method which handles the tap on the calender popup.
*
* @param e {qx.event.type.Pointer} The pointer event.
*/
_onChangeDate : function(e)
{
var textField = this.getChildControl("textfield");
var selectedDate = this.getChildControl("list").getValue();
textField.setValue(this.getDateFormat().format(selectedDate));
this.close();
},
/**
* Toggles the popup's visibility.
*
* @param e {qx.event.type.Pointer} Pointer tap event
*/
_onTap : function(e) {
this.close();
},
/**
* Handler for the blur event of the current widget.
*
* @param e {qx.event.type.Focus} The blur event.
*/
_onBlur : function(e) {
this.close();
},
/**
* Handler method which handles the key press. It forwards all key event
* to the opened date chooser except the escape key event. Escape closes
* the popup.
* If the list is cloned, all key events will not be processed further.
*
* @param e {qx.event.type.KeySequence} Keypress event
*/
_onKeyPress : function(e)
{
// get the key identifier
var iden = e.getKeyIdentifier();
if (iden == "Down" && e.isAltPressed())
{
this.toggle();
e.stopPropagation();
return;
}
// if the popup is closed, ignore all
var popup = this.getChildControl("popup");
if (popup.getVisibility() == "hidden") {
return;
}
// hide the list always on escape
if (iden == "Escape")
{
this.close();
e.stopPropagation();
return;
}
// Stop navigation keys when popup is open
if (iden === "Left" || iden === "Right" || iden === "Down" || iden === "Up") {
e.preventDefault();
}
// forward the rest of the events to the date chooser
this.getChildControl("list").handleKeyPress(e);
},
/**
* Redirects changeVisibility event from the list to this widget.
*
* @param e {qx.event.type.Data} Property change event
*/
_onPopupChangeVisibility : function(e)
{
e.getData() == "visible" ? this.addState("popupOpen") : this.removeState("popupOpen");
// Synchronize the chooser with the current value on every
// opening of the popup. This is needed when the value has been
// modified and not saved yet (e.g. no blur)
var popup = this.getChildControl("popup");
if (popup.isVisible())
{
var chooser = this.getChildControl("list");
var date = this.getValue();
chooser.setValue(date);
}
},
/**
* Reacts on value changes of the text field and syncs the
* value to the combobox.
*
* @param e {qx.event.type.Data} Change event
*/
_onTextFieldChangeValue : function(e)
{
// Apply to popup
var date = this.getValue();
if (date != null)
{
var list = this.getChildControl("list");
list.setValue(date);
}
// Fire event
this.fireDataEvent("changeValue", this.getValue());
},
/**
* Checks if the textfield of the DateField is empty.
*
* @return {Boolean} True, if the textfield of the DateField is empty.
*/
isEmpty: function()
{
var value = this.getChildControl("textfield").getValue();
return value == null || value == "";
},
// overridden
focus : function()
{
this.base(arguments);
this.getChildControl("textfield").getFocusElement().focus();
}
},
destruct : function() {
// listen for locale changes
if (qx.core.Environment.get("qx.dynlocale"))
{
if (this.__localeListenerId) {
qx.locale.Manager.getInstance().removeListenerById(this.__localeListenerId);
}
}
}
});