Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
578 lines
13 KiB
JavaScript
Executable file
578 lines
13 KiB
JavaScript
Executable file
/* global mozIntl */
|
|
define(function(require, exports) {
|
|
'use strict';
|
|
|
|
var Timespan = require('./timespan');
|
|
var co = require('ext/co');
|
|
|
|
const SECOND = 1000;
|
|
const MINUTE = (SECOND * 60);
|
|
const HOUR = MINUTE * 60;
|
|
|
|
exports._hourDate = new Date();
|
|
exports.startDay = 0;
|
|
exports.FLOATING = 'floating';
|
|
exports.ALLDAY = 'allday';
|
|
exports.SECOND = SECOND;
|
|
exports.MINUTE = MINUTE;
|
|
exports.HOUR = HOUR;
|
|
exports.PAST = 'past';
|
|
exports.NEXT_MONTH = 'next-month';
|
|
exports.OTHER_MONTH = 'other-month';
|
|
exports.PRESENT = 'present';
|
|
exports.FUTURE = 'future';
|
|
|
|
Object.defineProperty(exports, 'today', {
|
|
get: function() {
|
|
return new Date();
|
|
}
|
|
});
|
|
|
|
exports.daysInWeek = function() {
|
|
// XXX: We need to add this to mozIntl.calendarInfo
|
|
return 7;
|
|
};
|
|
|
|
/**
|
|
* Calculates day of week when starting day is Monday.
|
|
*/
|
|
exports.dayOfWeekFromMonday = function(numeric) {
|
|
var day = numeric - 1;
|
|
if (day < 0) {
|
|
return 6;
|
|
}
|
|
return day;
|
|
};
|
|
|
|
/**
|
|
* Calculates day of week from startDay value
|
|
* passed by the locale currently being used
|
|
*/
|
|
exports.dayOfWeekFromStartDay = function(numeric) {
|
|
var day = numeric - exports.startDay;
|
|
if (day < 0) {
|
|
return 7 + day;
|
|
}
|
|
return day;
|
|
};
|
|
|
|
/**
|
|
* Checks is given date is today.
|
|
*
|
|
* @param {Date} date compare.
|
|
* @return {Boolean} true when today.
|
|
*/
|
|
exports.isToday = function(date) {
|
|
return exports.isSameDate(date, exports.today);
|
|
};
|
|
|
|
/**
|
|
* Checks if date object only contains date information (not time).
|
|
*
|
|
* Example:
|
|
*
|
|
* var time = new Date(2012, 0, 1, 1);
|
|
* this._isOnlyDate(time); // false
|
|
*
|
|
* var time = new Date(2012, 0, 1);
|
|
* this._isOnlyDate(time); // true
|
|
*
|
|
* @param {Date} date to verify.
|
|
* @return {Boolean} see above.
|
|
*/
|
|
exports.isOnlyDate = function(date) {
|
|
if (
|
|
date.getHours() === 0 &&
|
|
date.getMinutes() === 0 &&
|
|
date.getSeconds() === 0
|
|
) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* Calculates the difference between
|
|
* two points in hours.
|
|
*
|
|
* @param {Date|Numeric} start start hour.
|
|
* @param {Date|Numeric} end end hour.
|
|
*/
|
|
exports.hourDiff = function(start, end) {
|
|
start = (start instanceof Date) ? start.valueOf() : start;
|
|
end = (end instanceof Date) ? end.valueOf() : end;
|
|
|
|
start = start / HOUR;
|
|
end = end / HOUR;
|
|
|
|
return end - start;
|
|
};
|
|
|
|
/**
|
|
* Creates timespan for given day.
|
|
*
|
|
* @param {Date} date date of span.
|
|
* @param {Boolean} includeTime uses given date
|
|
* as the start time of the timespan
|
|
* rather then the absolute start of
|
|
* the day of the given date.
|
|
*/
|
|
exports.spanOfDay = function(date, includeTime) {
|
|
if (typeof(includeTime) === 'undefined') {
|
|
date = exports.createDay(date);
|
|
}
|
|
|
|
var end = exports.createDay(date);
|
|
end.setDate(end.getDate() + 1);
|
|
return new Timespan(date, end);
|
|
};
|
|
|
|
/**
|
|
* Creates timespan for a given month.
|
|
* Starts at the first week that occurs
|
|
* in the given month. Ends at the
|
|
* last day, minute, second of given month.
|
|
*/
|
|
exports.spanOfMonth = function(month) {
|
|
month = exports.monthStart(month);
|
|
var startDay = exports.getWeekStartDate(month);
|
|
var endDay = exports.monthEnd(month);
|
|
endDay = exports.getWeekEndDate(endDay);
|
|
return new Timespan(startDay, endDay);
|
|
};
|
|
|
|
exports.monthEnd = function(date, diff = 0) {
|
|
var endDay = new Date(
|
|
date.getFullYear(),
|
|
date.getMonth() + diff + 1,
|
|
1
|
|
);
|
|
endDay.setMilliseconds(-1);
|
|
return endDay;
|
|
};
|
|
|
|
/**
|
|
* Converts a date to UTC
|
|
*/
|
|
exports.getUTC = function(date) {
|
|
return new Date(
|
|
date.getUTCFullYear(),
|
|
date.getUTCMonth(),
|
|
date.getUTCDate(),
|
|
date.getUTCHours(),
|
|
date.getUTCMinutes(),
|
|
date.getUTCSeconds(),
|
|
date.getUTCMilliseconds()
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Converts transport time into a JS Date object.
|
|
*
|
|
* @param {Object} transport date in transport format.
|
|
* @return {Date} javascript date converts the transport into
|
|
* the current time.
|
|
*/
|
|
exports.dateFromTransport = function(transport) {
|
|
var utc = transport.utc;
|
|
var offset = transport.offset;
|
|
var zone = transport.tzid;
|
|
|
|
var date = new Date(
|
|
// offset is expected to be 0 in the floating case
|
|
parseInt(utc) - parseInt(offset)
|
|
);
|
|
|
|
if (zone && zone === exports.FLOATING) {
|
|
return exports.getUTC(date);
|
|
}
|
|
|
|
return date;
|
|
};
|
|
|
|
/**
|
|
* Converts a date object into a transport value
|
|
* which can be stored in the database or sent
|
|
* to a service.
|
|
*
|
|
* When the tzid value is given an is the string
|
|
* value of "floating" it will convert the local
|
|
* time directly to UTC zero and record no offset.
|
|
* This along with the tzid is understood to be
|
|
* a "floating" time which will occur at that position
|
|
* regardless of the current tzid's offset.
|
|
*
|
|
* @param {Date} date js date object.
|
|
* @param {String} [tzid] optional tzid.
|
|
* @param {Boolean} isDate true when is a "date" representation.
|
|
*/
|
|
exports.dateToTransport = function(date, tzid, isDate) {
|
|
var result = Object.create(null);
|
|
|
|
if (isDate) {
|
|
result.isDate = isDate;
|
|
}
|
|
|
|
if (tzid) {
|
|
result.tzid = tzid;
|
|
}
|
|
|
|
var utc = Date.UTC(
|
|
date.getFullYear(),
|
|
date.getMonth(),
|
|
date.getDate(),
|
|
date.getHours(),
|
|
date.getMinutes(),
|
|
date.getSeconds(),
|
|
date.getMilliseconds()
|
|
);
|
|
|
|
// remember a "date" is always a floating
|
|
// point in time otherwise we don't use it...
|
|
if (isDate || tzid && tzid === exports.FLOATING) {
|
|
result.utc = utc;
|
|
result.offset = 0;
|
|
result.tzid = exports.FLOATING;
|
|
} else {
|
|
var localUtc = date.valueOf();
|
|
var offset = utc - localUtc;
|
|
|
|
result.utc = utc;
|
|
result.offset = offset;
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
/**
|
|
* Checks if two date objects occur
|
|
* on the same date (in the same month, year, day).
|
|
* Disregards time.
|
|
*
|
|
* @param {Date} first date.
|
|
* @param {Date} second date.
|
|
* @return {Boolean} true when they are the same date.
|
|
*/
|
|
exports.isSameDate = function(first, second) {
|
|
return first.getMonth() == second.getMonth() &&
|
|
first.getDate() == second.getDate() &&
|
|
first.getFullYear() == second.getFullYear();
|
|
};
|
|
|
|
/**
|
|
* Returns an identifier for a specific
|
|
* date in time for a given date
|
|
*
|
|
* @param {Date} date to get id for.
|
|
* @return {String} identifier.
|
|
*/
|
|
exports.getDayId = function(date) {
|
|
return [
|
|
'd',
|
|
date.getFullYear(),
|
|
date.getMonth(),
|
|
date.getDate()
|
|
].join('-');
|
|
};
|
|
|
|
/**
|
|
* Returns a date object from
|
|
* a string id for a date.
|
|
*
|
|
* @param {String} id identifier for date.
|
|
* @return {Date} date output.
|
|
*/
|
|
exports.dateFromId = function(id) {
|
|
var parts = id.split('-'),
|
|
date,
|
|
type;
|
|
|
|
if (parts.length > 1) {
|
|
type = parts.shift();
|
|
switch (type) {
|
|
case 'd':
|
|
date = new Date(parts[0], parts[1], parts[2]);
|
|
break;
|
|
case 'm':
|
|
date = new Date(parts[0], parts[1]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return date;
|
|
};
|
|
|
|
/**
|
|
* Returns an identifier for a specific
|
|
* month in time for a given date.
|
|
*
|
|
* @return {String} identifier.
|
|
*/
|
|
exports.getMonthId = function(date) {
|
|
return [
|
|
'm',
|
|
date.getFullYear(),
|
|
date.getMonth()
|
|
].join('-');
|
|
};
|
|
|
|
exports.createDay = function(date, day, month, year) {
|
|
return new Date(
|
|
year != null ? year : date.getFullYear(),
|
|
month != null ? month : date.getMonth(),
|
|
day != null ? day : date.getDate()
|
|
);
|
|
};
|
|
|
|
exports.endOfDay = function(date) {
|
|
var day = exports.createDay(date, date.getDate() + 1);
|
|
day.setMilliseconds(-1);
|
|
return day;
|
|
};
|
|
|
|
exports.monthStart = function(date, diff = 0) {
|
|
return new Date(date.getFullYear(), date.getMonth() + diff, 1);
|
|
};
|
|
|
|
/**
|
|
* Returns localized day of week.
|
|
*
|
|
* @param {Date|Number} date numeric or date object.
|
|
*/
|
|
exports.dayOfWeek = function(date) {
|
|
var number = date;
|
|
|
|
if (typeof(date) !== 'number') {
|
|
number = date.getDay();
|
|
}
|
|
return exports.dayOfWeekFromStartDay(number);
|
|
};
|
|
|
|
/**
|
|
* Finds localized week start date of given date.
|
|
*
|
|
* @param {Date} date any day the week.
|
|
* @return {Date} first date in the week of given date.
|
|
*/
|
|
exports.getWeekStartDate = function(date) {
|
|
var currentDay = exports.dayOfWeek(date);
|
|
var startDay = (date.getDate() - currentDay);
|
|
|
|
return exports.createDay(date, startDay);
|
|
};
|
|
|
|
exports.getWeekEndDate = function(date) {
|
|
// TODO: There are localization problems
|
|
// with this approach as we assume a 7 day week.
|
|
var start = exports.getWeekStartDate(date);
|
|
start.setDate(start.getDate() + 7);
|
|
start.setMilliseconds(-1);
|
|
|
|
return start;
|
|
};
|
|
|
|
/**
|
|
* Returns an array of dates objects.
|
|
* Inclusive. First and last are
|
|
* the given instances.
|
|
*
|
|
* @param {Date} start starting day.
|
|
* @param {Date} end ending day.
|
|
* @param {Boolean} includeTime include times start/end ?
|
|
*/
|
|
exports.daysBetween = function(start, end, includeTime) {
|
|
if (start instanceof Timespan) {
|
|
if (end) {
|
|
includeTime = end;
|
|
}
|
|
|
|
end = new Date(start.end);
|
|
start = new Date(start.start);
|
|
}
|
|
|
|
if (start > end) {
|
|
var tmp = end;
|
|
end = start;
|
|
start = tmp;
|
|
tmp = null;
|
|
}
|
|
|
|
var list = [];
|
|
var last = start.getDate();
|
|
|
|
// handle the case where start & end dates
|
|
// are the same date.
|
|
if (exports.isSameDate(start, end)) {
|
|
if (includeTime) {
|
|
list.push(end);
|
|
} else {
|
|
list.push(exports.createDay(start));
|
|
}
|
|
return list;
|
|
}
|
|
|
|
while (true) {
|
|
var next = new Date(
|
|
start.getFullYear(),
|
|
start.getMonth(),
|
|
++last
|
|
);
|
|
|
|
if (next > end) {
|
|
throw new Error(
|
|
'sanity fails next is greater then end'
|
|
);
|
|
}
|
|
|
|
if (!exports.isSameDate(next, end)) {
|
|
list.push(next);
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (includeTime) {
|
|
list.unshift(start);
|
|
list.push(end);
|
|
} else {
|
|
list.unshift(exports.createDay(start));
|
|
list.push(exports.createDay(end));
|
|
}
|
|
|
|
return list;
|
|
},
|
|
|
|
/**
|
|
* Returns an array of weekdays based on the start date.
|
|
* Will always return the 7 daysof that week regardless of
|
|
* what the start date isbut they will be returned
|
|
* in the order of their localized getDay function.
|
|
*
|
|
* @param {Date} startDate point of origin.
|
|
* @return {Array} a list of dates in order of getDay().
|
|
*/
|
|
exports.getWeeksDays = function(startDate) {
|
|
//local day position
|
|
var weeksDayStart = exports.getWeekStartDate(startDate);
|
|
var result = [weeksDayStart];
|
|
|
|
for (var i = 1; i < 7; i++) {
|
|
result.push(exports.createDay(weeksDayStart, weeksDayStart.getDate() + i));
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
/**
|
|
* Checks if date is in the past
|
|
*
|
|
* @param {Date} date to check.
|
|
* @return {Boolean} true when date is in the past.
|
|
*/
|
|
exports.isPast = function(date) {
|
|
return (date.valueOf() < exports.today.valueOf());
|
|
};
|
|
|
|
/**
|
|
* Checks if date is in the future
|
|
*
|
|
* @param {Date} date to check.
|
|
* @return {Boolean} true when date is in the future.
|
|
*/
|
|
exports.isFuture = function(date) {
|
|
return !exports.isPast(date);
|
|
};
|
|
|
|
/**
|
|
* Based on the input date
|
|
* will return one of the following states
|
|
*
|
|
* past, present, future
|
|
*
|
|
* @param {Date} day for compare.
|
|
* @param {Date} month comparison month.
|
|
* @return {String} state.
|
|
*/
|
|
exports.relativeState = function(day, month) {
|
|
var states;
|
|
//var today = exports.today;
|
|
|
|
// 1. the date is today (real time)
|
|
if (exports.isToday(day)) {
|
|
return exports.PRESENT;
|
|
}
|
|
|
|
// 2. the date is in the past (real time)
|
|
if (exports.isPast(day)) {
|
|
states = exports.PAST;
|
|
// 3. the date is in the future (real time)
|
|
} else {
|
|
states = exports.FUTURE;
|
|
}
|
|
|
|
// 4. the date is not in the current month (relative time)
|
|
if (day.getMonth() !== month.getMonth()) {
|
|
states += ' ' + exports.OTHER_MONTH;
|
|
}
|
|
|
|
return states;
|
|
};
|
|
|
|
/**
|
|
* Computes the relative hour (0...23.9999) inside the given day.
|
|
* If `date` is on a different day than `baseDate` it will return `0`.
|
|
* Used by week view to compute the position of the busytimes relative to
|
|
* the top of the view.
|
|
*/
|
|
exports.relativeOffset = function(baseDate, date) {
|
|
if (exports.isSameDate(baseDate, date)) {
|
|
return date.getHours() + (date.getMinutes() / 60);
|
|
}
|
|
// different day!
|
|
return 0;
|
|
};
|
|
|
|
/**
|
|
* Computes the relative duration between startDate and endDate inside
|
|
* a given baseDate. Returns a number between 0 and 24.
|
|
* Used by MultiDay view to compute the height of the busytimes relative to
|
|
* the length inside the baseDate.
|
|
*/
|
|
exports.relativeDuration = function(baseDate, startDate, endDate) {
|
|
if (!exports.isSameDate(startDate, endDate)) {
|
|
if (exports.isSameDate(baseDate, startDate)) {
|
|
endDate = exports.endOfDay(baseDate);
|
|
} else if (exports.isSameDate(baseDate, endDate)) {
|
|
startDate = exports.createDay(endDate);
|
|
} else {
|
|
// started before baseDate and ends on a different day
|
|
return 24;
|
|
}
|
|
}
|
|
return exports.hourDiff(startDate, endDate);
|
|
};
|
|
|
|
/**
|
|
* Check if event spans thru the whole day.
|
|
*/
|
|
exports.isAllDay = function(baseDate, startDate, endDate) {
|
|
// beginning reference point (start of given date)
|
|
var refStart = exports.createDay(baseDate);
|
|
var refEnd = exports.endOfDay(baseDate);
|
|
|
|
var startBefore = startDate <= refStart;
|
|
var endsAfter = endDate >= refEnd;
|
|
|
|
// yahoo uses same start/end date for recurring all day events!!!
|
|
return (startBefore && endsAfter) || Number(startDate) === Number(endDate);
|
|
};
|
|
|
|
if (typeof(window) !== 'undefined') {
|
|
window.addEventListener('localized', co.wrap(function *() {
|
|
exports.startDay = yield mozIntl.calendarInfo('firstDayOfTheWeek');
|
|
}));
|
|
}
|
|
|
|
});
|