egroupware/etemplate/js/et2_widget_date.js
Andreas Stöckel d486e50a57 phpgwapi:
* Changed way of how "webserverUrl" gets set - any type of data can now be
	  injected into the egw object by creating an object with the data and an
	  entry "prefsOnly" set to true. This allows to ensure, that "webserverUrl"
	  is the first thing that is being set in the egw object (as needed when
	  including new JS/CSS files at runtime)

jsapi:
	* Fixed including JS/CSS files at runtime in other windows than the root
	  window
	* Added "ready" function/module, which provides an alternative to the
	  $j("ready") function. The ready module provides the functionality to
	  postpone calling the "ready" until certain events happened.
	* using jQuery calendar object instead of jscalendar in the calendar
	  function.
	* added "jquery" module which takes care of including all jQuery modules
	  in all windows
	* added possibility for modules to update constants using the "constant"
	  function.
	* added possibility for modules to access certain other modules using
	  the "module" function

etemplate:
	* Using new egw(window).ready function to build the template first if
	  loading has finished.
2012-03-09 15:32:29 +00:00

711 lines
19 KiB
JavaScript

/**
* eGroupWare eTemplate2 - JS Date object
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright Nathan Gray 2011
* @version $Id$
*/
"use strict";
/*egw:uses
jquery.jquery;
jquery.jquery-ui;
lib/date;
et2_core_inputWidget;
et2_core_valueWidget;
*/
/**
* Class which implements the "date" XET-Tag
*/
var et2_date = et2_inputWidget.extend({
attributes: {
"value": {
"type": "any"
},
"type": {
"ignore": false
}
},
init: function() {
this._super.apply(this, arguments);
this.date = new Date();
this.date.setHours(0);
this.date.setMinutes(0);
this.date.setSeconds(0);
this.input = null;
this.createInputWidget();
},
createInputWidget: function() {
this.span = $j(document.createElement("span")).addClass("et2_date");
if(this._type == "date" || this._type == "date-time")
{
this.input_date = $j(document.createElement("input"));
var type=(this._type == "date-timeonly" ? "time" : "text");
this.input_date.addClass("et2_date").attr("type", type).attr("size", 5)
.appendTo(this.span);
}
// If date also has a time
if(this._type == "date-time" || this._type == "date-timeonly")
{
var input_time = $j(document.createElement("input")).attr("type", "time");
if(input_time.attr("type") == "time")
{
this.input_time = input_time;
this.input_time.appendTo(this.span).attr("size", 5);
// Update internal value if control changes
this.input_time.change(this,function(e){e.data.set_value($j(e.target).val());});
}
else
{
// browser doesn't support HTML5 time type
this._make_time_selects(this.span);
}
}
this.setDOMNode(this.span[0]);
// jQuery-UI date picker
this.egw().calendar(this.input_date, this._type == "date-time");
},
_make_time_selects: function (node) {
var timeformat = this.egw().preference("timeformat");
this.input_hours = $j(document.createElement("select"));
for(var i = 0; i < 24; i++)
{
var time = i;
if(timeformat == 12)
{
switch(i)
{
case 0:
time = "12 am";
break;
case 12: time = "12 pm";
break;
default:
time = i % 12 + " " + (i < 12 ? "am" : "pm");
}
}
else if (time < 10)
{
time = "0" + time;
}
var option = $j(document.createElement("option")).attr("value", i).text(time);
option.appendTo(this.input_hours);
}
this.input_hours.appendTo(node).change(this, function(e) {
if(e.data.type == "date-timeonly")
{
e.data.set_value(e.target.options[e.target.selectedIndex].value + ":" + $j('option:selected',e.data.input_minutes).text());
}
else
{
e.data.date.setHours(e.target.options[e.target.selectedIndex].value);
e.data.set_value(e.data.date);
}
});
node.append(":");
this.input_minutes = $j(document.createElement("select"));
for(var i = 0; i < 60; i+=5)
{
var time = i;
if(time < 10)
{
time = "0"+time;
}
var option = $j(document.createElement("option")).attr("value", time).text(time);
option.appendTo(this.input_minutes);
}
this.input_minutes.appendTo(node).change(this, function(e) {
if(e.data.type == "date-timeonly")
{
e.data.set_value($j('option:selected',e.data.input_hours).val() + ":" + e.target.options[e.target.selectedIndex].text);
}
else
{
e.data.date.setMinutes(e.target.options[e.target.selectedIndex].value);
e.data.set_value(e.data.date);
}
});
},
set_type: function(_type) {
this.type = _type;
this.createInputWidget();
},
set_value: function(_value) {
if(_value == null)
{
this.value = _value;
if(this.input_date)
{
this.input_date.val("");
}
if(this.input_time)
{
this.input_time.val("");
}
return;
}
// Handle just time as a string in the form H:i
if(typeof _value == 'string' && isNaN(_value)) {
if(_value.indexOf(":") > 0 && this.type == "date-timeonly") {
this.value = _value;
// HTML5
if(!this.input_hours)
{
return this._super.apply(this, [_value]);
}
else
{
var parts = _value.split(":");
$j("option[value='"+parts[0]+"']",this.input_hours).attr("selected","selected");
if($j("option[value='"+parseInt(parts[1])+"']",this.input_minutes).length == 0)
{
// Selected an option that isn't in the list
var i = parseInt(parts[1]);
var option = $j(document.createElement("option")).attr("value", i).text(i < 10 ? "0"+i : i).attr("selected","selected");
option.appendTo(this.input_minutes);
}
else
{
$j("option[value='"+parts[1]+"']",this.input_minutes).attr("selected","selected");
}
return;
}
} else {
var text = new Date(_value);
// Handle timezone offset - times are already in user time
var localOffset = text.getTimezoneOffset() * 60000;
this.date.setTime(text.valueOf()+localOffset);
_value = Math.round(this.date.valueOf() / 1000);
}
} else if (typeof _value == 'number') {
// Timestamp
// JS dates use milliseconds
this.date.setTime(parseInt(_value)*1000);
} else if (typeof _value == 'object' && _value.date) {
this.date = _value.date;
} else if (typeof _value == 'object' && _value.valueOf) {
this.date = _value;
}
if(this.input_date)
{
this.input_date.datepicker('setDate',this.date);
}
if(this.input_time)
{
this.input_time.val(date("H:i", this.date));
}
if(this.input_hours)
{
$j("option[value='"+date("H",this.date)+"']",this.input_hours).attr("selected","selected");
}
if(this.input_minutes)
{
if($j("option[value='"+parseInt(date("i",this.date))+"']",this.input_minutes).length == 0)
{
// Selected an option that isn't in the list
var i = date("i",this.date);
var option = $j(document.createElement("option")).attr("value", i).text(i).attr("selected","selected");
option.appendTo(this.input_minutes);
} else {
$j("option[value='"+date("i",this.date)+"']",this.input_minutes).attr("selected","selected");
}
}
},
getValue: function() {
if(this._type == "date-timeonly")
{
return this.value;
}
else
{
// Handle timezone offset - times are already in user time
var localOffset = this.date.getTimezoneOffset() * 60000;
return Math.round((this.date.valueOf()-localOffset) / 1000);
}
}
});
et2_register_widget(et2_date, ["date", "date-time", "date-timeonly"]);
var et2_date_duration = et2_date.extend({
attributes: {
"data_format": {
"name": "Data format",
"default": "m",
"type": "string",
"description": "Units to read/store the data. 'd' = days (float), 'h' = hours (float), 'm' = minutes (int)."
},
"display_format": {
"name": "Display format",
"default": "dh",
"type": "string",
"description": "Permitted units for displaying the data. 'd' = days, 'h' = hours, 'm' = minutes. Use combinations to give a choice. Default is 'dh' = days or hours with selectbox."
},
"percent_allowed": {
"name": "Percent allowed",
"default": false,
"type": "boolean",
"description": "Allows to enter a percentage."
},
"hours_per_day": {
"name": "Hours per day",
"default": 8,
"type": "integer",
"description": "Number of hours in a day, for converting between hours and (working) days."
},
"empty_not_0": {
"name": "0 or empty",
"default": false,
"type": "boolean",
"description": "Should the widget differ between 0 and empty, which get then returned as NULL"
},
"short_labels": {
"name": "Short labels",
"default": false,
"type": "boolean",
"description": "use d/h/m instead of day/hour/minute"
}
},
legacyOptions: ["data_format","display_format", "hours_per_day", "empty_not_0", "short_labels"],
time_formats: {"d":"d","h":"h","m":"m"},
init: function() {
this._super.apply(this, arguments);
this.input = null;
// Legacy option put percent in with display format
if(this.options.display_format.indexOf("%") != -1)
{
this.options.percent_allowed = true;
this.options.display_format = this.options.display_format.replace("%","");
}
// Get translations
this.time_formats = {
"d": this.options.short_labels ? this.egw().lang("m") : this.egw().lang("Days"),
"h": this.options.short_labels ? this.egw().lang("h") : this.egw().lang("Hours"),
"m": this.options.short_labels ? this.egw().lang("m") : this.egw().lang("Minutes")
},
this.createInputWidget();
},
createInputWidget: function() {
// Create nodes
this.node = $j(document.createElement("span"));
this.duration = $j(document.createElement("input")).attr("size", "2");
this.node.append(this.duration);
if(this.options.display_format.length > 1)
{
this.format = $j(document.createElement("select"));
this.node.append(this.format);
for(var i = 0; i < this.options.display_format.length; i++) {
this.format.append("<option value='"+this.options.display_format[i]+"'>"+this.time_formats[this.options.display_format[i]]+"</option>");
}
} else {
this.format = $j(document.createElement("<span>"+this.time_formats[this.options.display_format])+"</span>").appendTo(this.node);
}
},
attachToDOM: function() {
var node = this.getInputNode();
if (node)
{
$j(node).bind("change.et2_inputWidget", this, function(e) {
e.data.change(this);
});
}
et2_DOMWidget.prototype.attachToDOM.apply(this, arguments);
},
getDOMNode: function() {
return this.node[0];
},
getInputNode: function() {
return this.duration[0];
},
/**
* Use id on node, same as DOMWidget
*/
set_id: function(_value) {
this.id = _value;
var node = this.getDOMNode(this);
if (node)
{
if (_value != "")
{
node.setAttribute("id", _value);
}
else
{
node.removeAttribute("id");
}
}
},
set_value: function(_value) {
this.options.value = _value;
var display = this._convert_to_display(_value);
// Set display
if(this.duration[0].nodeName == "INPUT")
{
this.duration.val(display.value);
}
else
{
this.duration.text(display.value + " ");
}
// Set unit as figured for display
if(display.unit != this.options.display_format)
{
if(this.format.children().length > 1) {
$j("option[value='"+display.unit+"']",this.format).attr('selected','selected');
}
else
{
this.format.text(this.time_formats[display.unit]);
}
}
},
/**
* Converts the value in data format into value in display format.
*
* @param _value int/float Data in data format
*
* @return Object {value: Value in display format, unit: unit for display}
*/
_convert_to_display: function(_value) {
if (_value)
{
// Put value into minutes for further processing
switch(this.options.data_format)
{
case 'd':
_value *= this.options.hours_per_day;
// fall-through
case 'h':
_value *= 60;
break;
}
}
// Figure out best unit for display
var _unit = this.options.display_format == "d" ? "d" : "h";
if (this.options.data_format.indexOf('m') > -1 && _value && _value < 60)
{
_unit = 'm';
}
else if (this.options.data_format.indexOf('d') > -1 && _value >= 60*this.options.hours_per_day)
{
_unit = 'd';
}
_value = this.options.empty_not_0 && _value === '' || !this.options.empty_not_0 && !_value ? '' :
(_unit == 'm' ? parseInt( _value) : (Math.round((_value / 60.0 / (_unit == 'd' ? this.options.hours_per_day : 1))*100)/100));
if(_value === '') _unit = '';
// use decimal separator from user prefs
var sep = '.';
var format = this.egw().preference('number_format');
if (format && (sep = format[0]) && sep != '.')
{
_value = _value.replace('.',sep);
}
return {value: _value, unit:_unit};
},
/**
* Change displayed value into storage value and return
*/
getValue: function() {
var value = this.duration.val();
if(value === '')
{
return this.options.empty_not_0 ? null : '';
}
// Put value into minutes for further processing
switch(this.format ? this.format.val() : this.options.display_format)
{
case 'd':
value *= this.options.hours_per_day;
// fall-through
case 'h':
value *= 60;
break;
}
switch(this.options.data_format)
{
case 'd':
value /= this.options.hours_per_day;
// fall-through
case 'h':
value /= 60.0;
break;
}
return value;
}
});
et2_register_widget(et2_date_duration, ["date-duration"]);
var et2_date_duration_ro = et2_date_duration.extend([et2_IDetachedDOM],{
createInputWidget: function() {
this.node = $j(document.createElement("span"));
var display = this._convert_to_display(this.options.value);
this.duration = $j(document.createElement("span")).appendTo(this.node);
this.format = $j(document.createElement("span")).appendTo(this.node);
},
/**
* Code for implementing et2_IDetachedDOM
* Fast-clonable read-only widget that only deals with DOM nodes, not the widget tree
*/
/**
* Build a list of attributes which can be set when working in the
* "detached" mode in the _attrs array which is provided
* by the calling code.
*/
getDetachedAttributes: function(_attrs) {
_attrs.push("value");
},
/**
* Returns an array of DOM nodes. The (relativly) same DOM-Nodes have to be
* passed to the "setDetachedAttributes" function in the same order.
*/
getDetachedNodes: function() {
return [this.duration[0], this.format[0]];
},
/**
* Sets the given associative attribute->value array and applies the
* attributes to the given DOM-Node.
*
* @param _nodes is an array of nodes which has to be in the same order as
* the nodes returned by "getDetachedNodes"
* @param _values is an associative array which contains a subset of attributes
* returned by the "getDetachedAttributes" function and sets them to the
* given values.
*/
setDetachedAttributes: function(_nodes, _values) {
for(var i = 0; i < _nodes.length; i++) {
// Clear the node
for (var j = _nodes[i].childNodes.length - 1; j >= 0; j--)
{
_nodes[i].removeChild(_nodes[i].childNodes[j]);
}
}
var display = this._convert_to_display(_values.value);
_nodes[0].appendChild(document.createTextNode(display.value));
_nodes[1].appendChild(document.createTextNode(display.unit));
}
});
et2_register_widget(et2_date_duration_ro, ["date-duration_ro"]);
/**
* et2_date_ro is the readonly implementation of some date widget.
*/
var et2_date_ro = et2_valueWidget.extend([et2_IDetachedDOM], {
/**
* Ignore all more advanced attributes.
*/
attributes: {
"value": {
"type": "integer"
},
"type": {
"ignore": false
}
},
/**
* Internal container for working easily with dates
*/
date: new Date(),
init: function() {
this._super.apply(this, arguments);
this.value = "";
this.span = $j(document.createElement(this.type == "date-since" ? "span" : "time"))
.addClass("et2_date_ro et2_label");
this.setDOMNode(this.span[0]);
},
set_value: function(_value) {
if(typeof _value == 'undefined') _value = 0;
this.value = _value;
if(_value == 0 || _value == null)
{
this.span.attr("datetime", "");
return;
}
if(typeof _value == 'string' && isNaN(_value))
{
var text = new Date(_value);
// Handle timezone offset - times are already in user time, but parse does UTC
var localOffset = text.getTimezoneOffset() * 60000;
// JS dates use milliseconds
this.date.setTime(text.valueOf()+localOffset);
}
else
{
// JS dates use milliseconds
this.date.setTime(parseInt(_value)*1000);
}
var display = this.date.toString();
switch(this._type) {
case "date":
display = date(this.egw().preference('dateformat'), this.date);
break;
case "date-timeonly":
display = date(this.egw().preference('timeformat') == '24' ? 'H:i' : 'g:i a', this.date);
break;
case "date-time":
display = date(this.egw().preference('dateformat') + " " +
(this.egw().preference('timeformat') == '24' ? 'H:i' : 'g:i a'), this.date);
break;
case "date-since":
var unit2label = {
'Y': 'years',
'm': 'month',
'd': 'days',
'H': 'hours',
'i': 'minutes',
's': 'seconds'
};
var unit2s = {
'Y': 31536000,
'm': 2628000,
'd': 86400,
'H': 3600,
'i': 60,
's': 1
};
var d = new Date();
var diff = Math.round(d.valueOf() / 1000) - Math.round(this.date.valueOf()/1000);
display = '';
for(var unit in unit2s)
{
var unit_s = unit2s[unit];
if (diff >= unit_s || unit == 's')
{
display = Math.round(diff/unit_s,1)+' '+this.egw().lang(unit2label[unit]);
break;
}
}
break
}
this.span.attr("datetime", date("Y-m-d H:i:s",this.date)).text(display);
},
/**
* Creates a list of attributes which can be set when working in the
* "detached" mode. The result is stored in the _attrs array which is provided
* by the calling code.
*/
getDetachedAttributes: function(_attrs) {
_attrs.push("value", "class");
},
/**
* Returns an array of DOM nodes. The (relatively) same DOM-Nodes have to be
* passed to the "setDetachedAttributes" function in the same order.
*/
getDetachedNodes: function() {
return [this.span[0]];
},
/**
* Sets the given associative attribute->value array and applies the
* attributes to the given DOM-Node.
*
* @param _nodes is an array of nodes which have to be in the same order as
* the nodes returned by "getDetachedNodes"
* @param _values is an associative array which contains a subset of attributes
* returned by the "getDetachedAttributes" function and sets them to the
* given values.
*/
setDetachedAttributes: function(_nodes, _values) {
this.span = jQuery(_nodes[0]);
this.set_value(_values["value"]);
if(_values["class"])
{
this.span.addClass(_values["class"]);
}
}
});
et2_register_widget(et2_date_ro, ["date_ro", "date-time_ro", "date-since"]);
var et2_date_timeonly_ro = et2_date_ro.extend({
attributes: {
"value": {
"type": "string"
}
},
set_value: function(_value) {
if(this.egw().preference("timeformat") == "12" && _value.indexOf(":") > 0) {
var parts = _value.split(":");
if(parts[0] >= 12) {
this.span.text((parts[0] == "12" ? "12" : parseInt(parts[0])-12)+":"+parts[1]+" pm");
}
else
{
this.span.text(_value + " am");
}
}
else
{
this.span.text(_value);
}
}
});
et2_register_widget(et2_date_timeonly_ro, ["date-timeonly_ro"]);