fugitsit
Version:
Small library that wraps ICAL.js and provide more convenient means for editing
383 lines (344 loc) • 10 kB
JavaScript
/**
* @copyright Copyright (c) 2019 Georg Ehrke
*
* @author Georg Ehrke <georg-nextcloud@ehrke.email>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
import AbstractRecurringComponent from './abstractRecurringComponent.js'
import {
advertiseMultipleOccurrenceProperty,
advertiseMultiValueStringPropertySeparatedByLang,
advertiseSingleOccurrenceProperty,
} from '../abstractComponent.js'
import ConferenceProperty from '../../properties/conferenceProperty.js'
import GeoProperty from '../../properties/geoProperty.js'
/**
* @class ToDoComponent
* @classdesc
*
* @url https://tools.ietf.org/html/rfc5545#section-3.6.2
*/
export default class ToDoComponent extends AbstractRecurringComponent {
/**
* Returns whether this event is an all-day event
*
* @returns {boolean}
*/
isAllDay() {
const propertiesToCheck = ['DTSTART', 'DUE']
for (const propertyToCheck of propertiesToCheck) {
if (this.hasProperty(propertyToCheck)) {
return this.getFirstPropertyFirstValue(propertyToCheck).isDate
}
}
// If a task is not associated with any date, it is defined to
// occur on any successive date until it is completed.
// We are treating it as all-day in that case.
return true
}
/**
* Checks whether it's possible to switch from date-time to date or vise-versa
*
* @returns {boolean}
*/
canModifyAllDay() {
if (!this.hasProperty('dtstart') && !this.hasProperty('due')) {
return false
}
return !this.recurrenceManager.masterItem.isRecurring()
}
/**
* Gets the calculated end-date of the task
*
* If there is a due-date, we will just return that.
* If there is a start-date and a duration, we will
* calculate the end-date based on that.
*
* If there is neither a due-date nor a combination
* of start-date and duration, we just return null
*
* @returns {DateTimeValue|null}
*/
get endDate() {
if (this.hasProperty('due')) {
return this.getFirstPropertyFirstValue('due')
}
if (!this.hasProperty('dtstart') || !this.hasProperty('duration')) {
return null
}
const endDate = this.startDate.clone()
endDate.addDuration(this.getFirstPropertyFirstValue('duration'))
return endDate
}
/**
* Shifts the entire task by the given duration
*
* @param {DurationValue} delta The duration to shift event by
* @param {Boolean} allDay Whether the updated event should be all-day or not
* @param {Timezone} defaultTimezone The default timezone if moving from all-day to timed event
* @param {DurationValue} defaultAllDayDuration The default all-day duration if moving from timed to all-day
* @param {DurationValue} defaultTimedDuration The default timed duration if moving from all-day to timed
*/
shiftByDuration(delta, allDay, defaultTimezone, defaultAllDayDuration, defaultTimedDuration) {
const currentAllDay = this.isAllDay()
if (!this.hasProperty('dtstart') && !this.hasProperty('due')) {
throw new TypeError('This task does not have a start-date nor due-date')
}
if (currentAllDay !== allDay && !this.canModifyAllDay()) {
throw new TypeError('Can\'t modify all-day of this todo')
}
// If this task has a start-date, update it
// This is especially important, if you shift
// the task by a negative duration, because
// dtstart always has to be prior to the due date
if (this.hasProperty('dtstart')) {
this.startDate.isDate = allDay
this.startDate.addDuration(delta)
if (currentAllDay && !allDay) {
this.startDate.replaceTimezone(defaultTimezone)
}
}
if (this.hasProperty('due')) {
this.dueTime.isDate = allDay
this.dueTime.addDuration(delta)
if (currentAllDay && !allDay) {
this.dueTime.replaceTimezone(defaultTimezone)
}
}
}
/**
* Checks if this event is in a given time-frame
*
* @param {DateTimeValue} start Start of time-range to check
* @param {DateTimeValue} end End of time-range to check
* @returns {boolean}
*/
isInTimeFrame(start, end) {
if (!this.hasProperty('dtstart') && !this.hasProperty('due')) {
return true
}
if (!this.hasProperty('dtstart') && this.hasProperty('due')) {
return start.compare(this.endDate) <= 0
}
return start.compare(this.endDate) <= 0 && end.compare(this.startDate) >= 0
}
/**
* Gets the geographical position property
*
* @returns {GeoProperty}
*/
get geographicalPosition() {
return this.getFirstProperty('GEO')
}
/**
* Sets the geographical position based on latitude and longitude
*
* @url https://tools.ietf.org/html/rfc5545#section-3.8.1.6
*
* @param {Number} lat - latitude
* @param {Number} long - longitude
*/
setGeographicalPositionFromLatitudeAndLongitude(lat, long) {
this.deleteAllProperties('GEO')
this.addProperty(GeoProperty.fromPosition(lat, long))
}
/**
* Adds a new conference property based on URI, label and features
*
* @url https://tools.ietf.org/html/rfc7986#section-5.11
*
* @param {String} uri The URI of the conference
* @param {String=} label The label of the conference
* @param {String[]=} features Supported features of conference-system
*/
addConference(uri, label = null, features = null) {
this.addProperty(ConferenceProperty.fromURILabelAndFeatures(uri, label, features))
}
}
/**
* The time when a task was completed
*
* @url https://tools.ietf.org/html/rfc5545#section-3.8.2.1
*
* @name ToDoComponent#completedTime
* @type {DateTimeValue}
*/
advertiseSingleOccurrenceProperty(ToDoComponent.prototype, {
name: 'completedTime',
iCalendarName: 'COMPLETED',
})
/**
* The time when a task is due
*
* @url https://tools.ietf.org/html/rfc5545#section-3.8.2.3
*
* @name ToDoComponent#dueTime
* @type {DateTimeValue}
*/
advertiseSingleOccurrenceProperty(ToDoComponent.prototype, {
name: 'dueTime',
iCalendarName: 'DUE',
})
/**
* The time when a task was completed
*
* @url https://tools.ietf.org/html/rfc5545#section-3.8.2.5
*
* @name ToDoComponent#duration
* @type {DurationValue}
*/
advertiseSingleOccurrenceProperty(ToDoComponent.prototype, {
name: 'duration',
})
/**
* The percentage a task was already fulfilled
*
* @url https://tools.ietf.org/html/rfc5545#section-3.8.1.8
*
* @name ToDoComponent#percent
* @type {Number}
*/
advertiseSingleOccurrenceProperty(ToDoComponent.prototype, {
name: 'percent',
iCalendarName: 'PERCENT-COMPLETE',
})
/**
* Description of this task.
*
* @url https://tools.ietf.org/html/rfc5545#section-3.8.1.5
*
* @name ToDoComponent#description
* @type {String}
*/
advertiseSingleOccurrenceProperty(ToDoComponent.prototype, 'description')
/**
* Location of this task.
*
* @url https://tools.ietf.org/html/rfc5545#section-3.8.1.7
*
* @name ToDoComponent#location
* @type {String}
*/
advertiseSingleOccurrenceProperty(ToDoComponent.prototype, 'location')
/**
* Priority of this task.
*
* @url https://tools.ietf.org/html/rfc5545#section-3.8.1.9
*
* @name ToDoComponent#priority
* @type {String}
*/
advertiseSingleOccurrenceProperty(ToDoComponent.prototype, {
name: 'priority',
allowedValues: Array.from(Array(10).keys()),
defaultValue: 0,
unknownValue: 0,
})
/**
* Returns an iterator over all resources
* If the parameter lang is given, it will only
* return an iterator for Resources matching that language
*
* @url https://tools.ietf.org/html/rfc5545#section-3.8.1.10
*
* @name ToDoComponent#getResourceIterator
* @function
* @param {String=} lang
* @returns {IterableIterator<String>}
*/
/**
* Returns a list of all resources
* If the parameter lang is given, it will only
* return an iterator for resources matching that language
*
* @url https://tools.ietf.org/html/rfc5545#section-3.8.1.10
*
* @name ToDoComponent#getResourceList
* @function
* @param {String=} lang
* @returns {String[]}
*/
/**
* Adds a resource
*
* @url https://tools.ietf.org/html/rfc5545#section-3.8.1.10
*
* @name ToDoComponent#addResource
* @function
* @param {String} resource
* @param {String=} lang
*/
/**
* Removes a resource
*
* @url https://tools.ietf.org/html/rfc5545#section-3.8.1.10
*
* @name ToDoComponent#removeResource
* @function
* @param {String} resource
* @param {String=} lang
*/
/**
* Removes all resources from this task
*
* @url https://tools.ietf.org/html/rfc5545#section-3.8.1.10
*
* @name ToDoComponent#clearAllResources
* @function
* @param {String=} lang
*/
advertiseMultiValueStringPropertySeparatedByLang(ToDoComponent.prototype, {
name: 'resource',
iCalendarName: 'RESOURCES',
})
/**
* Gets an iterator over all conference properties
*
* @url https://tools.ietf.org/html/rfc7986#section-5.11
*
* @name ToDoComponent#getConferenceIterator
* @function
* @returns {IterableIterator<ConferenceProperty>}
*/
/**
* Gets a list of all conference properties
*
* @url https://tools.ietf.org/html/rfc7986#section-5.11
*
* @name ToDoComponent#getConferenceList
* @function
* @returns {ConferenceProperty[]}
*/
/**
* Removes a conference from this event
*
* @url https://tools.ietf.org/html/rfc7986#section-5.11
*
* @name ToDoComponent#removeConference
* @function
* @param {ConferenceProperty} conference
*/
/**
* Removes all conferences from this event
*
* @url https://tools.ietf.org/html/rfc7986#section-5.11
*
* @name ToDoComponent#clearAllConferences
* @function
*/
advertiseMultipleOccurrenceProperty(ToDoComponent.prototype, 'conference')