/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * Portions Copyright (C) Philipp Kewisch */ import { isStrictlyNaN, extend } from "./helpers.js"; import UtcOffset from "./utc_offset.js"; import VCardTime from "./vcard_time.js"; import Recur from "./recur.js"; import Period from "./period.js"; import Duration from "./duration.js"; import Time from "./time.js"; import Binary from "./binary.js"; /** * This lets typescript resolve our custom types in the * generated d.ts files (jsdoc typedefs are converted to typescript types). * Ignore prevents the typedefs from being documented more than once. * @ignore * @typedef {import("./types.js").designSet} designSet * Imports the 'designSet' type from the "types.js" module */ /** @module ICAL.design */ const FROM_ICAL_NEWLINE = /\\\\|\\;|\\,|\\[Nn]/g; const TO_ICAL_NEWLINE = /\\|;|,|\n/g; const FROM_VCARD_NEWLINE = /\\\\|\\,|\\[Nn]/g; const TO_VCARD_NEWLINE = /\\|,|\n/g; function createTextType(fromNewline, toNewline) { let result = { matches: /.*/, fromICAL: function(aValue, structuredEscape) { return replaceNewline(aValue, fromNewline, structuredEscape); }, toICAL: function(aValue, structuredEscape) { let regEx = toNewline; if (structuredEscape) regEx = new RegExp(regEx.source + '|' + structuredEscape, regEx.flags); return aValue.replace(regEx, function(str) { switch (str) { case "\\": return "\\\\"; case ";": return "\\;"; case ",": return "\\,"; case "\n": return "\\n"; /* c8 ignore next 2 */ default: return str; } }); } }; return result; } // default types used multiple times const DEFAULT_TYPE_TEXT = { defaultType: "text" }; const DEFAULT_TYPE_TEXT_MULTI = { defaultType: "text", multiValue: "," }; const DEFAULT_TYPE_TEXT_STRUCTURED = { defaultType: "text", structuredValue: ";" }; const DEFAULT_TYPE_INTEGER = { defaultType: "integer" }; const DEFAULT_TYPE_DATETIME_DATE = { defaultType: "date-time", allowedTypes: ["date-time", "date"] }; const DEFAULT_TYPE_DATETIME = { defaultType: "date-time" }; const DEFAULT_TYPE_URI = { defaultType: "uri" }; const DEFAULT_TYPE_UTCOFFSET = { defaultType: "utc-offset" }; const DEFAULT_TYPE_RECUR = { defaultType: "recur" }; const DEFAULT_TYPE_DATE_ANDOR_TIME = { defaultType: "date-and-or-time", allowedTypes: ["date-time", "date", "text"] }; function replaceNewlineReplace(string) { switch (string) { case "\\\\": return "\\"; case "\\;": return ";"; case "\\,": return ","; case "\\n": case "\\N": return "\n"; /* c8 ignore next 2 */ default: return string; } } function replaceNewline(value, newline, structuredEscape) { // avoid regex when possible. if (value.indexOf('\\') === -1) { return value; } if (structuredEscape) newline = new RegExp(newline.source + '|\\\\' + structuredEscape, newline.flags); return value.replace(newline, replaceNewlineReplace); } let commonProperties = { "categories": DEFAULT_TYPE_TEXT_MULTI, "url": DEFAULT_TYPE_URI, "version": DEFAULT_TYPE_TEXT, "uid": DEFAULT_TYPE_TEXT }; let commonValues = { "boolean": { values: ["TRUE", "FALSE"], fromICAL: function(aValue) { switch (aValue) { case 'TRUE': return true; case 'FALSE': return false; default: //TODO: parser warning return false; } }, toICAL: function(aValue) { if (aValue) { return 'TRUE'; } return 'FALSE'; } }, float: { matches: /^[+-]?\d+\.\d+$/, fromICAL: function(aValue) { let parsed = parseFloat(aValue); if (isStrictlyNaN(parsed)) { // TODO: parser warning return 0.0; } return parsed; }, toICAL: function(aValue) { return String(aValue); } }, integer: { fromICAL: function(aValue) { let parsed = parseInt(aValue); if (isStrictlyNaN(parsed)) { return 0; } return parsed; }, toICAL: function(aValue) { return String(aValue); } }, "utc-offset": { toICAL: function(aValue) { if (aValue.length < 7) { // no seconds // -0500 return aValue.slice(0, 3) + aValue.slice(4, 6); } else { // seconds // -050000 return aValue.slice(0, 3) + aValue.slice(4, 6) + aValue.slice(7, 9); } }, fromICAL: function(aValue) { if (aValue.length < 6) { // no seconds // -05:00 return aValue.slice(0, 3) + ':' + aValue.slice(3, 5); } else { // seconds // -05:00:00 return aValue.slice(0, 3) + ':' + aValue.slice(3, 5) + ':' + aValue.slice(5, 7); } }, decorate: function(aValue) { return UtcOffset.fromString(aValue); }, undecorate: function(aValue) { return aValue.toString(); } } }; let icalParams = { // Although the syntax is DQUOTE uri DQUOTE, I don't think we should // enforce anything aside from it being a valid content line. // // At least some params require - if multi values are used - DQUOTEs // for each of its values - e.g. delegated-from="uri1","uri2" // To indicate this, I introduced the new k/v pair // multiValueSeparateDQuote: true // // "ALTREP": { ... }, // CN just wants a param-value // "CN": { ... } "cutype": { values: ["INDIVIDUAL", "GROUP", "RESOURCE", "ROOM", "UNKNOWN"], allowXName: true, allowIanaToken: true }, "delegated-from": { valueType: "cal-address", multiValue: ",", multiValueSeparateDQuote: true }, "delegated-to": { valueType: "cal-address", multiValue: ",", multiValueSeparateDQuote: true }, // "DIR": { ... }, // See ALTREP "encoding": { values: ["8BIT", "BASE64"] }, // "FMTTYPE": { ... }, // See ALTREP "fbtype": { values: ["FREE", "BUSY", "BUSY-UNAVAILABLE", "BUSY-TENTATIVE"], allowXName: true, allowIanaToken: true }, // "LANGUAGE": { ... }, // See ALTREP "member": { valueType: "cal-address", multiValue: ",", multiValueSeparateDQuote: true }, "partstat": { // TODO These values are actually different per-component values: ["NEEDS-ACTION", "ACCEPTED", "DECLINED", "TENTATIVE", "DELEGATED", "COMPLETED", "IN-PROCESS"], allowXName: true, allowIanaToken: true }, "range": { values: ["THISANDFUTURE"] }, "related": { values: ["START", "END"] }, "reltype": { values: ["PARENT", "CHILD", "SIBLING"], allowXName: true, allowIanaToken: true }, "role": { values: ["REQ-PARTICIPANT", "CHAIR", "OPT-PARTICIPANT", "NON-PARTICIPANT"], allowXName: true, allowIanaToken: true }, "rsvp": { values: ["TRUE", "FALSE"] }, "sent-by": { valueType: "cal-address" }, "tzid": { matches: /^\// }, "value": { // since the value here is a 'type' lowercase is used. values: ["binary", "boolean", "cal-address", "date", "date-time", "duration", "float", "integer", "period", "recur", "text", "time", "uri", "utc-offset"], allowXName: true, allowIanaToken: true } }; // When adding a value here, be sure to add it to the parameter types! const icalValues = extend(commonValues, { text: createTextType(FROM_ICAL_NEWLINE, TO_ICAL_NEWLINE), uri: { // TODO /* ... */ }, "binary": { decorate: function(aString) { return Binary.fromString(aString); }, undecorate: function(aBinary) { return aBinary.toString(); } }, "cal-address": { // needs to be an uri }, "date": { decorate: function(aValue, aProp) { if (design.strict) { return Time.fromDateString(aValue, aProp); } else { return Time.fromString(aValue, aProp); } }, /** * undecorates a time object. */ undecorate: function(aValue) { return aValue.toString(); }, fromICAL: function(aValue) { // from: 20120901 // to: 2012-09-01 if (!design.strict && aValue.length >= 15) { // This is probably a date-time, e.g. 20120901T130000Z return icalValues["date-time"].fromICAL(aValue); } else { return aValue.slice(0, 4) + '-' + aValue.slice(4, 6) + '-' + aValue.slice(6, 8); } }, toICAL: function(aValue) { // from: 2012-09-01 // to: 20120901 let len = aValue.length; if (len == 10) { return aValue.slice(0, 4) + aValue.slice(5, 7) + aValue.slice(8, 10); } else if (len >= 19) { return icalValues["date-time"].toICAL(aValue); } else { //TODO: serialize warning? return aValue; } } }, "date-time": { fromICAL: function(aValue) { // from: 20120901T130000 // to: 2012-09-01T13:00:00 if (!design.strict && aValue.length == 8) { // This is probably a date, e.g. 20120901 return icalValues.date.fromICAL(aValue); } else { let result = aValue.slice(0, 4) + '-' + aValue.slice(4, 6) + '-' + aValue.slice(6, 8) + 'T' + aValue.slice(9, 11) + ':' + aValue.slice(11, 13) + ':' + aValue.slice(13, 15); if (aValue[15] && aValue[15] === 'Z') { result += 'Z'; } return result; } }, toICAL: function(aValue) { // from: 2012-09-01T13:00:00 // to: 20120901T130000 let len = aValue.length; if (len == 10 && !design.strict) { return icalValues.date.toICAL(aValue); } else if (len >= 19) { let result = aValue.slice(0, 4) + aValue.slice(5, 7) + // grab the (DDTHH) segment aValue.slice(8, 13) + // MM aValue.slice(14, 16) + // SS aValue.slice(17, 19); if (aValue[19] && aValue[19] === 'Z') { result += 'Z'; } return result; } else { // TODO: error return aValue; } }, decorate: function(aValue, aProp) { if (design.strict) { return Time.fromDateTimeString(aValue, aProp); } else { return Time.fromString(aValue, aProp); } }, undecorate: function(aValue) { return aValue.toString(); } }, duration: { decorate: function(aValue) { return Duration.fromString(aValue); }, undecorate: function(aValue) { return aValue.toString(); } }, period: { fromICAL: function(string) { let parts = string.split('/'); parts[0] = icalValues['date-time'].fromICAL(parts[0]); if (!Duration.isValueString(parts[1])) { parts[1] = icalValues['date-time'].fromICAL(parts[1]); } return parts; }, toICAL: function(parts) { parts = parts.slice(); if (!design.strict && parts[0].length == 10) { parts[0] = icalValues.date.toICAL(parts[0]); } else { parts[0] = icalValues['date-time'].toICAL(parts[0]); } if (!Duration.isValueString(parts[1])) { if (!design.strict && parts[1].length == 10) { parts[1] = icalValues.date.toICAL(parts[1]); } else { parts[1] = icalValues['date-time'].toICAL(parts[1]); } } return parts.join("/"); }, decorate: function(aValue, aProp) { return Period.fromJSON(aValue, aProp, !design.strict); }, undecorate: function(aValue) { return aValue.toJSON(); } }, recur: { fromICAL: function(string) { return Recur._stringToData(string, true); }, toICAL: function(data) { let str = ""; for (let [k, val] of Object.entries(data)) { if (k == "until") { if (val.length > 10) { val = icalValues['date-time'].toICAL(val); } else { val = icalValues.date.toICAL(val); } } else if (k == "wkst") { if (typeof val === 'number') { val = Recur.numericDayToIcalDay(val); } } else if (Array.isArray(val)) { val = val.join(","); } str += k.toUpperCase() + "=" + val + ";"; } return str.slice(0, Math.max(0, str.length - 1)); }, decorate: function decorate(aValue) { return Recur.fromData(aValue); }, undecorate: function(aRecur) { return aRecur.toJSON(); } }, time: { fromICAL: function(aValue) { // from: MMHHSS(Z)? // to: HH:MM:SS(Z)? if (aValue.length < 6) { // TODO: parser exception? return aValue; } // HH::MM::SSZ? let result = aValue.slice(0, 2) + ':' + aValue.slice(2, 4) + ':' + aValue.slice(4, 6); if (aValue[6] === 'Z') { result += 'Z'; } return result; }, toICAL: function(aValue) { // from: HH:MM:SS(Z)? // to: MMHHSS(Z)? if (aValue.length < 8) { //TODO: error return aValue; } let result = aValue.slice(0, 2) + aValue.slice(3, 5) + aValue.slice(6, 8); if (aValue[8] === 'Z') { result += 'Z'; } return result; } } }); let icalProperties = extend(commonProperties, { "action": DEFAULT_TYPE_TEXT, "attach": { defaultType: "uri" }, "attendee": { defaultType: "cal-address" }, "calscale": DEFAULT_TYPE_TEXT, "class": DEFAULT_TYPE_TEXT, "comment": DEFAULT_TYPE_TEXT, "completed": DEFAULT_TYPE_DATETIME, "contact": DEFAULT_TYPE_TEXT, "created": DEFAULT_TYPE_DATETIME, "description": DEFAULT_TYPE_TEXT, "dtend": DEFAULT_TYPE_DATETIME_DATE, "dtstamp": DEFAULT_TYPE_DATETIME, "dtstart": DEFAULT_TYPE_DATETIME_DATE, "due": DEFAULT_TYPE_DATETIME_DATE, "duration": { defaultType: "duration" }, "exdate": { defaultType: "date-time", allowedTypes: ["date-time", "date"], multiValue: ',' }, "exrule": DEFAULT_TYPE_RECUR, "freebusy": { defaultType: "period", multiValue: "," }, "geo": { defaultType: "float", structuredValue: ";" }, "last-modified": DEFAULT_TYPE_DATETIME, "location": DEFAULT_TYPE_TEXT, "method": DEFAULT_TYPE_TEXT, "organizer": { defaultType: "cal-address" }, "percent-complete": DEFAULT_TYPE_INTEGER, "priority": DEFAULT_TYPE_INTEGER, "prodid": DEFAULT_TYPE_TEXT, "related-to": DEFAULT_TYPE_TEXT, "repeat": DEFAULT_TYPE_INTEGER, "rdate": { defaultType: "date-time", allowedTypes: ["date-time", "date", "period"], multiValue: ',', detectType: function(string) { if (string.indexOf('/') !== -1) { return 'period'; } return (string.indexOf('T') === -1) ? 'date' : 'date-time'; } }, "recurrence-id": DEFAULT_TYPE_DATETIME_DATE, "resources": DEFAULT_TYPE_TEXT_MULTI, "request-status": DEFAULT_TYPE_TEXT_STRUCTURED, "rrule": DEFAULT_TYPE_RECUR, "sequence": DEFAULT_TYPE_INTEGER, "status": DEFAULT_TYPE_TEXT, "summary": DEFAULT_TYPE_TEXT, "transp": DEFAULT_TYPE_TEXT, "trigger": { defaultType: "duration", allowedTypes: ["duration", "date-time"] }, "tzoffsetfrom": DEFAULT_TYPE_UTCOFFSET, "tzoffsetto": DEFAULT_TYPE_UTCOFFSET, "tzurl": DEFAULT_TYPE_URI, "tzid": DEFAULT_TYPE_TEXT, "tzname": DEFAULT_TYPE_TEXT }); // When adding a value here, be sure to add it to the parameter types! const vcardValues = extend(commonValues, { text: createTextType(FROM_VCARD_NEWLINE, TO_VCARD_NEWLINE), uri: createTextType(FROM_VCARD_NEWLINE, TO_VCARD_NEWLINE), date: { decorate: function(aValue) { return VCardTime.fromDateAndOrTimeString(aValue, "date"); }, undecorate: function(aValue) { return aValue.toString(); }, fromICAL: function(aValue) { if (aValue.length == 8) { return icalValues.date.fromICAL(aValue); } else if (aValue[0] == '-' && aValue.length == 6) { return aValue.slice(0, 4) + '-' + aValue.slice(4); } else { return aValue; } }, toICAL: function(aValue) { if (aValue.length == 10) { return icalValues.date.toICAL(aValue); } else if (aValue[0] == '-' && aValue.length == 7) { return aValue.slice(0, 4) + aValue.slice(5); } else { return aValue; } } }, time: { decorate: function(aValue) { return VCardTime.fromDateAndOrTimeString("T" + aValue, "time"); }, undecorate: function(aValue) { return aValue.toString(); }, fromICAL: function(aValue) { let splitzone = vcardValues.time._splitZone(aValue, true); let zone = splitzone[0], value = splitzone[1]; //console.log("SPLIT: ",splitzone); if (value.length == 6) { value = value.slice(0, 2) + ':' + value.slice(2, 4) + ':' + value.slice(4, 6); } else if (value.length == 4 && value[0] != '-') { value = value.slice(0, 2) + ':' + value.slice(2, 4); } else if (value.length == 5) { value = value.slice(0, 3) + ':' + value.slice(3, 5); } if (zone.length == 5 && (zone[0] == '-' || zone[0] == '+')) { zone = zone.slice(0, 3) + ':' + zone.slice(3); } return value + zone; }, toICAL: function(aValue) { let splitzone = vcardValues.time._splitZone(aValue); let zone = splitzone[0], value = splitzone[1]; if (value.length == 8) { value = value.slice(0, 2) + value.slice(3, 5) + value.slice(6, 8); } else if (value.length == 5 && value[0] != '-') { value = value.slice(0, 2) + value.slice(3, 5); } else if (value.length == 6) { value = value.slice(0, 3) + value.slice(4, 6); } if (zone.length == 6 && (zone[0] == '-' || zone[0] == '+')) { zone = zone.slice(0, 3) + zone.slice(4); } return value + zone; }, _splitZone: function(aValue, isFromIcal) { let lastChar = aValue.length - 1; let signChar = aValue.length - (isFromIcal ? 5 : 6); let sign = aValue[signChar]; let zone, value; if (aValue[lastChar] == 'Z') { zone = aValue[lastChar]; value = aValue.slice(0, Math.max(0, lastChar)); } else if (aValue.length > 6 && (sign == '-' || sign == '+')) { zone = aValue.slice(signChar); value = aValue.slice(0, Math.max(0, signChar)); } else { zone = ""; value = aValue; } return [zone, value]; } }, "date-time": { decorate: function(aValue) { return VCardTime.fromDateAndOrTimeString(aValue, "date-time"); }, undecorate: function(aValue) { return aValue.toString(); }, fromICAL: function(aValue) { return vcardValues['date-and-or-time'].fromICAL(aValue); }, toICAL: function(aValue) { return vcardValues['date-and-or-time'].toICAL(aValue); } }, "date-and-or-time": { decorate: function(aValue) { return VCardTime.fromDateAndOrTimeString(aValue, "date-and-or-time"); }, undecorate: function(aValue) { return aValue.toString(); }, fromICAL: function(aValue) { let parts = aValue.split('T'); return (parts[0] ? vcardValues.date.fromICAL(parts[0]) : '') + (parts[1] ? 'T' + vcardValues.time.fromICAL(parts[1]) : ''); }, toICAL: function(aValue) { let parts = aValue.split('T'); return vcardValues.date.toICAL(parts[0]) + (parts[1] ? 'T' + vcardValues.time.toICAL(parts[1]) : ''); } }, timestamp: icalValues['date-time'], "language-tag": { matches: /^[a-zA-Z0-9-]+$/ // Could go with a more strict regex here }, "phone-number": { fromICAL: function(aValue) { return Array.from(aValue).filter(function(c) { return c === '\\' ? undefined : c; }).join(''); }, toICAL: function(aValue) { return Array.from(aValue).map(function(c) { return c === ',' || c === ";" ? '\\' + c : c; }).join(''); } } }); let vcardParams = { "type": { valueType: "text", multiValue: "," }, "value": { // since the value here is a 'type' lowercase is used. values: ["text", "uri", "date", "time", "date-time", "date-and-or-time", "timestamp", "boolean", "integer", "float", "utc-offset", "language-tag"], allowXName: true, allowIanaToken: true } }; let vcardProperties = extend(commonProperties, { "adr": { defaultType: "text", structuredValue: ";", multiValue: "," }, "anniversary": DEFAULT_TYPE_DATE_ANDOR_TIME, "bday": DEFAULT_TYPE_DATE_ANDOR_TIME, "caladruri": DEFAULT_TYPE_URI, "caluri": DEFAULT_TYPE_URI, "clientpidmap": DEFAULT_TYPE_TEXT_STRUCTURED, "email": DEFAULT_TYPE_TEXT, "fburl": DEFAULT_TYPE_URI, "fn": DEFAULT_TYPE_TEXT, "gender": DEFAULT_TYPE_TEXT_STRUCTURED, "geo": DEFAULT_TYPE_URI, "impp": DEFAULT_TYPE_URI, "key": DEFAULT_TYPE_URI, "kind": DEFAULT_TYPE_TEXT, "lang": { defaultType: "language-tag" }, "logo": DEFAULT_TYPE_URI, "member": DEFAULT_TYPE_URI, "n": { defaultType: "text", structuredValue: ";", multiValue: "," }, "nickname": DEFAULT_TYPE_TEXT_MULTI, "note": DEFAULT_TYPE_TEXT, "org": { defaultType: "text", structuredValue: ";" }, "photo": DEFAULT_TYPE_URI, "related": DEFAULT_TYPE_URI, "rev": { defaultType: "timestamp" }, "role": DEFAULT_TYPE_TEXT, "sound": DEFAULT_TYPE_URI, "source": DEFAULT_TYPE_URI, "tel": { defaultType: "uri", allowedTypes: ["uri", "text"] }, "title": DEFAULT_TYPE_TEXT, "tz": { defaultType: "text", allowedTypes: ["text", "utc-offset", "uri"] }, "xml": DEFAULT_TYPE_TEXT }); let vcard3Values = extend(commonValues, { binary: icalValues.binary, date: vcardValues.date, "date-time": vcardValues["date-time"], "phone-number": vcardValues["phone-number"], uri: icalValues.uri, text: vcardValues.text, time: icalValues.time, vcard: icalValues.text, "utc-offset": { toICAL: function(aValue) { return aValue.slice(0, 7); }, fromICAL: function(aValue) { return aValue.slice(0, 7); }, decorate: function(aValue) { return UtcOffset.fromString(aValue); }, undecorate: function(aValue) { return aValue.toString(); } } }); let vcard3Params = { "type": { valueType: "text", multiValue: "," }, "value": { // since the value here is a 'type' lowercase is used. values: ["text", "uri", "date", "date-time", "phone-number", "time", "boolean", "integer", "float", "utc-offset", "vcard", "binary"], allowXName: true, allowIanaToken: true } }; let vcard3Properties = extend(commonProperties, { fn: DEFAULT_TYPE_TEXT, n: { defaultType: "text", structuredValue: ";", multiValue: "," }, nickname: DEFAULT_TYPE_TEXT_MULTI, photo: { defaultType: "binary", allowedTypes: ["binary", "uri"] }, bday: { defaultType: "date-time", allowedTypes: ["date-time", "date"], detectType: function(string) { return (string.indexOf('T') === -1) ? 'date' : 'date-time'; } }, adr: { defaultType: "text", structuredValue: ";", multiValue: "," }, label: DEFAULT_TYPE_TEXT, tel: { defaultType: "phone-number" }, email: DEFAULT_TYPE_TEXT, mailer: DEFAULT_TYPE_TEXT, tz: { defaultType: "utc-offset", allowedTypes: ["utc-offset", "text"] }, geo: { defaultType: "float", structuredValue: ";" }, title: DEFAULT_TYPE_TEXT, role: DEFAULT_TYPE_TEXT, logo: { defaultType: "binary", allowedTypes: ["binary", "uri"] }, agent: { defaultType: "vcard", allowedTypes: ["vcard", "text", "uri"] }, org: DEFAULT_TYPE_TEXT_STRUCTURED, note: DEFAULT_TYPE_TEXT_MULTI, prodid: DEFAULT_TYPE_TEXT, rev: { defaultType: "date-time", allowedTypes: ["date-time", "date"], detectType: function(string) { return (string.indexOf('T') === -1) ? 'date' : 'date-time'; } }, "sort-string": DEFAULT_TYPE_TEXT, sound: { defaultType: "binary", allowedTypes: ["binary", "uri"] }, class: DEFAULT_TYPE_TEXT, key: { defaultType: "binary", allowedTypes: ["binary", "text"] } }); /** * iCalendar design set * @type {designSet} */ let icalSet = { name: "ical", value: icalValues, param: icalParams, property: icalProperties, propertyGroups: false }; /** * vCard 4.0 design set * @type {designSet} */ let vcardSet = { name: "vcard4", value: vcardValues, param: vcardParams, property: vcardProperties, propertyGroups: true }; /** * vCard 3.0 design set * @type {designSet} */ let vcard3Set = { name: "vcard3", value: vcard3Values, param: vcard3Params, property: vcard3Properties, propertyGroups: true }; /** * The design data, used by the parser to determine types for properties and * other metadata needed to produce correct jCard/jCal data. * * @alias ICAL.design * @exports module:ICAL.design */ const design = { /** * Can be set to false to make the parser more lenient. */ strict: true, /** * The default set for new properties and components if none is specified. * @type {designSet} */ defaultSet: icalSet, /** * The default type for unknown properties * @type {String} */ defaultType: 'unknown', /** * Holds the design set for known top-level components * * @type {Object} * @property {designSet} vcard vCard VCARD * @property {designSet} vevent iCalendar VEVENT * @property {designSet} vtodo iCalendar VTODO * @property {designSet} vjournal iCalendar VJOURNAL * @property {designSet} valarm iCalendar VALARM * @property {designSet} vtimezone iCalendar VTIMEZONE * @property {designSet} daylight iCalendar DAYLIGHT * @property {designSet} standard iCalendar STANDARD * * @example * let propertyName = 'fn'; * let componentDesign = ICAL.design.components.vcard; * let propertyDetails = componentDesign.property[propertyName]; * if (propertyDetails.defaultType == 'text') { * // Yep, sure is... * } */ components: { vcard: vcardSet, vcard3: vcard3Set, vevent: icalSet, vtodo: icalSet, vjournal: icalSet, valarm: icalSet, vtimezone: icalSet, daylight: icalSet, standard: icalSet }, /** * The design set for iCalendar (rfc5545/rfc7265) components. * @type {designSet} */ icalendar: icalSet, /** * The design set for vCard (rfc6350/rfc7095) components. * @type {designSet} */ vcard: vcardSet, /** * The design set for vCard (rfc2425/rfc2426/rfc7095) components. * @type {designSet} */ vcard3: vcard3Set, /** * Gets the design set for the given component name. * * @param {String} componentName The name of the component * @return {designSet} The design set for the component */ getDesignSet: function(componentName) { let isInDesign = componentName && componentName in design.components; return isInDesign ? design.components[componentName] : design.defaultSet; } }; export default design;