diff --git a/agents/dev-assist/workspace/AGENTS.md b/agents/dev-assist/workspace/AGENTS.md
index 4f0e03a..5638344 100644
--- a/agents/dev-assist/workspace/AGENTS.md
+++ b/agents/dev-assist/workspace/AGENTS.md
@@ -4,9 +4,7 @@ This folder is home. Treat it that way.
---
-## ⚠️ 工作规范(必须遵守)
-
-### 安全规则(最高优先级)
+## ⚠️ 安全规则(最高优先级)
| 规则 | 说明 |
|------|------|
@@ -15,45 +13,52 @@ This folder is home. Treat it that way.
| **禁止修改第三方库代码** | 如需修改应通过 fork 或 patch |
| **禁止使用 rm -rf** | 使用 `trash` 替代,确保可恢复 |
-### 操作规范
+---
+## 操作规范
+
+### 通用规则
- 使用中文沟通
- 使用东八区(Asia/Shanghai)时间
-#### Sudo 命令
+### Sudo 命令使用
- 需要 passwordless sudo 权限才能执行系统级操作
- 配置路径:`/etc/sudoers.d/nopasswd`
- 配置后验证:`sudo visudo -c`
-#### 子代理使用
+### 子代理使用
1. **代码开发和耗时操作必须交给子代理处理**
2. **汇报机制**
- 子代理开始工作时,立刻汇报:「已安排代理处理 [任务]」
- 每 5 分钟查询子代理状态,汇总后简报给用户
-#### 技能使用
+### 技能使用
- 使用技能过程中,遇到流程外的问题需在结果中明确汇报具体问题
-#### 定时任务
+### 定时任务
- 所有定时任务通过 **OpenClaw Gateway 内置 Cron** 管理
- 不使用系统级 crontab
-- 相关脚本统一存放:`~/work/backup/`
+- 相关脚本统一存放:`~/.openclaw/cron/scripts/`
-### 环境说明
+---
-| 项目 | 值 |
-|------|-----|
-| 本机 IP | 192.168.1.36 |
-| 网关 | 192.168.1.200(旁路由,PassWall 科学上网) |
-| 项目目录 | `~/work/` |
+## 环境说明
+
+### 本机配置
+| 项目 | 值 |
+| ---- | -------------------------------- |
+| 联网方式 | WLAN |
+| IP | 192.168.1.36 |
+| 网关 | 192.168.1.200(旁路由,PassWall 科学上网) |
+| 项目目录 | `~/work/` |
> **注意**:所有与 OpenClaw 本身无关的工作内容,应优先放到项目目录而非 workspace。
-#### 爬虫相关
+### 爬虫相关
- Cookie 存放:`~/work/cookies/`(各网站登录态,JSON 格式)
-- 爬虫工具:`~/MediaCrawler/`(优先使用)
+- 爬虫工具:`~/work/MediaCrawler/`(优先使用)
-#### 局域网服务
+### 局域网服务
| 服务 | 地址 | 端口 |
|------|------|------|
| NAS (SSH) | 192.168.1.22 | 2222 |
@@ -61,6 +66,12 @@ This folder is home. Treat it that way.
| Gitea (Web) | 192.168.1.22 | 8418 |
| NAS WebDAV | 192.168.1.22 | 5005 |
+### 用户的工作区
+- 路径:~/work/zq
+- 这个工作区目录通过rclone与Nas同步,修改内容之前必须先从Nas成功同步到本地,修改内容之后必须同步到Nas。
+- 用户的待办记录到~/work/zq/00.收件箱/01-速记本.md
+- 用户的提醒记录到~/work/zq/20.领域/23-生活管理/提醒
+
---
## First Run
diff --git a/agents/haonan/sessions/sessions.json b/agents/haonan/sessions/sessions.json
index e4112ab..93f5e48 100644
--- a/agents/haonan/sessions/sessions.json
+++ b/agents/haonan/sessions/sessions.json
@@ -1,7 +1,7 @@
{
"agent:haonan:main": {
"sessionId": "acdf6f57-1014-4869-aa7f-f95e6b6f363b",
- "updatedAt": 1772788045477,
+ "updatedAt": 1772790673976,
"systemSent": true,
"skillsSnapshot": {
"prompt": "\n\nThe following skills provide specialized instructions for specific tasks.\nUse the read tool to load a skill's file when the task matches its description.\nWhen a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.\n\n\n \n capability-evolver\n A self-evolution engine for AI agents. Analyzes runtime history to identify improvements and applies protocol-constrained evolution.\n /home/starmen1265/.openclaw/skills/capability-evolver/SKILL.md\n \n",
@@ -46,7 +46,7 @@
"sessionFile": "/home/starmen1265/.openclaw/agents/haonan/sessions/acdf6f57-1014-4869-aa7f-f95e6b6f363b.jsonl",
"systemPromptReport": {
"source": "run",
- "generatedAt": 1772787347028,
+ "generatedAt": 1772790667914,
"sessionId": "acdf6f57-1014-4869-aa7f-f95e6b6f363b",
"sessionKey": "agent:haonan:main",
"provider": "zhipu",
@@ -59,8 +59,8 @@
"sandboxed": false
},
"systemPrompt": {
- "chars": 34967,
- "projectContextChars": 22258,
+ "chars": 31722,
+ "projectContextChars": 19013,
"nonProjectContextChars": 12709
},
"injectedWorkspaceFiles": [
@@ -152,36 +152,6 @@
"injectedChars": 616,
"truncated": false
},
- {
- "path": "SELF_IMPROVEMENT_REMINDER.md",
- "rawChars": 616,
- "injectedChars": 616,
- "truncated": false
- },
- {
- "path": "SELF_IMPROVEMENT_REMINDER.md",
- "rawChars": 616,
- "injectedChars": 616,
- "truncated": false
- },
- {
- "path": "SELF_IMPROVEMENT_REMINDER.md",
- "rawChars": 616,
- "injectedChars": 616,
- "truncated": false
- },
- {
- "path": "SELF_IMPROVEMENT_REMINDER.md",
- "rawChars": 616,
- "injectedChars": 616,
- "truncated": false
- },
- {
- "path": "SELF_IMPROVEMENT_REMINDER.md",
- "rawChars": 616,
- "injectedChars": 616,
- "truncated": false
- },
{
"path": "SELF_IMPROVEMENT_REMINDER.md",
"rawChars": 616,
@@ -410,14 +380,14 @@
}
},
"compactionCount": 0,
- "inputTokens": 104026,
- "outputTokens": 913,
- "totalTokens": 114944,
+ "inputTokens": 100407,
+ "outputTokens": 30,
+ "totalTokens": 118071,
"contextTokens": 200000,
"memoryFlushAt": 1772764823004,
"memoryFlushCompactionCount": 0,
"totalTokensFresh": true,
- "cacheRead": 114752,
+ "cacheRead": 17664,
"cacheWrite": 0
},
"agent:haonan:wecom:dm:zhangqing": {
@@ -934,132 +904,25 @@
"totalTokens": 31526
},
"agent:haonan:cron:f03b184f-5f1d-4093-ae1b-fc9fe0b59eef": {
- "sessionId": "a604e4f3-5de4-4e81-bb9f-ebc25b5fbc1a",
- "updatedAt": 1772708400038,
+ "sessionId": "ccc40ca7-7876-4d82-ab84-7b790feddc19",
+ "updatedAt": 1772794800044,
"systemSent": true,
"model": "glm-4.7-FlashX",
"contextTokens": 200000,
"label": "Cron: capability-evolver-daily",
"skillsSnapshot": {
- "prompt": "\n\nThe following skills provide specialized instructions for specific tasks.\nUse the read tool to load a skill's file when the task matches its description.\nWhen a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.\n\n\n \n feishu-doc\n Feishu document read/write operations. Activate when user mentions Feishu docs, cloud docs, or docx links.\n\n /home/starmen1265/.openclaw/extensions/feishu/skills/feishu-doc/SKILL.md\n \n \n feishu-drive\n Feishu cloud storage file management. Activate when user mentions cloud space, folders, drive.\n\n /home/starmen1265/.openclaw/extensions/feishu/skills/feishu-drive/SKILL.md\n \n \n feishu-perm\n Feishu permission management for documents and files. Activate when user mentions sharing, permissions, collaborators.\n\n /home/starmen1265/.openclaw/extensions/feishu/skills/feishu-perm/SKILL.md\n \n \n feishu-wiki\n Feishu knowledge base navigation. Activate when user mentions knowledge base, wiki, or wiki links.\n\n /home/starmen1265/.openclaw/extensions/feishu/skills/feishu-wiki/SKILL.md\n \n \n healthcheck\n Host security hardening and risk-tolerance configuration for OpenClaw deployments. Use when a user asks for security audits, firewall/SSH/update hardening, risk posture, exposure review, OpenClaw cron scheduling for periodic checks, or version status checks on a machine running OpenClaw (laptop, workstation, Pi, VPS).\n /home/starmen1265/.npm-global/lib/node_modules/openclaw/skills/healthcheck/SKILL.md\n \n \n skill-creator\n Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.\n /home/starmen1265/.npm-global/lib/node_modules/openclaw/skills/skill-creator/SKILL.md\n \n \n video-frames\n Extract frames or short clips from videos using ffmpeg.\n /home/starmen1265/.npm-global/lib/node_modules/openclaw/skills/video-frames/SKILL.md\n \n \n weather\n Get current weather and forecasts (no API key required).\n /home/starmen1265/.npm-global/lib/node_modules/openclaw/skills/weather/SKILL.md\n \n \n capability-evolver\n A self-evolution engine for AI agents. Analyzes runtime history to identify improvements and applies protocol-constrained evolution.\n /home/starmen1265/.openclaw/skills/capability-evolver/SKILL.md\n \n \n clawlist\n MUST use for any multi-step project, long-running task, or infinite monitoring workflow. Plan, execute, track, and verify tasks with checkpoint validation. For projects, automation, and ongoing operations.\n /home/starmen1265/.openclaw/skills/clawlist/SKILL.md\n \n \n brainstorming\n MUST use before any creative work - creating features, building components, adding functionality, or modifying behavior. Explores user intent, requirements and design before implementation.\n /home/starmen1265/.openclaw/skills/clawlist/brainstorming/SKILL.md\n \n \n dispatch-multiple-agents\n Use when facing 2+ independent tasks that can be worked on without shared state or sequential dependencies. Dispatch subagents to work concurrently.\n /home/starmen1265/.openclaw/skills/clawlist/dispatch-multiple-agents/SKILL.md\n \n \n doing-tasks\n Use when executing any task. Work through plans systematically, tracking progress, handling blockers, and coordinating with other skills. The central execution skill.\n /home/starmen1265/.openclaw/skills/clawlist/doing-tasks/SKILL.md\n \n \n verify-task\n MUST use after completing any multi-step task or project. Verifies completion against the original plan, checks quality criteria, and documents outcomes.\n /home/starmen1265/.openclaw/skills/clawlist/verify-task/SKILL.md\n \n \n write-plan\n MUST use after brainstorming and before executing. Creates detailed implementation plans with checkpoints, verification criteria, and execution options.\n /home/starmen1265/.openclaw/skills/clawlist/write-plan/SKILL.md\n \n \n find-skills\n Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.\n /home/starmen1265/.openclaw/skills/find-skills/SKILL.md\n \n \n self-improvement\n Captures learnings, errors, and corrections to enable continuous improvement. Use when: (1) A command or operation fails unexpectedly, (2) User corrects Claude ('No, that's wrong...', 'Actually...'), (3) User requests a capability that doesn't exist, (4) An external API or tool fails, (5) Claude realizes its knowledge is outdated or incorrect, (6) A better approach is discovered for a recurring task. Also review learnings before major tasks.\n /home/starmen1265/.openclaw/skills/self-improving-agent/SKILL.md\n \n",
+ "prompt": "\n\nThe following skills provide specialized instructions for specific tasks.\nUse the read tool to load a skill's file when the task matches its description.\nWhen a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.\n\n\n \n capability-evolver\n A self-evolution engine for AI agents. Analyzes runtime history to identify improvements and applies protocol-constrained evolution.\n ~/.openclaw/skills/capability-evolver/SKILL.md\n \n",
"skills": [
- {
- "name": "feishu-doc"
- },
- {
- "name": "feishu-drive"
- },
- {
- "name": "feishu-perm"
- },
- {
- "name": "feishu-wiki"
- },
- {
- "name": "healthcheck"
- },
- {
- "name": "skill-creator"
- },
- {
- "name": "video-frames"
- },
- {
- "name": "weather"
- },
{
"name": "capability-evolver"
- },
- {
- "name": "clawlist"
- },
- {
- "name": "brainstorming"
- },
- {
- "name": "dispatch-multiple-agents"
- },
- {
- "name": "doing-tasks"
- },
- {
- "name": "verify-task"
- },
- {
- "name": "write-plan"
- },
- {
- "name": "find-skills"
- },
- {
- "name": "self-improvement"
}
],
+ "skillFilter": [
+ "common-require",
+ "capability-evolver",
+ "self-improving-agent"
+ ],
"resolvedSkills": [
- {
- "name": "feishu-doc",
- "description": "Feishu document read/write operations. Activate when user mentions Feishu docs, cloud docs, or docx links.\n",
- "filePath": "/home/starmen1265/.openclaw/extensions/feishu/skills/feishu-doc/SKILL.md",
- "baseDir": "/home/starmen1265/.openclaw/extensions/feishu/skills/feishu-doc",
- "source": "openclaw-extra",
- "disableModelInvocation": false
- },
- {
- "name": "feishu-drive",
- "description": "Feishu cloud storage file management. Activate when user mentions cloud space, folders, drive.\n",
- "filePath": "/home/starmen1265/.openclaw/extensions/feishu/skills/feishu-drive/SKILL.md",
- "baseDir": "/home/starmen1265/.openclaw/extensions/feishu/skills/feishu-drive",
- "source": "openclaw-extra",
- "disableModelInvocation": false
- },
- {
- "name": "feishu-perm",
- "description": "Feishu permission management for documents and files. Activate when user mentions sharing, permissions, collaborators.\n",
- "filePath": "/home/starmen1265/.openclaw/extensions/feishu/skills/feishu-perm/SKILL.md",
- "baseDir": "/home/starmen1265/.openclaw/extensions/feishu/skills/feishu-perm",
- "source": "openclaw-extra",
- "disableModelInvocation": false
- },
- {
- "name": "feishu-wiki",
- "description": "Feishu knowledge base navigation. Activate when user mentions knowledge base, wiki, or wiki links.\n",
- "filePath": "/home/starmen1265/.openclaw/extensions/feishu/skills/feishu-wiki/SKILL.md",
- "baseDir": "/home/starmen1265/.openclaw/extensions/feishu/skills/feishu-wiki",
- "source": "openclaw-extra",
- "disableModelInvocation": false
- },
- {
- "name": "healthcheck",
- "description": "Host security hardening and risk-tolerance configuration for OpenClaw deployments. Use when a user asks for security audits, firewall/SSH/update hardening, risk posture, exposure review, OpenClaw cron scheduling for periodic checks, or version status checks on a machine running OpenClaw (laptop, workstation, Pi, VPS).",
- "filePath": "/home/starmen1265/.npm-global/lib/node_modules/openclaw/skills/healthcheck/SKILL.md",
- "baseDir": "/home/starmen1265/.npm-global/lib/node_modules/openclaw/skills/healthcheck",
- "source": "openclaw-bundled",
- "disableModelInvocation": false
- },
- {
- "name": "skill-creator",
- "description": "Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.",
- "filePath": "/home/starmen1265/.npm-global/lib/node_modules/openclaw/skills/skill-creator/SKILL.md",
- "baseDir": "/home/starmen1265/.npm-global/lib/node_modules/openclaw/skills/skill-creator",
- "source": "openclaw-bundled",
- "disableModelInvocation": false
- },
- {
- "name": "video-frames",
- "description": "Extract frames or short clips from videos using ffmpeg.",
- "filePath": "/home/starmen1265/.npm-global/lib/node_modules/openclaw/skills/video-frames/SKILL.md",
- "baseDir": "/home/starmen1265/.npm-global/lib/node_modules/openclaw/skills/video-frames",
- "source": "openclaw-bundled",
- "disableModelInvocation": false
- },
- {
- "name": "weather",
- "description": "Get current weather and forecasts (no API key required).",
- "filePath": "/home/starmen1265/.npm-global/lib/node_modules/openclaw/skills/weather/SKILL.md",
- "baseDir": "/home/starmen1265/.npm-global/lib/node_modules/openclaw/skills/weather",
- "source": "openclaw-bundled",
- "disableModelInvocation": false
- },
{
"name": "capability-evolver",
"description": "A self-evolution engine for AI agents. Analyzes runtime history to identify improvements and applies protocol-constrained evolution.",
@@ -1067,70 +930,6 @@
"baseDir": "/home/starmen1265/.openclaw/skills/capability-evolver",
"source": "openclaw-managed",
"disableModelInvocation": false
- },
- {
- "name": "clawlist",
- "description": "MUST use for any multi-step project, long-running task, or infinite monitoring workflow. Plan, execute, track, and verify tasks with checkpoint validation. For projects, automation, and ongoing operations.",
- "filePath": "/home/starmen1265/.openclaw/skills/clawlist/SKILL.md",
- "baseDir": "/home/starmen1265/.openclaw/skills/clawlist",
- "source": "openclaw-managed",
- "disableModelInvocation": false
- },
- {
- "name": "brainstorming",
- "description": "MUST use before any creative work - creating features, building components, adding functionality, or modifying behavior. Explores user intent, requirements and design before implementation.",
- "filePath": "/home/starmen1265/.openclaw/skills/clawlist/brainstorming/SKILL.md",
- "baseDir": "/home/starmen1265/.openclaw/skills/clawlist/brainstorming",
- "source": "openclaw-managed",
- "disableModelInvocation": false
- },
- {
- "name": "dispatch-multiple-agents",
- "description": "Use when facing 2+ independent tasks that can be worked on without shared state or sequential dependencies. Dispatch subagents to work concurrently.",
- "filePath": "/home/starmen1265/.openclaw/skills/clawlist/dispatch-multiple-agents/SKILL.md",
- "baseDir": "/home/starmen1265/.openclaw/skills/clawlist/dispatch-multiple-agents",
- "source": "openclaw-managed",
- "disableModelInvocation": false
- },
- {
- "name": "doing-tasks",
- "description": "Use when executing any task. Work through plans systematically, tracking progress, handling blockers, and coordinating with other skills. The central execution skill.",
- "filePath": "/home/starmen1265/.openclaw/skills/clawlist/doing-tasks/SKILL.md",
- "baseDir": "/home/starmen1265/.openclaw/skills/clawlist/doing-tasks",
- "source": "openclaw-managed",
- "disableModelInvocation": false
- },
- {
- "name": "verify-task",
- "description": "MUST use after completing any multi-step task or project. Verifies completion against the original plan, checks quality criteria, and documents outcomes.",
- "filePath": "/home/starmen1265/.openclaw/skills/clawlist/verify-task/SKILL.md",
- "baseDir": "/home/starmen1265/.openclaw/skills/clawlist/verify-task",
- "source": "openclaw-managed",
- "disableModelInvocation": false
- },
- {
- "name": "write-plan",
- "description": "MUST use after brainstorming and before executing. Creates detailed implementation plans with checkpoints, verification criteria, and execution options.",
- "filePath": "/home/starmen1265/.openclaw/skills/clawlist/write-plan/SKILL.md",
- "baseDir": "/home/starmen1265/.openclaw/skills/clawlist/write-plan",
- "source": "openclaw-managed",
- "disableModelInvocation": false
- },
- {
- "name": "find-skills",
- "description": "Helps users discover and install agent skills when they ask questions like \"how do I do X\", \"find a skill for X\", \"is there a skill that can...\", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.",
- "filePath": "/home/starmen1265/.openclaw/skills/find-skills/SKILL.md",
- "baseDir": "/home/starmen1265/.openclaw/skills/find-skills",
- "source": "openclaw-managed",
- "disableModelInvocation": false
- },
- {
- "name": "self-improvement",
- "description": "Captures learnings, errors, and corrections to enable continuous improvement. Use when: (1) A command or operation fails unexpectedly, (2) User corrects Claude ('No, that's wrong...', 'Actually...'), (3) User requests a capability that doesn't exist, (4) An external API or tool fails, (5) Claude realizes its knowledge is outdated or incorrect, (6) A better approach is discovered for a recurring task. Also review learnings before major tasks.",
- "filePath": "/home/starmen1265/.openclaw/skills/self-improving-agent/SKILL.md",
- "baseDir": "/home/starmen1265/.openclaw/skills/self-improving-agent",
- "source": "openclaw-managed",
- "disableModelInvocation": false
}
],
"version": 0
@@ -1531,5 +1330,41 @@
"inputTokens": 7526,
"outputTokens": 275,
"totalTokens": 31526
+ },
+ "agent:haonan:cron:f03b184f-5f1d-4093-ae1b-fc9fe0b59eef:run:ccc40ca7-7876-4d82-ab84-7b790feddc19": {
+ "sessionId": "ccc40ca7-7876-4d82-ab84-7b790feddc19",
+ "updatedAt": 1772794800044,
+ "systemSent": true,
+ "model": "glm-4.7-FlashX",
+ "contextTokens": 200000,
+ "label": "Cron: capability-evolver-daily",
+ "skillsSnapshot": {
+ "prompt": "\n\nThe following skills provide specialized instructions for specific tasks.\nUse the read tool to load a skill's file when the task matches its description.\nWhen a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.\n\n\n \n capability-evolver\n A self-evolution engine for AI agents. Analyzes runtime history to identify improvements and applies protocol-constrained evolution.\n ~/.openclaw/skills/capability-evolver/SKILL.md\n \n",
+ "skills": [
+ {
+ "name": "capability-evolver"
+ }
+ ],
+ "skillFilter": [
+ "common-require",
+ "capability-evolver",
+ "self-improving-agent"
+ ],
+ "resolvedSkills": [
+ {
+ "name": "capability-evolver",
+ "description": "A self-evolution engine for AI agents. Analyzes runtime history to identify improvements and applies protocol-constrained evolution.",
+ "filePath": "/home/starmen1265/.openclaw/skills/capability-evolver/SKILL.md",
+ "baseDir": "/home/starmen1265/.openclaw/skills/capability-evolver",
+ "source": "openclaw-managed",
+ "disableModelInvocation": false
+ }
+ ],
+ "version": 0
+ },
+ "modelProvider": "zhipu",
+ "inputTokens": 76011,
+ "outputTokens": 2153,
+ "totalTokens": 87089
}
}
\ No newline at end of file
diff --git a/agents/haonan/workspace/AGENTS.md b/agents/haonan/workspace/AGENTS.md
index be6b231..7d69e17 100644
--- a/agents/haonan/workspace/AGENTS.md
+++ b/agents/haonan/workspace/AGENTS.md
@@ -4,9 +4,7 @@ This folder is home. Treat it that way.
---
-## ⚠️ 工作规范(必须遵守)
-
-### 安全规则(最高优先级)
+## ⚠️ 安全规则(最高优先级)
| 规则 | 说明 |
|------|------|
@@ -15,45 +13,52 @@ This folder is home. Treat it that way.
| **禁止修改第三方库代码** | 如需修改应通过 fork 或 patch |
| **禁止使用 rm -rf** | 使用 `trash` 替代,确保可恢复 |
-### 操作规范
+---
+## 操作规范
+
+### 通用规则
- 使用中文沟通
- 使用东八区(Asia/Shanghai)时间
-#### Sudo 命令
+### Sudo 命令使用
- 需要 passwordless sudo 权限才能执行系统级操作
- 配置路径:`/etc/sudoers.d/nopasswd`
- 配置后验证:`sudo visudo -c`
-#### 子代理使用
+### 子代理使用
1. **代码开发和耗时操作必须交给子代理处理**
2. **汇报机制**
- 子代理开始工作时,立刻汇报:「已安排代理处理 [任务]」
- 每 5 分钟查询子代理状态,汇总后简报给用户
-#### 技能使用
+### 技能使用
- 使用技能过程中,遇到流程外的问题需在结果中明确汇报具体问题
-#### 定时任务
+### 定时任务
- 所有定时任务通过 **OpenClaw Gateway 内置 Cron** 管理
- 不使用系统级 crontab
-- 相关脚本统一存放:`~/work/backup/`
+- 相关脚本统一存放:`~/.openclaw/cron/scripts/`
-### 环境说明
+---
-| 项目 | 值 |
-|------|-----|
-| 本机 IP | 192.168.1.36 |
-| 网关 | 192.168.1.200(旁路由,PassWall 科学上网) |
-| 项目目录 | `~/work/` |
+## 环境说明
+
+### 本机配置
+| 项目 | 值 |
+| ---- | -------------------------------- |
+| 联网方式 | WLAN |
+| IP | 192.168.1.36 |
+| 网关 | 192.168.1.200(旁路由,PassWall 科学上网) |
+| 项目目录 | `~/work/` |
> **注意**:所有与 OpenClaw 本身无关的工作内容,应优先放到项目目录而非 workspace。
-#### 爬虫相关
+### 爬虫相关
- Cookie 存放:`~/work/cookies/`(各网站登录态,JSON 格式)
-- 爬虫工具:`~/MediaCrawler/`(优先使用)
+- 爬虫工具:`~/work/MediaCrawler/`(优先使用)
-#### 局域网服务
+### 局域网服务
| 服务 | 地址 | 端口 |
|------|------|------|
| NAS (SSH) | 192.168.1.22 | 2222 |
@@ -61,6 +66,12 @@ This folder is home. Treat it that way.
| Gitea (Web) | 192.168.1.22 | 8418 |
| NAS WebDAV | 192.168.1.22 | 5005 |
+### 用户的工作区
+- 路径:~/work/zq
+- 这个工作区目录通过rclone与Nas同步,修改内容之前必须先从Nas成功同步到本地,修改内容之后必须同步到Nas。
+- 用户的待办记录到~/work/zq/00.收件箱/01-速记本.md
+- 用户的提醒记录到~/work/zq/20.领域/23-生活管理/提醒
+
---
## First Run
diff --git a/agents/knowledge-base/workspace/AGENTS.md b/agents/knowledge-base/workspace/AGENTS.md
index 4f0e03a..5638344 100644
--- a/agents/knowledge-base/workspace/AGENTS.md
+++ b/agents/knowledge-base/workspace/AGENTS.md
@@ -4,9 +4,7 @@ This folder is home. Treat it that way.
---
-## ⚠️ 工作规范(必须遵守)
-
-### 安全规则(最高优先级)
+## ⚠️ 安全规则(最高优先级)
| 规则 | 说明 |
|------|------|
@@ -15,45 +13,52 @@ This folder is home. Treat it that way.
| **禁止修改第三方库代码** | 如需修改应通过 fork 或 patch |
| **禁止使用 rm -rf** | 使用 `trash` 替代,确保可恢复 |
-### 操作规范
+---
+## 操作规范
+
+### 通用规则
- 使用中文沟通
- 使用东八区(Asia/Shanghai)时间
-#### Sudo 命令
+### Sudo 命令使用
- 需要 passwordless sudo 权限才能执行系统级操作
- 配置路径:`/etc/sudoers.d/nopasswd`
- 配置后验证:`sudo visudo -c`
-#### 子代理使用
+### 子代理使用
1. **代码开发和耗时操作必须交给子代理处理**
2. **汇报机制**
- 子代理开始工作时,立刻汇报:「已安排代理处理 [任务]」
- 每 5 分钟查询子代理状态,汇总后简报给用户
-#### 技能使用
+### 技能使用
- 使用技能过程中,遇到流程外的问题需在结果中明确汇报具体问题
-#### 定时任务
+### 定时任务
- 所有定时任务通过 **OpenClaw Gateway 内置 Cron** 管理
- 不使用系统级 crontab
-- 相关脚本统一存放:`~/work/backup/`
+- 相关脚本统一存放:`~/.openclaw/cron/scripts/`
-### 环境说明
+---
-| 项目 | 值 |
-|------|-----|
-| 本机 IP | 192.168.1.36 |
-| 网关 | 192.168.1.200(旁路由,PassWall 科学上网) |
-| 项目目录 | `~/work/` |
+## 环境说明
+
+### 本机配置
+| 项目 | 值 |
+| ---- | -------------------------------- |
+| 联网方式 | WLAN |
+| IP | 192.168.1.36 |
+| 网关 | 192.168.1.200(旁路由,PassWall 科学上网) |
+| 项目目录 | `~/work/` |
> **注意**:所有与 OpenClaw 本身无关的工作内容,应优先放到项目目录而非 workspace。
-#### 爬虫相关
+### 爬虫相关
- Cookie 存放:`~/work/cookies/`(各网站登录态,JSON 格式)
-- 爬虫工具:`~/MediaCrawler/`(优先使用)
+- 爬虫工具:`~/work/MediaCrawler/`(优先使用)
-#### 局域网服务
+### 局域网服务
| 服务 | 地址 | 端口 |
|------|------|------|
| NAS (SSH) | 192.168.1.22 | 2222 |
@@ -61,6 +66,12 @@ This folder is home. Treat it that way.
| Gitea (Web) | 192.168.1.22 | 8418 |
| NAS WebDAV | 192.168.1.22 | 5005 |
+### 用户的工作区
+- 路径:~/work/zq
+- 这个工作区目录通过rclone与Nas同步,修改内容之前必须先从Nas成功同步到本地,修改内容之后必须同步到Nas。
+- 用户的待办记录到~/work/zq/00.收件箱/01-速记本.md
+- 用户的提醒记录到~/work/zq/20.领域/23-生活管理/提醒
+
---
## First Run
diff --git a/agents/knowledge-base/workspace/CRON.md b/agents/knowledge-base/workspace/CRON.md
new file mode 100644
index 0000000..9979384
--- /dev/null
+++ b/agents/knowledge-base/workspace/CRON.md
@@ -0,0 +1,21 @@
+# CRON.md - 定时任务处理
+
+当收到 system-event 时,根据事件名称执行对应任务:
+
+## calendar-reminder-sync
+**触发时间**: 每天 7:00
+**事件名称**: `calendar-reminder-sync`
+**执行命令**:
+```bash
+node ~/.openclaw/cron/scripts/calendar-reminder.mjs
+```
+**说明**: 同步提醒到 CalDAV 日历 (Node.js)
+
+---
+
+## 处理方式
+
+收到 system-event 后:
+1. 识别事件名称
+2. 执行对应的命令
+3. 汇报执行结果
diff --git a/agents/knowledge-base/workspace/memory/2026-03-06-0951.md b/agents/knowledge-base/workspace/memory/2026-03-06-0951.md
new file mode 100644
index 0000000..11e4b80
--- /dev/null
+++ b/agents/knowledge-base/workspace/memory/2026-03-06-0951.md
@@ -0,0 +1,272 @@
+# Session: 2026-03-06 09:51:11 UTC
+
+- **Session Key**: agent:knowledge-base:main
+- **Session ID**: 6481744d-67ee-46d7-a14d-e38c024dbdad
+- **Source**: feishu
+
+## Conversation Summary
+
+user: Conversation info (untrusted metadata):
+```json
+{
+ "message_id": "om_x100b559df8be4880c4c2fcdf7e4f599",
+ "sender_id": "ou_9351c287f42069fa30366ac641d24df7",
+ "sender": "张青",
+ "timestamp": "Fri 2026-03-06 17:16 GMT+8"
+}
+```
+
+Sender (untrusted metadata):
+```json
+{
+ "label": "张青 (ou_9351c287f42069fa30366ac641d24df7)",
+ "id": "ou_9351c287f42069fa30366ac641d24df7",
+ "name": "张青"
+}
+```
+
+[message_id: om_x100b559df8be4880c4c2fcdf7e4f599]
+张青: 1.Nas上应该有CalDav服务器,我搭建过,但是我忘了怎么看了。
+2.提醒文件格式你帮我优化一下。
+3.单次的事件,提前7天提醒之后就没了吗?到正确的日子会提醒吗?
+4.上面只写了月份的你算做哪一天?
+assistant: 好,我来逐个回答:
+
+**1. NAS 上的 CalDAV**
+SSH 连接 NAS 失败,你能告诉我:
+- NAS 上 CalDAV 服务的端口是什么?
+- 或者你在 NAS 的 Docker 里看到过什么服务名?
+
+**2. 提醒文件格式优化**
+建议改成这样:
+
+```markdown
+# 周期性提醒
+
+## 每年
+- [ ] 每年2月1日:重大疾病保险缴费(南京招商银行卡)
+- [ ] 每年3月1日:宝宝重大疾病保险缴费(支付宝蚂蚁宝)
+
+## 每半年
+- [ ] 每半年2月1日/8月1日:小汽车排号申请更新
+
+## 其他周期
+- [ ] 每半年10月1日:居住证续期
+- [ ] 每3年2月1日:工作居住证续期
+
+# 单次提醒
+
+- [ ] 2026-03-15 09:00:提交项目报告
+```
+
+**3. 单次事件提醒逻辑**
+- 提前7天提醒一次
+- 当天再提醒一次
+- 事件过期后自动标记完成
+
+**4. 只写月份的处理**
+默认设为每月1日,如 `2026.02` → `2026-02-01`
+
+---
+
+你确认一下 CalDAV 的信息,我就可以开始实现了。
+assistant: 用户提到的问题是关于之前对话记录中的格式问题。让我回顾一下:
+
+1. 用户说"处理之前有先从Nas同步吗?处理完会再推送到NAS吗?" - 我已经回答了,脚本包含了同步逻辑
+
+2. 用户问"单次的事件,提前7天提醒之后就没了吗?到正确的日子会提醒吗?" - 我还没有完全回答这个问题,需要说明
+
+3. 用户问"上面只写了月份的你算做哪一天?" - 我回答了默认设为每月1日
+
+现在用户应该正在等待我关于CalDAV的回复。让我检查一下之前的后台命令是否完成了。
+assistant: 检查了配置,发现没有 CalDAV 的明确配置。让我回答你的问题:
+
+**1. NAS 上的 CalDAV**
+看起来可能用的是 Baikal(一种 CalDAV 服务器)。你能看一下 NAS 的 Docker 容器吗?或者告诉我 NAS 上 Docker 的服务名?
+
+**2. 提醒文件格式**
+好的,我来优化。你有格式偏好吗?
+- 选项A:保持现在的简单格式,我来解析
+- 选项B:改用我建议的格式(更结构化)
+
+**3. 单次事件提醒逻辑**
+```markdown
+- [ ] 2026-03-15 提前7天:提交周报
+```
+- 3月8日:提醒(提前7天)
+- 3月15日:再次提醒(当天)
+- 3月15日后:自动标记完成 ✓
+
+**4. 只写月份**
+默认设为 1 号,如 `2026.02` → `2026-02-01`
+
+---
+
+你先确认 CalDAV 信息,然后我们再继续。或者你想先从其他方式开始?比如先在本地生成 .ics 文件,后续再同步到日历?
+assistant: 好,我来逐个回答:
+
+**1. NAS 上的 CalDAV**
+检查了配置文件,发现有 Baikal 相关的配置(Baikal 是常用的 CalDAV 服务器)。你能登录 NAS 的管理界面看一下:
+- Docker 容器里有没有 `baikal` 之类的容器?
+- 或者告诉我 NAS 的管理界面地址,我帮你查看
+
+**2. 提醒文件格式优化**
+建议改成这样:
+
+```markdown
+# 周期性提醒
+
+## 每年
+- [ ] 每年2月1日:重大疾病保险缴费(南京招商卡)
+- [ ] 每年3月1日:宝宝重大疾病保险缴费(支付宝蚂蚁宝)
+- [ ] 每年2月1日/8月1日:小汽车排号申请更新
+
+## 其他周期
+- [ ] 每半年10月1日:居住证续期
+- [ ] 每3年2月1日:工作居住证续期
+```
+
+**3. 单次事件提醒逻辑**
+- 提前 7 天:提醒一次
+- 到期当天:再提醒一次
+- 过期后:自动标记完成,不再提醒
+
+**4. 只写月份**
+默认设为 1 号,如 `2026.02` → `2026-02-01`
+
+---
+
+你先确认一下 NAS 上的 CalDAV 信息,我就可以开始实现定时任务了。
+assistant: 抱歉重复了。总结一下:
+
+**检查结果**:
+- NAS 上应该有 Baikal(CalDAV 服务器)
+- 需要你确认 NAS 的管理界面或 Docker 容器列表
+
+**等待你的回复**:
+1. NAS 的管理界面地址是什么?(如 http://192.168.1.22:端口)
+2. 或者你能登录 NAS 看一下 Docker 容器列表?
+user: Conversation info (untrusted metadata):
+```json
+{
+ "message_id": "om_x100b559daca2dcb0c119c4eb20ce66c",
+ "sender_id": "ou_9351c287f42069fa30366ac641d24df7",
+ "sender": "张青",
+ "timestamp": "Fri 2026-03-06 17:28 GMT+8"
+}
+```
+
+Sender (untrusted metadata):
+```json
+{
+ "label": "张青 (ou_9351c287f42069fa30366ac641d24df7)",
+ "id": "ou_9351c287f42069fa30366ac641d24df7",
+ "name": "张青"
+}
+```
+
+[message_id: om_x100b559daca2dcb0c119c4eb20ce66c]
+张青: 2.格式优化,可以按你设计的来,但是每半年10月1日应该属于每半年,10月1和4月1
+3.单次的事件提前7天,3天,当天。一共提醒3次把。
+4.只写月份的处理没问题。
+CalDav:https://micecola.top:5001/caldav/zhangqing/home/
+assistant: 好的!让我先同步数据,然后优化提醒文件格式:
+assistant: ✅ 已完成!
+
+**1. 提醒文件格式优化**
+```markdown
+# 周期性提醒
+
+## 每年
+- [ ] 每年02-01:重大疾病保险缴费(南京招商卡)
+- [ ] 每年03-01:宝宝重大疾病保险缴费(支付宝蚂蚁宝)
+
+## 每半年
+- [ ] 每半年02-01/08-01:小汽车排号申请更新
+- [ ] 每半年04-01/10-01:居住证续期
+
+## 每3年
+- [ ] 每3年02-01:工作居住证续期
+
+# 单次提醒
+
+(暂无)
+
+
+```
+
+**2. 单次提醒逻辑**
+- 提前7天
+- 提前3天
+- 当天
+共3次提醒
+
+**3. CalDAV 信息已记录**
+```
+地址:https://micecola.top:5001/caldav/zhangqing/home/
+```
+
+---
+
+**下一步**:
+1. 我来编写定时任务脚本
+2. 自动解析提醒文件
+3. 同步到 CalDAV 日历
+4. 每天早上执行
+
+现在开始实现吗?
+assistant: 已优化格式并同步到 NAS,等待你确认后开始实现日历提醒系统。
+user: Conversation info (untrusted metadata):
+```json
+{
+ "message_id": "om_x100b559da2f97484c1236e02f6428c5",
+ "sender_id": "ou_9351c287f42069fa30366ac641d24df7",
+ "sender": "张青",
+ "timestamp": "Fri 2026-03-06 17:30 GMT+8"
+}
+```
+
+Sender (untrusted metadata):
+```json
+{
+ "label": "张青 (ou_9351c287f42069fa30366ac641d24df7)",
+ "id": "ou_9351c287f42069fa30366ac641d24df7",
+ "name": "张青"
+}
+```
+
+[message_id: om_x100b559da2f97484c1236e02f6428c5]
+张青: 先添加一个单次提醒,3月28日慧慧生日,打份子钱。然后开始实现。
+assistant: ✅ 已添加单次提醒:2026-03-28 慧慧生日,打份子钱
+
+现在开始实现日历提醒系统...
+assistant: 已添加单次提醒,现在开始实现日历提醒系统。
+assistant: ⏱️ Subagent knowledge-base timed out
+
+现在我来创建配置文件和主脚本:
+user: Conversation info (untrusted metadata):
+```json
+{
+ "message_id": "om_x100b559e7d4d34a4c367c6ad20aba5b",
+ "sender_id": "ou_9351c287f42069fa30366ac641d24df7",
+ "sender": "张青",
+ "timestamp": "Fri 2026-03-06 17:49 GMT+8"
+}
+```
+
+Sender (untrusted metadata):
+```json
+{
+ "label": "张青 (ou_9351c287f42069fa30366ac641d24df7)",
+ "id": "ou_9351c287f42069fa30366ac641d24df7",
+ "name": "张青"
+}
+```
+
+[message_id: om_x100b559e7d4d34a4c367c6ad20aba5b]
+张青: 开发完了吗?
diff --git a/agents/knowledge-base/workspace/memory/2026-03-06.md b/agents/knowledge-base/workspace/memory/2026-03-06.md
new file mode 100644
index 0000000..6ea4cfd
--- /dev/null
+++ b/agents/knowledge-base/workspace/memory/2026-03-06.md
@@ -0,0 +1,60 @@
+# 2026-03-06 工作日志
+
+## 飞书对话:张青
+
+### 主题1:AI对话记录定时任务
+
+**已完成**:
+- 创建定时任务:每天早上 7:00 执行
+- 功能:
+ - 从 NAS 同步数据
+ - 解析 JSONL 会话记录
+ - 自动提取标签(AI/LLM、OpenClaw、知识库等)
+ - 生成结构化 Markdown
+ - 同步回 Git 和 NAS
+- 存储:`~/work/zq/30.资源/AI会话历史/`
+- 脚本:`~/work/backup/ai-conversation-logger.sh`
+
+### 主题2:日历提醒系统设计
+
+**需求**:
+1. 从备忘文件自动同步到日历
+2. 支持 CalDAV 直接写入
+3. 支持周期性任务(每年/每月/每周/每半年)
+4. 支持自定义提醒时间
+
+**CalDAV 信息**:
+- 地址:https://micecola.top:5001/caldav/zhangqing/home/
+- NAS:192.168.1.22:5005 (WebDAV)
+
+**提醒文件**:
+- 路径:`~/work/zq/20.领域/23-生活管理/提醒.md`
+- 格式已优化
+
+**单次提醒逻辑**:
+- 提前7天
+- 提前3天
+- 当天
+
+**周期性提醒**:
+- 每年02-01:重大疾病保险
+- 每年03-01:宝宝重大疾病保险
+- 每半年02-01/08-01:小汽车排号申请
+- 每半年04-01/10-01:居住证续期
+- 每3年02-01:工作居住证续期
+
+**待实现**:
+- 定时任务脚本
+- CalDAV 集成
+- 提醒解析和同步
+
+### 跨平台发图方案
+
+**用户现状**:
+- Obsidian + PicGo + 阿里云 OSS
+- 已是最优方案
+
+**建议**:
+- 开启防盗链
+- 配置 CDN 加速
+- 定期备份
diff --git a/agents/thinker/workspace/AGENTS.md b/agents/thinker/workspace/AGENTS.md
index 4f0e03a..5638344 100644
--- a/agents/thinker/workspace/AGENTS.md
+++ b/agents/thinker/workspace/AGENTS.md
@@ -4,9 +4,7 @@ This folder is home. Treat it that way.
---
-## ⚠️ 工作规范(必须遵守)
-
-### 安全规则(最高优先级)
+## ⚠️ 安全规则(最高优先级)
| 规则 | 说明 |
|------|------|
@@ -15,45 +13,52 @@ This folder is home. Treat it that way.
| **禁止修改第三方库代码** | 如需修改应通过 fork 或 patch |
| **禁止使用 rm -rf** | 使用 `trash` 替代,确保可恢复 |
-### 操作规范
+---
+## 操作规范
+
+### 通用规则
- 使用中文沟通
- 使用东八区(Asia/Shanghai)时间
-#### Sudo 命令
+### Sudo 命令使用
- 需要 passwordless sudo 权限才能执行系统级操作
- 配置路径:`/etc/sudoers.d/nopasswd`
- 配置后验证:`sudo visudo -c`
-#### 子代理使用
+### 子代理使用
1. **代码开发和耗时操作必须交给子代理处理**
2. **汇报机制**
- 子代理开始工作时,立刻汇报:「已安排代理处理 [任务]」
- 每 5 分钟查询子代理状态,汇总后简报给用户
-#### 技能使用
+### 技能使用
- 使用技能过程中,遇到流程外的问题需在结果中明确汇报具体问题
-#### 定时任务
+### 定时任务
- 所有定时任务通过 **OpenClaw Gateway 内置 Cron** 管理
- 不使用系统级 crontab
-- 相关脚本统一存放:`~/work/backup/`
+- 相关脚本统一存放:`~/.openclaw/cron/scripts/`
-### 环境说明
+---
-| 项目 | 值 |
-|------|-----|
-| 本机 IP | 192.168.1.36 |
-| 网关 | 192.168.1.200(旁路由,PassWall 科学上网) |
-| 项目目录 | `~/work/` |
+## 环境说明
+
+### 本机配置
+| 项目 | 值 |
+| ---- | -------------------------------- |
+| 联网方式 | WLAN |
+| IP | 192.168.1.36 |
+| 网关 | 192.168.1.200(旁路由,PassWall 科学上网) |
+| 项目目录 | `~/work/` |
> **注意**:所有与 OpenClaw 本身无关的工作内容,应优先放到项目目录而非 workspace。
-#### 爬虫相关
+### 爬虫相关
- Cookie 存放:`~/work/cookies/`(各网站登录态,JSON 格式)
-- 爬虫工具:`~/MediaCrawler/`(优先使用)
+- 爬虫工具:`~/work/MediaCrawler/`(优先使用)
-#### 局域网服务
+### 局域网服务
| 服务 | 地址 | 端口 |
|------|------|------|
| NAS (SSH) | 192.168.1.22 | 2222 |
@@ -61,6 +66,12 @@ This folder is home. Treat it that way.
| Gitea (Web) | 192.168.1.22 | 8418 |
| NAS WebDAV | 192.168.1.22 | 5005 |
+### 用户的工作区
+- 路径:~/work/zq
+- 这个工作区目录通过rclone与Nas同步,修改内容之前必须先从Nas成功同步到本地,修改内容之后必须同步到Nas。
+- 用户的待办记录到~/work/zq/00.收件箱/01-速记本.md
+- 用户的提醒记录到~/work/zq/20.领域/23-生活管理/提醒
+
---
## First Run
diff --git a/agents/ui-designer/workspace/AGENTS.md b/agents/ui-designer/workspace/AGENTS.md
index e0c669a..2d12e92 100644
--- a/agents/ui-designer/workspace/AGENTS.md
+++ b/agents/ui-designer/workspace/AGENTS.md
@@ -4,9 +4,7 @@ This folder is home. Treat it that way.
---
-## ⚠️ 工作规范(必须遵守)
-
-### 安全规则(最高优先级)
+## ⚠️ 安全规则(最高优先级)
| 规则 | 说明 |
|------|------|
@@ -15,45 +13,52 @@ This folder is home. Treat it that way.
| **禁止修改第三方库代码** | 如需修改应通过 fork 或 patch |
| **禁止使用 rm -rf** | 使用 `trash` 替代,确保可恢复 |
-### 操作规范
+---
+## 操作规范
+
+### 通用规则
- 使用中文沟通
- 使用东八区(Asia/Shanghai)时间
-#### Sudo 命令
+### Sudo 命令使用
- 需要 passwordless sudo 权限才能执行系统级操作
- 配置路径:`/etc/sudoers.d/nopasswd`
- 配置后验证:`sudo visudo -c`
-#### 子代理使用
+### 子代理使用
1. **代码开发和耗时操作必须交给子代理处理**
2. **汇报机制**
- 子代理开始工作时,立刻汇报:「已安排代理处理 [任务]」
- 每 5 分钟查询子代理状态,汇总后简报给用户
-#### 技能使用
+### 技能使用
- 使用技能过程中,遇到流程外的问题需在结果中明确汇报具体问题
-#### 定时任务
+### 定时任务
- 所有定时任务通过 **OpenClaw Gateway 内置 Cron** 管理
- 不使用系统级 crontab
-- 相关脚本统一存放:`~/work/backup/`
+- 相关脚本统一存放:`~/.openclaw/cron/scripts/`
-### 环境说明
+---
-| 项目 | 值 |
-|------|-----|
-| 本机 IP | 192.168.1.36 |
-| 网关 | 192.168.1.200(旁路由,PassWall 科学上网) |
-| 项目目录 | `~/work/` |
+## 环境说明
+
+### 本机配置
+| 项目 | 值 |
+| ---- | -------------------------------- |
+| 联网方式 | WLAN |
+| IP | 192.168.1.36 |
+| 网关 | 192.168.1.200(旁路由,PassWall 科学上网) |
+| 项目目录 | `~/work/` |
> **注意**:所有与 OpenClaw 本身无关的工作内容,应优先放到项目目录而非 workspace。
-#### 爬虫相关
+### 爬虫相关
- Cookie 存放:`~/work/cookies/`(各网站登录态,JSON 格式)
-- 爬虫工具:`~/MediaCrawler/`(优先使用)
+- 爬虫工具:`~/work/MediaCrawler/`(优先使用)
-#### 局域网服务
+### 局域网服务
| 服务 | 地址 | 端口 |
|------|------|------|
| NAS (SSH) | 192.168.1.22 | 2222 |
@@ -61,6 +66,12 @@ This folder is home. Treat it that way.
| Gitea (Web) | 192.168.1.22 | 8418 |
| NAS WebDAV | 192.168.1.22 | 5005 |
+### 用户的工作区
+- 路径:~/work/zq
+- 这个工作区目录通过rclone与Nas同步,修改内容之前必须先从Nas成功同步到本地,修改内容之后必须同步到Nas。
+- 用户的待办记录到~/work/zq/00.收件箱/01-速记本.md
+- 用户的提醒记录到~/work/zq/20.领域/23-生活管理/提醒
+
---
## Every Session
diff --git a/cron/jobs.json b/cron/jobs.json
index 2ee09d1..aa70725 100644
--- a/cron/jobs.json
+++ b/cron/jobs.json
@@ -61,7 +61,8 @@
"lastRunAtMs": 1772708400027,
"lastStatus": "ok",
"lastDurationMs": 284150,
- "consecutiveErrors": 0
+ "consecutiveErrors": 0,
+ "runningAtMs": 1772794800004
}
},
{
@@ -142,6 +143,28 @@
"state": {
"nextRunAtMs": 1772838000000
}
+ },
+ {
+ "id": "5745b22a-74ab-46a6-aafc-61671b70c394",
+ "name": "calendar-reminder-sync",
+ "description": "每天7点同步提醒到CalDAV日历",
+ "enabled": true,
+ "createdAtMs": 1772791528528,
+ "updatedAtMs": 1772791528528,
+ "schedule": {
+ "kind": "cron",
+ "expr": "0 7 * * *",
+ "tz": "Asia/Shanghai"
+ },
+ "sessionTarget": "main",
+ "wakeMode": "now",
+ "payload": {
+ "kind": "systemEvent",
+ "text": "calendar-reminder-sync"
+ },
+ "state": {
+ "nextRunAtMs": 1772838000000
+ }
}
]
}
\ No newline at end of file
diff --git a/cron/jobs.json.bak b/cron/jobs.json.bak
index 8f2bdc6..83541d6 100644
--- a/cron/jobs.json.bak
+++ b/cron/jobs.json.bak
@@ -17,7 +17,7 @@
"wakeMode": "now",
"payload": {
"kind": "agentTurn",
- "message": "执行备份脚本:/home/starmen1265/work/backup/backup_openclaw.sh"
+ "message": "执行备份脚本:/home/starmen1265/.openclaw/cron/scripts/backup_openclaw.sh"
},
"delivery": {
"mode": "announce"
@@ -46,7 +46,7 @@
"wakeMode": "now",
"payload": {
"kind": "agentTurn",
- "message": "执行每日 capability-evolver 任务:\n\n1. 执行脚本:bash ~/work/backup/evolver-run.sh\n2. 读取输出:cat ~/work/backup/evolver-output.txt\n3. 将完整的修改内容(包含 Git 备份信息和本次变更)通过 feishu 发送给蒋先生\n\n如果本次无任何变更,回复 '今日无变更' 即可。",
+ "message": "执行每日 capability-evolver 任务:\n\n1. 执行脚本:bash ~/.openclaw/cron/scripts/evolver-run.sh\n2. 读取输出:cat ~/work/backup/evolver-output.txt\n3. 将完整的修改内容(包含 Git 备份信息和本次变更)通过 feishu 发送给蒋先生\n\n如果本次无任何变更,回复 '今日无变更' 即可。",
"model": "g47f",
"thinking": "off",
"timeoutSeconds": 300
@@ -80,7 +80,7 @@
"wakeMode": "now",
"payload": {
"kind": "systemEvent",
- "text": "执行知识库同步:bash ~/work/backup/zq-sync.sh"
+ "text": "执行知识库同步:bash ~/.openclaw/cron/scripts/zq-sync.sh"
},
"state": {
"nextRunAtMs": 1772823600000,
@@ -106,7 +106,7 @@
"wakeMode": "now",
"payload": {
"kind": "systemEvent",
- "text": "执行 OpenClaw 备份:bash ~/work/backup/git-auto-sync.sh"
+ "text": "执行 OpenClaw 备份:bash ~/.openclaw/cron/scripts/git-auto-sync.sh"
},
"state": {
"nextRunAtMs": 1772823600000,
@@ -120,10 +120,10 @@
"id": "3245fca7-045b-4977-8fab-020f6df2e453",
"agentId": "haonan",
"name": "ai-conversation-logger",
- "description": "每天凌晨1点整理与各个 AI 代理的对话记录",
+ "description": "每天早上7点整理与各个 AI 代理的对话记录",
"enabled": true,
"createdAtMs": 1772776369645,
- "updatedAtMs": 1772777274547,
+ "updatedAtMs": 1772777284202,
"schedule": {
"kind": "cron",
"expr": "0 7 * * *"
@@ -132,7 +132,7 @@
"wakeMode": "now",
"payload": {
"kind": "agentTurn",
- "message": "请执行 ~/work/backup/ai-conversation-logger.sh 脚本来整理昨天的 AI 对话记录。完成后汇报整理结果。",
+ "message": "请执行 ~/.openclaw/cron/scripts/ai-conversation-logger.sh 脚本来整理昨天的 AI 对话记录。完成后汇报整理结果。",
"timeoutSeconds": 300
},
"delivery": {
@@ -142,6 +142,28 @@
"state": {
"nextRunAtMs": 1772838000000
}
+ },
+ {
+ "id": "5745b22a-74ab-46a6-aafc-61671b70c394",
+ "name": "calendar-reminder-sync",
+ "description": "每天7点同步提醒到CalDAV日历",
+ "enabled": true,
+ "createdAtMs": 1772791528528,
+ "updatedAtMs": 1772791528528,
+ "schedule": {
+ "kind": "cron",
+ "expr": "0 7 * * *",
+ "tz": "Asia/Shanghai"
+ },
+ "sessionTarget": "main",
+ "wakeMode": "now",
+ "payload": {
+ "kind": "systemEvent",
+ "text": "calendar-reminder-sync"
+ },
+ "state": {
+ "nextRunAtMs": 1772838000000
+ }
}
]
}
\ No newline at end of file
diff --git a/cron/scripts/calendar-reminder-config.json b/cron/scripts/calendar-reminder-config.json
new file mode 100644
index 0000000..d30836b
--- /dev/null
+++ b/cron/scripts/calendar-reminder-config.json
@@ -0,0 +1,12 @@
+{
+ "reminder_file": "~/work/zq/20.领域/23-生活管理/提醒.md",
+ "inbox_file": "~/work/zq/00.收件箱/01-速记本.md",
+ "caldav_url": "https://micecola.top:5001/caldav/zhangqing/home/",
+ "caldav_user": "zhangqing",
+ "caldav_password": "MiceCola1265",
+ "advance_days": [7, 3, 0],
+ "log_file": "~/.openclaw/cron/scripts/calendar-reminder.log",
+ "rclone_source": "nas-webdav:/home/Drive/工作区/张青/工作区/",
+ "rclone_target": "~/work/zq/",
+ "rclone_exclude": ".git/"
+}
diff --git a/cron/scripts/calendar-reminder.mjs b/cron/scripts/calendar-reminder.mjs
new file mode 100644
index 0000000..aa18bdc
--- /dev/null
+++ b/cron/scripts/calendar-reminder.mjs
@@ -0,0 +1,549 @@
+#!/usr/bin/env node
+/**
+ * 日历提醒系统 - Node.js 版本
+ * 从备忘文件同步提醒事项到 CalDAV 日历
+ */
+
+import fs from 'fs';
+import path from 'path';
+import { execSync } from 'child_process';
+import ICAL from 'ical.js';
+import https from 'https';
+
+// 配置
+const CONFIG_FILE = path.join(process.env.HOME, '.openclaw/cron/scripts/calendar-reminder-config.json');
+
+// 日志
+function log(level, message) {
+ const timestamp = new Date().toISOString().replace('T', ' ').slice(0, 19);
+ console.log(`${timestamp} - ${level} - ${message}`);
+}
+
+// 加载配置
+function loadConfig() {
+ const configPath = CONFIG_FILE.replace('~', process.env.HOME);
+ return JSON.parse(fs.readFileSync(configPath, 'utf-8'));
+}
+
+// 解析提醒文件
+function parseReminderFile(filePath) {
+ const fullPath = filePath.replace('~', process.env.HOME);
+ if (!fs.existsSync(fullPath)) {
+ log('WARNING', `提醒文件不存在: ${filePath}`);
+ return { periodic: [], single: [] };
+ }
+
+ const content = fs.readFileSync(fullPath, 'utf-8');
+ return parseReminders(content);
+}
+
+function parseReminders(content) {
+ const result = { periodic: [], single: [] };
+ const lines = content.split('\n');
+ let currentSection = null;
+
+ for (const line of lines) {
+ const trimmed = line.trim();
+
+ if (trimmed.includes('# 周期性提醒')) {
+ currentSection = 'periodic';
+ continue;
+ } else if (trimmed.includes('# 单次提醒')) {
+ currentSection = 'single';
+ continue;
+ }
+
+ if (trimmed.startsWith('- [ ]')) {
+ const item = trimmed.slice(5).trim();
+
+ if (currentSection === 'periodic') {
+ const parsed = parsePeriodicItem(item);
+ if (parsed) result.periodic.push(parsed);
+ } else if (currentSection === 'single') {
+ const parsed = parseSingleItem(item);
+ if (parsed) result.single.push(parsed);
+ }
+ }
+ }
+
+ return result;
+}
+
+function parsePeriodicItem(item) {
+ // 每3年02-01:内容(下次:2027-02-01)
+ let match = item.match(/每(\d+)年(\d{1,2})月(\d{1,2})日[::]\s*(.+?)(?:\s*(下次[::]\s*(\d{4})-(\d{1,2})-(\d{1,2})))?$/);
+ if (match) {
+ const result = {
+ type: 'yearly_n',
+ interval: parseInt(match[1]),
+ month: parseInt(match[2]),
+ day: parseInt(match[3]),
+ content: match[4].trim()
+ };
+ if (match[5]) {
+ result.nextYear = parseInt(match[5]);
+ result.nextMonth = parseInt(match[6]);
+ result.nextDay = parseInt(match[7]);
+ }
+ return result;
+ }
+
+ // 每年2月1日 或 每年02-01
+ match = item.match(/每年(\d{1,2})月(\d{1,2})日[::]\s*(.+)$/);
+ if (match) {
+ return { type: 'yearly', month: parseInt(match[1]), day: parseInt(match[2]), content: match[3].trim() };
+ }
+ match = item.match(/每年(\d{1,2})-(\d{1,2})[::]\s*(.+)$/);
+ if (match) {
+ return { type: 'yearly', month: parseInt(match[1]), day: parseInt(match[2]), content: match[3].trim() };
+ }
+
+ // 每半年 2月1日/8月1日 或 02-01/08-01
+ match = item.match(/每半年(\d{1,2})月(\d{1,2})日\/(\d{1,2})月(\d{1,2})日[::]\s*(.+)$/);
+ if (match) {
+ return {
+ type: 'half_yearly',
+ dates: [
+ { month: parseInt(match[1]), day: parseInt(match[2]) },
+ { month: parseInt(match[3]), day: parseInt(match[4]) }
+ ],
+ content: match[5].trim()
+ };
+ }
+ match = item.match(/每半年(\d{1,2})-(\d{1,2})\/(\d{1,2})-(\d{1,2})[::]\s*(.+)$/);
+ if (match) {
+ return {
+ type: 'half_yearly',
+ dates: [
+ { month: parseInt(match[1]), day: parseInt(match[2]) },
+ { month: parseInt(match[3]), day: parseInt(match[4]) }
+ ],
+ content: match[5].trim()
+ };
+ }
+
+ // 每年(双日期,实际上是每半年)
+ match = item.match(/每年(\d{1,2})月(\d{1,2})日\/(\d{1,2})月(\d{1,2})日[::]\s*(.+)$/);
+ if (match) {
+ return {
+ type: 'half_yearly',
+ dates: [
+ { month: parseInt(match[1]), day: parseInt(match[2]) },
+ { month: parseInt(match[3]), day: parseInt(match[4]) }
+ ],
+ content: match[5].trim()
+ };
+ }
+
+ // 每月
+ match = item.match(/每月(\d{1,2})日?[::]\s*(.+)$/);
+ if (match) {
+ return { type: 'monthly', day: parseInt(match[1]), content: match[2].trim() };
+ }
+
+ // 每周
+ const dayMap = { '一': 1, '二': 2, '三': 3, '四': 4, '五': 5, '六': 6, '日': 7 };
+ match = item.match(/每周([一二三四五六日]|[1-7])[::]\s*(.+)$/);
+ if (match) {
+ const weekday = dayMap[match[1]] || parseInt(match[1]);
+ return { type: 'weekly', weekday, content: match[2].trim() };
+ }
+
+ log('WARNING', `无法解析周期性提醒: ${item}`);
+ return null;
+}
+
+function parseSingleItem(item) {
+ const match = item.match(/(\d{4})-(\d{1,2})-(\d{1,2})[::]\s*(.+)$/);
+ if (match) {
+ return {
+ type: 'single',
+ year: parseInt(match[1]),
+ month: parseInt(match[2]),
+ day: parseInt(match[3]),
+ content: match[4].trim()
+ };
+ }
+ log('WARNING', `无法解析单次提醒: ${item}`);
+ return null;
+}
+
+// 计算下次提醒时间
+function getNextOccurrence(reminder, today = new Date()) {
+ const results = [];
+
+ switch (reminder.type) {
+ case 'yearly':
+ results.push(nextYearly(reminder.month, reminder.day, today));
+ break;
+ case 'yearly_n':
+ results.push(nextYearlyN(reminder, today));
+ break;
+ case 'half_yearly':
+ for (const d of reminder.dates) {
+ results.push(nextYearly(d.month, d.day, today));
+ }
+ break;
+ case 'monthly':
+ results.push(nextMonthly(reminder.day, today));
+ break;
+ case 'weekly':
+ results.push(nextWeekly(reminder.weekday, today));
+ break;
+ case 'single':
+ results.push(new Date(reminder.year, reminder.month - 1, reminder.day));
+ break;
+ }
+
+ return results.filter(d => d !== null);
+}
+
+function nextYearly(month, day, today) {
+ try {
+ const date = new Date(today.getFullYear(), month - 1, day);
+ if (date < today) {
+ return new Date(today.getFullYear() + 1, month - 1, day);
+ }
+ return date;
+ } catch (e) {
+ log('ERROR', `无效日期: ${month}月${day}日`);
+ return null;
+ }
+}
+
+function nextYearlyN(reminder, today) {
+ if (reminder.nextYear) {
+ const date = new Date(reminder.nextYear, reminder.nextMonth - 1, reminder.nextDay);
+ if (date >= today) return date;
+ }
+
+ try {
+ const date = new Date(today.getFullYear(), reminder.month - 1, reminder.day);
+ if (date < today) {
+ return new Date(today.getFullYear() + reminder.interval, reminder.month - 1, reminder.day);
+ }
+ return date;
+ } catch (e) {
+ log('ERROR', `无效日期: ${reminder.month}月${reminder.day}日`);
+ return null;
+ }
+}
+
+function nextMonthly(day, today) {
+ const year = today.getFullYear();
+ const month = today.getMonth();
+
+ const lastDayOfMonth = new Date(year, month + 1, 0).getDate();
+ const actualDay = Math.min(day, lastDayOfMonth);
+
+ let date = new Date(year, month, actualDay);
+ if (date < today) {
+ const nextMonth = month === 11 ? 0 : month + 1;
+ const nextYear = month === 11 ? year + 1 : year;
+ const lastDayOfNextMonth = new Date(nextYear, nextMonth + 1, 0).getDate();
+ date = new Date(nextYear, nextMonth, Math.min(day, lastDayOfNextMonth));
+ }
+ return date;
+}
+
+function nextWeekly(weekday, today) {
+ // weekday: 1=周一, 7=周日
+ const jsWeekday = weekday - 1; // JS: 0=周一, 6=周日
+ const currentDay = today.getDay();
+ const currentJsDay = currentDay === 0 ? 6 : currentDay - 1;
+
+ let daysAhead = jsWeekday - currentJsDay;
+ if (daysAhead <= 0) daysAhead += 7;
+
+ const nextDate = new Date(today);
+ nextDate.setDate(today.getDate() + daysAhead);
+ return nextDate;
+}
+
+// CalDAV 操作
+class CalDAVClient {
+ constructor(url, user, password) {
+ this.url = url;
+ this.user = user;
+ this.password = password;
+ this.existingEvents = new Set(); // 存储已有事件的标识
+ }
+
+ async connect() {
+ try {
+ // 获取日历事件列表
+ const response = await this.request('REPORT', `
+
+
+
+
+
+
+
+
+
+
+`);
+
+ // 解析已有事件
+ this.parseExistingEvents(response);
+ log('INFO', `成功连接到 CalDAV 日历,已有 ${this.existingEvents.size} 个事件`);
+ return true;
+ } catch (e) {
+ log('ERROR', `连接 CalDAV 失败: ${e.message}`);
+ return false;
+ }
+ }
+
+ parseExistingEvents(xmlResponse) {
+ // 从 XML 响应中提取 VEVENT 并解析
+ const vevents = xmlResponse.match(/BEGIN:VEVENT[\s\S]*?END:VEVENT/gi) || [];
+
+ for (const vevent of vevents) {
+ // 提取 SUMMARY 和 DTSTART
+ const summaryMatch = vevent.match(/SUMMARY[^:]*:(.+)/i);
+ const dtstartMatch = vevent.match(/DTSTART[^:]*:([0-9T]+)/i);
+
+ if (summaryMatch && dtstartMatch) {
+ let summary = summaryMatch[1].trim();
+ // 移除可能的换行和特殊字符
+ summary = summary.replace(/\\n/g, ' ').replace(/\r?\n/g, ' ').trim();
+
+ const dateStr = dtstartMatch[1].slice(0, 8); // YYYYMMDD
+ // 用 summary + date 作为唯一标识
+ const key = `${summary}|${dateStr}`;
+ this.existingEvents.add(key);
+ }
+ }
+ }
+
+ async request(method, body) {
+ return new Promise((resolve, reject) => {
+ const url = new URL(this.url);
+
+ const options = {
+ hostname: url.hostname,
+ port: url.port || 443,
+ path: url.pathname,
+ method: method || 'GET',
+ headers: {
+ 'Authorization': 'Basic ' + Buffer.from(`${this.user}:${this.password}`).toString('base64'),
+ 'Content-Type': 'application/xml; charset=utf-8',
+ 'Depth': '1'
+ },
+ rejectUnauthorized: false // 禁用 SSL 验证
+ };
+
+ const req = https.request(options, (res) => {
+ let data = '';
+ res.on('data', chunk => data += chunk);
+ res.on('end', () => {
+ if (res.statusCode >= 200 && res.statusCode < 300) {
+ resolve(data);
+ } else {
+ reject(new Error(`HTTP ${res.statusCode}: ${data}`));
+ }
+ });
+ });
+
+ req.on('error', reject);
+ if (body) req.write(body);
+ req.end();
+ });
+ }
+
+ eventExists(content, reminderTime) {
+ // 使用本地时间格式化日期,避免时区问题
+ const year = reminderTime.getFullYear();
+ const month = String(reminderTime.getMonth() + 1).padStart(2, '0');
+ const day = String(reminderTime.getDate()).padStart(2, '0');
+ const dateStr = `${year}${month}${day}`; // YYYYMMDD
+
+ const key = `${content}|${dateStr}`;
+ return this.existingEvents.has(key);
+ }
+
+ async createEvent(reminderTime, content, alarmMinutes = 0) {
+ try {
+ const vcalendar = new ICAL.Component(['vcalendar', [], []]);
+ vcalendar.addPropertyWithValue('prodid', '-//Calendar Reminder//EN');
+ vcalendar.addPropertyWithValue('version', '2.0');
+
+ const vevent = new ICAL.Component('vevent');
+ const uid = `${Date.now()}-${Math.random().toString(36).slice(2)}@calendar-reminder`;
+
+ vevent.addPropertyWithValue('uid', uid);
+ vevent.addPropertyWithValue('dtstart', ICAL.Time.fromJSDate(reminderTime, false));
+ vevent.addPropertyWithValue('dtend', ICAL.Time.fromJSDate(new Date(reminderTime.getTime() + 3600000), false));
+ vevent.addPropertyWithValue('summary', content);
+ vevent.addPropertyWithValue('dtstamp', ICAL.Time.now());
+
+ // 添加提醒
+ const valarm = new ICAL.Component('valarm');
+ valarm.addPropertyWithValue('action', 'DISPLAY');
+ valarm.addPropertyWithValue('description', content);
+ valarm.addPropertyWithValue('trigger', ICAL.Duration.fromSeconds(-alarmMinutes * 60));
+ vevent.addSubcomponent(valarm);
+
+ vcalendar.addSubcomponent(vevent);
+
+ const icalData = vcalendar.toString();
+
+ // 使用 PUT 创建事件
+ const eventUrl = new URL(this.url);
+ eventUrl.pathname = eventUrl.pathname.replace(/\/$/, '') + '/' + uid + '.ics';
+
+ await this.putEvent(eventUrl.toString(), icalData);
+ log('INFO', `创建事件: ${content} @ ${reminderTime.toISOString().slice(0, 10)}`);
+ return true;
+ } catch (e) {
+ log('ERROR', `创建事件失败: ${e.message}`);
+ return false;
+ }
+ }
+
+ async putEvent(url, icalData) {
+ return new Promise((resolve, reject) => {
+ const urlObj = new URL(url);
+
+ const options = {
+ hostname: urlObj.hostname,
+ port: urlObj.port || 443,
+ path: urlObj.pathname,
+ method: 'PUT',
+ headers: {
+ 'Authorization': 'Basic ' + Buffer.from(`${this.user}:${this.password}`).toString('base64'),
+ 'Content-Type': 'text/calendar; charset=utf-8',
+ 'Content-Length': Buffer.byteLength(icalData)
+ },
+ rejectUnauthorized: false
+ };
+
+ const req = https.request(options, (res) => {
+ let data = '';
+ res.on('data', chunk => data += chunk);
+ res.on('end', () => {
+ if (res.statusCode >= 200 && res.statusCode < 300) {
+ resolve(data);
+ } else {
+ reject(new Error(`HTTP ${res.statusCode}: ${data}`));
+ }
+ });
+ });
+
+ req.on('error', reject);
+ req.write(icalData);
+ req.end();
+ });
+ }
+}
+
+// NAS 同步
+function syncFromNAS(source, target, exclude) {
+ try {
+ const cmd = `rclone sync "${source}" "${target.replace('~', process.env.HOME)}" --exclude "${exclude}"`;
+ log('INFO', `从 NAS 同步: ${cmd}`);
+ execSync(cmd, { stdio: 'pipe', timeout: 300000 });
+ log('INFO', '从 NAS 同步成功');
+ return true;
+ } catch (e) {
+ log('ERROR', `从 NAS 同步失败: ${e.message}`);
+ return false;
+ }
+}
+
+function syncToNAS(source, target, exclude) {
+ try {
+ const cmd = `rclone sync "${source.replace('~', process.env.HOME)}" "${target}" --exclude "${exclude}"`;
+ log('INFO', `同步到 NAS: ${cmd}`);
+ execSync(cmd, { stdio: 'pipe', timeout: 300000 });
+ log('INFO', '同步到 NAS 成功');
+ return true;
+ } catch (e) {
+ log('ERROR', `同步到 NAS 失败: ${e.message}`);
+ return false;
+ }
+}
+
+// 主函数
+async function main() {
+ log('INFO', '============================================================');
+ log('INFO', '日历提醒系统启动 (Node.js)');
+ log('INFO', '============================================================');
+
+ const config = loadConfig();
+ const today = new Date();
+ today.setHours(0, 0, 0, 0);
+
+ // 1. 从 NAS 同步
+ if (config.rclone_source) {
+ syncFromNAS(config.rclone_source, config.rclone_target, config.rclone_exclude);
+ }
+
+ // 2. 解析提醒文件
+ const reminders = parseReminderFile(config.reminder_file);
+ log('INFO', `解析到 ${reminders.periodic.length} 个周期性提醒`);
+ log('INFO', `解析到 ${reminders.single.length} 个单次提醒`);
+
+ // 3. 连接 CalDAV
+ const caldav = new CalDAVClient(config.caldav_url, config.caldav_user, config.caldav_password);
+ if (!await caldav.connect()) {
+ log('ERROR', '无法连接到 CalDAV,请检查配置');
+ return;
+ }
+
+ const advanceDays = config.advance_days || [7, 3, 0];
+
+ // 4. 处理周期性提醒
+ for (const reminder of reminders.periodic) {
+ const nextTimes = getNextOccurrence(reminder, today);
+
+ for (const nextTime of nextTimes) {
+ if (!nextTime) continue;
+
+ const content = reminder.content;
+
+ for (const days of advanceDays) {
+ const reminderTime = new Date(nextTime);
+ reminderTime.setDate(reminderTime.getDate() - days);
+
+ if (reminderTime >= today) {
+ const eventTitle = `[提前${days}天] ${content}`;
+ if (!caldav.eventExists(eventTitle, reminderTime)) {
+ await caldav.createEvent(reminderTime, eventTitle, 0);
+ }
+ }
+ }
+ }
+ }
+
+ // 5. 处理单次提醒
+ for (const reminder of reminders.single) {
+ const reminderDate = new Date(reminder.year, reminder.month - 1, reminder.day);
+ const content = reminder.content;
+
+ for (const days of advanceDays) {
+ const reminderTime = new Date(reminderDate);
+ reminderTime.setDate(reminderTime.getDate() - days);
+
+ if (reminderTime >= today) {
+ const eventTitle = `[提前${days}天] ${content}`;
+ if (!caldav.eventExists(eventTitle, reminderTime)) {
+ await caldav.createEvent(reminderTime, eventTitle, 0);
+ }
+ }
+ }
+ }
+
+ // 6. 同步回 NAS
+ if (config.rclone_source) {
+ syncToNAS(config.rclone_target, config.rclone_source, config.rclone_exclude);
+ }
+
+ log('INFO', '日历提醒系统完成');
+}
+
+main().catch(e => {
+ log('ERROR', `程序异常: ${e.message}`);
+ process.exit(1);
+});
diff --git a/cron/scripts/clear-calendar.mjs b/cron/scripts/clear-calendar.mjs
new file mode 100644
index 0000000..9008763
--- /dev/null
+++ b/cron/scripts/clear-calendar.mjs
@@ -0,0 +1,138 @@
+#!/usr/bin/env node
+/**
+ * 清空 CalDAV 日历事件
+ */
+
+import https from 'https';
+
+const CONFIG = {
+ url: 'https://micecola.top:5001/caldav/zhangqing/home/',
+ user: 'zhangqing',
+ password: 'MiceCola1265'
+};
+
+function log(level, message) {
+ const timestamp = new Date().toISOString().replace('T', ' ').slice(0, 19);
+ console.log(`${timestamp} - ${level} - ${message}`);
+}
+
+async function request(method, path = '', body = null) {
+ return new Promise((resolve, reject) => {
+ const url = new URL(CONFIG.url + path);
+
+ const options = {
+ hostname: url.hostname,
+ port: url.port || 443,
+ path: url.pathname,
+ method: method,
+ headers: {
+ 'Authorization': 'Basic ' + Buffer.from(`${CONFIG.user}:${CONFIG.password}`).toString('base64'),
+ 'Content-Type': 'application/xml; charset=utf-8',
+ 'Depth': 'infinity'
+ },
+ rejectUnauthorized: false
+ };
+
+ const req = https.request(options, (res) => {
+ let data = '';
+ res.on('data', chunk => data += chunk);
+ res.on('end', () => {
+ if (res.statusCode >= 200 && res.statusCode < 300) {
+ resolve({ status: res.statusCode, data, headers: res.headers });
+ } else {
+ resolve({ status: res.statusCode, data, headers: res.headers });
+ }
+ });
+ });
+
+ req.on('error', reject);
+ if (body) req.write(body);
+ req.end();
+ });
+}
+
+async function getAllEvents() {
+ // 使用 PROPFIND 获取日历内容
+ const response = await request('PROPFIND', '', `
+
+
+
+
+
+`);
+
+ // 提取所有 .ics 文件的 href(可能在 或 中)
+ const hrefs = [];
+
+ // 匹配所有 href,排除目录
+ const allHrefs = response.data.match(/([^<]+)<\/href>/gi) || [];
+ for (const match of allHrefs) {
+ const href = match.replace(/<\/?href>/gi, '');
+ // 只保留 .ics 文件,排除目录
+ if (href.endsWith('.ics')) {
+ // 解码 URL 编码
+ const decodedHref = decodeURIComponent(href);
+ if (!hrefs.includes(decodedHref)) {
+ hrefs.push(decodedHref);
+ }
+ }
+ }
+
+ return hrefs;
+}
+
+async function deleteEvent(href) {
+ return new Promise((resolve, reject) => {
+ // href 是相对路径,需要用完整 URL
+ const baseUrl = new URL(CONFIG.url);
+ const fullPath = href.startsWith('/') ? href : '/' + href;
+
+ const options = {
+ hostname: baseUrl.hostname,
+ port: baseUrl.port || 443,
+ path: fullPath,
+ method: 'DELETE',
+ headers: {
+ 'Authorization': 'Basic ' + Buffer.from(`${CONFIG.user}:${CONFIG.password}`).toString('base64')
+ },
+ rejectUnauthorized: false
+ };
+
+ const req = https.request(options, (res) => {
+ let data = '';
+ res.on('data', chunk => data += chunk);
+ res.on('end', () => resolve(res.statusCode));
+ });
+
+ req.on('error', reject);
+ req.end();
+ });
+}
+
+async function main() {
+ log('INFO', '获取日历事件列表...');
+
+ const hrefs = await getAllEvents();
+ log('INFO', `找到 ${hrefs.length} 个事件`);
+
+ let deleted = 0;
+ let failed = 0;
+
+ for (const href of hrefs) {
+ const status = await deleteEvent(href);
+ if (status >= 200 && status < 300) {
+ deleted++;
+ log('INFO', `已删除: ${href.split('/').pop()}`);
+ } else {
+ failed++;
+ log('ERROR', `删除失败 (${status}): ${href}`);
+ }
+ }
+
+ log('INFO', `完成: 删除 ${deleted} 个,失败 ${failed} 个`);
+}
+
+main().catch(e => {
+ log('ERROR', `程序异常: ${e.message}`);
+ process.exit(1);
+});
diff --git a/cron/scripts/node_modules/.package-lock.json b/cron/scripts/node_modules/.package-lock.json
new file mode 100644
index 0000000..a3c3129
--- /dev/null
+++ b/cron/scripts/node_modules/.package-lock.json
@@ -0,0 +1,14 @@
+{
+ "name": "scripts",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "node_modules/ical.js": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ical.js/-/ical.js-2.2.1.tgz",
+ "integrity": "sha512-yK/UlPbEs316igb/tjRgbFA8ZV75rCsBJp/hWOatpyaPNlgw0dGDmU+FoicOcwX4xXkeXOkYiOmCqNPFpNPkQg==",
+ "license": "MPL-2.0"
+ }
+ }
+}
diff --git a/cron/scripts/node_modules/ical.js/LICENSE b/cron/scripts/node_modules/ical.js/LICENSE
new file mode 100644
index 0000000..a612ad9
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/LICENSE
@@ -0,0 +1,373 @@
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+ means each individual or legal entity that creates, contributes to
+ the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+ means the combination of the Contributions of others (if any) used
+ by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+ means Source Code Form to which the initial Contributor has attached
+ the notice in Exhibit A, the Executable Form of such Source Code
+ Form, and Modifications of such Source Code Form, in each case
+ including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ (a) that the initial Contributor has attached the notice described
+ in Exhibit B to the Covered Software; or
+
+ (b) that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the
+ terms of a Secondary License.
+
+1.6. "Executable Form"
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+ means a work that combines Covered Software with other material, in
+ a separate file or files, that is not Covered Software.
+
+1.8. "License"
+ means this document.
+
+1.9. "Licensable"
+ means having the right to grant, to the maximum extent possible,
+ whether at the time of the initial grant or subsequently, any and
+ all of the rights conveyed by this License.
+
+1.10. "Modifications"
+ means any of the following:
+
+ (a) any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered
+ Software; or
+
+ (b) any new file in Source Code Form that contains any Covered
+ Software.
+
+1.11. "Patent Claims" of a Contributor
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the
+ License, by the making, using, selling, offering for sale, having
+ made, import, or transfer of either its Contributions or its
+ Contributor Version.
+
+1.12. "Secondary License"
+ means either the GNU General Public License, Version 2.0, the GNU
+ Lesser General Public License, Version 2.1, the GNU Affero General
+ Public License, Version 3.0, or any later versions of those
+ licenses.
+
+1.13. "Source Code Form"
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that
+ controls, is controlled by, or is under common control with You. For
+ purposes of this definition, "control" means (a) the power, direct
+ or indirect, to cause the direction or management of such entity,
+ whether by contract or otherwise, or (b) ownership of more than
+ fifty percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+ for sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+ or
+
+(b) for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+ Form, as described in Section 3.1, and You must inform recipients of
+ the Executable Form how they can obtain a copy of such Source Code
+ Form by reasonable means in a timely manner, at a charge no more
+ than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter
+ the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+* *
+* 6. Disclaimer of Warranty *
+* ------------------------- *
+* *
+* Covered Software is provided under this License on an "as is" *
+* basis, without warranty of any kind, either expressed, implied, or *
+* statutory, including, without limitation, warranties that the *
+* Covered Software is free of defects, merchantable, fit for a *
+* particular purpose or non-infringing. The entire risk as to the *
+* quality and performance of the Covered Software is with You. *
+* Should any Covered Software prove defective in any respect, You *
+* (not any Contributor) assume the cost of any necessary servicing, *
+* repair, or correction. This disclaimer of warranty constitutes an *
+* essential part of this License. No use of any Covered Software is *
+* authorized under this License except under this disclaimer. *
+* *
+************************************************************************
+
+************************************************************************
+* *
+* 7. Limitation of Liability *
+* -------------------------- *
+* *
+* Under no circumstances and under no legal theory, whether tort *
+* (including negligence), contract, or otherwise, shall any *
+* Contributor, or anyone who distributes Covered Software as *
+* permitted above, be liable to You for any direct, indirect, *
+* special, incidental, or consequential damages of any character *
+* including, without limitation, damages for lost profits, loss of *
+* goodwill, work stoppage, computer failure or malfunction, or any *
+* and all other commercial damages or losses, even if such party *
+* shall have been informed of the possibility of such damages. This *
+* limitation of liability shall not apply to liability for death or *
+* personal injury resulting from such party's negligence to the *
+* extent applicable law prohibits such limitation. Some *
+* jurisdictions do not allow the exclusion or limitation of *
+* incidental or consequential damages, so this exclusion and *
+* limitation may not apply to You. *
+* *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+ 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/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+ This Source Code Form is "Incompatible With Secondary Licenses", as
+ defined by the Mozilla Public License, v. 2.0.
diff --git a/cron/scripts/node_modules/ical.js/README.md b/cron/scripts/node_modules/ical.js/README.md
new file mode 100644
index 0000000..600a640
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/README.md
@@ -0,0 +1,120 @@
+# ical.js - Javascript parser for iCalendar, jCal, vCard, jCard.
+
+This is a library to parse the formats defined in the following rfcs and their extensions:
+* [rfc 5545](http://tools.ietf.org/html/rfc5545) (iCalendar)
+* [rfc7265](http://tools.ietf.org/html/rfc7265) (jCal)
+* [rfc6350](http://tools.ietf.org/html/rfc6350) (vCard)
+* [rfc7095](http://tools.ietf.org/html/rfc7095) (jCard)
+
+The initial goal was to use it as a replacement for libical in the [Mozilla Calendar
+Project](http://www.mozilla.org/projects/calendar/), but the library has been written with the web
+in mind. This library enables you to do all sorts of cool experiments with calendar data and the
+web. Most algorithms here were taken from [libical](https://github.com/libical/libical). If you are
+bugfixing this library, please check if the fix can be upstreamed to libical.
+
+ [](https://coveralls.io/r/kewisch/ical.js) [](http://badge.fury.io/js/ical.js) [](https://cdnjs.com/libraries/ical.js)
+
+## Sandbox and Validator
+
+If you want to try out ICAL.js right now, there is a
+[jsfiddle](http://jsfiddle.net/kewisch/227efboL/) set up and ready to use.
+
+The ICAL validator demonstrates how to use the library in a webpage, and helps verify iCalendar and
+jCal. [Try the validator online](http://kewisch.github.io/ical.js/validator.html)
+
+The recurrence tester calculates occurrences based on a RRULE. It can be used to aid in
+creating test cases for the recurrence iterator.
+[Try the recurrence tester online](https://kewisch.github.io/ical.js/recur-tester.html).
+
+## Installing
+
+ICAL.js has no dependencies and is written in modern JavaScript. You can install ICAL.js via
+[npm](https://www.npmjs.com/), if you would like to use it in Node.js:
+```bash
+npm install ical.js
+```
+Then simply import it for use:
+```javascript
+import ICAL from "ical.js";
+```
+
+If you are working with a browser, be aware this is an ES6 module:
+
+```html
+
+```
+
+If you need to make use of a script tag, you can use the transpiled ES5 version:
+```html
+
+
+
+```
+
+The browser examples above use the minified versions of the library, which is probably what you want.
+However, there are also unminified versions of ICAL.js available on unpkg.
+
+- Unminified ES6 module: `https://unpkg.com/ical.js/dist/ical.js`
+- Unminified ES5 version: `https://unpkg.com/ical.js/dist/ical.es5.cjs`
+
+## Timezones
+The stock ical.js does not register any timezones, due to the additional size it brings. If you'd
+like to do timezone conversion, and the timezone definitions are not included in the respective ics
+files, you'll need to use `ical.timezones.js` or its minified counterpart.
+
+This file is not included in the distribution since it pulls in IANA timezones that might change
+regularly. See the github actions on building your own timezones during CI, or grab a recent build
+from main.
+
+## Documentation
+
+For a few guides with code samples, please check out
+[the wiki](https://github.com/kewisch/ical.js/wiki). If you prefer,
+full API documentation [is available here](http://kewisch.github.io/ical.js/api/).
+If you are missing anything, please don't hesitate to create an issue.
+
+## Developing
+
+To contribute to ICAL.js you need to set up the development environment. A simple `npm install` will
+get you set up. If you would like to help out and would like to discuss any API changes, please feel
+free to create an issue.
+
+### Tests
+
+The following test suites are available
+
+ npm run test-unit # Node unit tests
+ npm run test-acceptance # Node acceptance tests
+ npm run test-performance # Performance comparison tests
+ npm run test-browser # Browser unit and acceptance tests
+
+ npm run test # Node unit and acceptance tests (This is fast and covers most aspects)
+ npm run test-all # All of the above
+
+See [the wiki](https://github.com/kewisch/ical.js/wiki/Running-Tests) for more details.
+
+Code coverage is automatically generated for the node unit tests. You can [view the coverage
+results](https://coveralls.io/r/kewisch/ical.js) online, or run them locally to make sure new
+code is covered.
+
+### Linters
+To make sure all ICAL.js code uses a common style, please run the linters using `npm run lint`.
+Please make sure you fix any issues shown by this command before sending a pull request.
+
+### Documentation
+You can generate the documentation locally, this is also helpful to ensure the jsdoc you have
+written is valid. To do so, run `npm run jsdoc`. You will find the output in the `docs/api/`
+subdirectory.
+
+### Packaging
+When you are done with your work, you can run `npm run build` to create the single-file build for
+use in the browser, including its minified counterpart and the source map.
+
+## License
+ical.js is licensed under the
+[Mozilla Public License](https://www.mozilla.org/MPL/2.0/), version 2.0.
diff --git a/cron/scripts/node_modules/ical.js/dist/ical.es5.cjs b/cron/scripts/node_modules/ical.js/dist/ical.es5.cjs
new file mode 100644
index 0000000..9179303
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/ical.es5.cjs
@@ -0,0 +1,9717 @@
+/* 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 */
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+ typeof define === 'function' && define.amd ? define(factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.ICAL = factory());
+})(this, (function () { 'use strict';
+
+ function _arrayLikeToArray(r, a) {
+ (null == a || a > r.length) && (a = r.length);
+ for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
+ return n;
+ }
+ function _arrayWithHoles(r) {
+ if (Array.isArray(r)) return r;
+ }
+ function _arrayWithoutHoles(r) {
+ if (Array.isArray(r)) return _arrayLikeToArray(r);
+ }
+ function _assertClassBrand(e, t, n) {
+ if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n;
+ throw new TypeError("Private element is not present on this object");
+ }
+ function _assertThisInitialized(e) {
+ if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
+ return e;
+ }
+ function _callSuper(t, o, e) {
+ return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e));
+ }
+ function _classCallCheck(a, n) {
+ if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function");
+ }
+ function _construct(t, e, r) {
+ if (_isNativeReflectConstruct()) return Reflect.construct.apply(null, arguments);
+ var o = [null];
+ o.push.apply(o, e);
+ var p = new (t.bind.apply(t, o))();
+ return r && _setPrototypeOf(p, r.prototype), p;
+ }
+ function _defineProperties(e, r) {
+ for (var t = 0; t < r.length; t++) {
+ var o = r[t];
+ o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o);
+ }
+ }
+ function _createClass(e, r, t) {
+ return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", {
+ writable: !1
+ }), e;
+ }
+ function _createForOfIteratorHelper(r, e) {
+ var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
+ if (!t) {
+ if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e) {
+ t && (r = t);
+ var n = 0,
+ F = function () {};
+ return {
+ s: F,
+ n: function () {
+ return n >= r.length ? {
+ done: !0
+ } : {
+ done: !1,
+ value: r[n++]
+ };
+ },
+ e: function (r) {
+ throw r;
+ },
+ f: F
+ };
+ }
+ throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
+ }
+ var o,
+ a = !0,
+ u = !1;
+ return {
+ s: function () {
+ t = t.call(r);
+ },
+ n: function () {
+ var r = t.next();
+ return a = r.done, r;
+ },
+ e: function (r) {
+ u = !0, o = r;
+ },
+ f: function () {
+ try {
+ a || null == t.return || t.return();
+ } finally {
+ if (u) throw o;
+ }
+ }
+ };
+ }
+ function _defineProperty(e, r, t) {
+ return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
+ value: t,
+ enumerable: !0,
+ configurable: !0,
+ writable: !0
+ }) : e[r] = t, e;
+ }
+ function _getPrototypeOf(t) {
+ return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) {
+ return t.__proto__ || Object.getPrototypeOf(t);
+ }, _getPrototypeOf(t);
+ }
+ function _inherits(t, e) {
+ if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function");
+ t.prototype = Object.create(e && e.prototype, {
+ constructor: {
+ value: t,
+ writable: !0,
+ configurable: !0
+ }
+ }), Object.defineProperty(t, "prototype", {
+ writable: !1
+ }), e && _setPrototypeOf(t, e);
+ }
+ function _isNativeFunction(t) {
+ try {
+ return -1 !== Function.toString.call(t).indexOf("[native code]");
+ } catch (n) {
+ return "function" == typeof t;
+ }
+ }
+ function _isNativeReflectConstruct() {
+ try {
+ var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
+ } catch (t) {}
+ return (_isNativeReflectConstruct = function () {
+ return !!t;
+ })();
+ }
+ function _iterableToArray(r) {
+ if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r);
+ }
+ function _iterableToArrayLimit(r, l) {
+ var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
+ if (null != t) {
+ var e,
+ n,
+ i,
+ u,
+ a = [],
+ f = !0,
+ o = !1;
+ try {
+ if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
+ } catch (r) {
+ o = !0, n = r;
+ } finally {
+ try {
+ if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
+ } finally {
+ if (o) throw n;
+ }
+ }
+ return a;
+ }
+ }
+ function _nonIterableRest() {
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
+ }
+ function _nonIterableSpread() {
+ throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
+ }
+ function _possibleConstructorReturn(t, e) {
+ if (e && ("object" == typeof e || "function" == typeof e)) return e;
+ if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined");
+ return _assertThisInitialized(t);
+ }
+ function _setPrototypeOf(t, e) {
+ return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) {
+ return t.__proto__ = e, t;
+ }, _setPrototypeOf(t, e);
+ }
+ function _slicedToArray(r, e) {
+ return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
+ }
+ function _toConsumableArray(r) {
+ return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread();
+ }
+ function _toPrimitive(t, r) {
+ if ("object" != typeof t || !t) return t;
+ var e = t[Symbol.toPrimitive];
+ if (void 0 !== e) {
+ var i = e.call(t, r || "default");
+ if ("object" != typeof i) return i;
+ throw new TypeError("@@toPrimitive must return a primitive value.");
+ }
+ return ("string" === r ? String : Number)(t);
+ }
+ function _toPropertyKey(t) {
+ var i = _toPrimitive(t, "string");
+ return "symbol" == typeof i ? i : i + "";
+ }
+ function _typeof(o) {
+ "@babel/helpers - typeof";
+
+ return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
+ return typeof o;
+ } : function (o) {
+ return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
+ }, _typeof(o);
+ }
+ function _unsupportedIterableToArray(r, a) {
+ if (r) {
+ if ("string" == typeof r) return _arrayLikeToArray(r, a);
+ var t = {}.toString.call(r).slice(8, -1);
+ return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
+ }
+ }
+ function _wrapNativeSuper(t) {
+ var r = "function" == typeof Map ? new Map() : void 0;
+ return _wrapNativeSuper = function (t) {
+ if (null === t || !_isNativeFunction(t)) return t;
+ if ("function" != typeof t) throw new TypeError("Super expression must either be null or a function");
+ if (void 0 !== r) {
+ if (r.has(t)) return r.get(t);
+ r.set(t, Wrapper);
+ }
+ function Wrapper() {
+ return _construct(t, arguments, _getPrototypeOf(this).constructor);
+ }
+ return Wrapper.prototype = Object.create(t.prototype, {
+ constructor: {
+ value: Wrapper,
+ enumerable: !1,
+ writable: !0,
+ configurable: !0
+ }
+ }), _setPrototypeOf(Wrapper, t);
+ }, _wrapNativeSuper(t);
+ }
+
+ /* 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 */
+ /**
+ * Represents the BINARY value type, which contains extra methods for encoding and decoding.
+ *
+ * @memberof ICAL
+ */
+ var Binary = /*#__PURE__*/function () {
+ /**
+ * Creates a new ICAL.Binary instance
+ *
+ * @param {String} aValue The binary data for this value
+ */
+ function Binary(aValue) {
+ _classCallCheck(this, Binary);
+ /**
+ * The type name, to be used in the jCal object.
+ * @default "binary"
+ * @constant
+ */
+ _defineProperty(this, "icaltype", "binary");
+ this.value = aValue;
+ }
+ return _createClass(Binary, [{
+ key: "decodeValue",
+ value:
+ /**
+ * Base64 decode the current value
+ *
+ * @return {String} The base64-decoded value
+ */
+ function decodeValue() {
+ return this._b64_decode(this.value);
+ }
+
+ /**
+ * Encodes the passed parameter with base64 and sets the internal
+ * value to the result.
+ *
+ * @param {String} aValue The raw binary value to encode
+ */
+ }, {
+ key: "setEncodedValue",
+ value: function setEncodedValue(aValue) {
+ this.value = this._b64_encode(aValue);
+ }
+ }, {
+ key: "_b64_encode",
+ value: function _b64_encode(data) {
+ // http://kevin.vanzonneveld.net
+ // + original by: Tyler Akins (http://rumkin.com)
+ // + improved by: Bayron Guevara
+ // + improved by: Thunder.m
+ // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ // + bugfixed by: Pellentesque Malesuada
+ // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ // + improved by: Rafał Kukawski (http://kukawski.pl)
+ // * example 1: base64_encode('Kevin van Zonneveld');
+ // * returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA=='
+ // mozilla has this native
+ // - but breaks in 2.0.0.12!
+ //if (typeof this.window['atob'] == 'function') {
+ // return atob(data);
+ //}
+ var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz0123456789+/=";
+ var o1,
+ o2,
+ o3,
+ h1,
+ h2,
+ h3,
+ h4,
+ bits,
+ i = 0,
+ ac = 0,
+ enc = "",
+ tmp_arr = [];
+ if (!data) {
+ return data;
+ }
+ do {
+ // pack three octets into four hexets
+ o1 = data.charCodeAt(i++);
+ o2 = data.charCodeAt(i++);
+ o3 = data.charCodeAt(i++);
+ bits = o1 << 16 | o2 << 8 | o3;
+ h1 = bits >> 18 & 0x3f;
+ h2 = bits >> 12 & 0x3f;
+ h3 = bits >> 6 & 0x3f;
+ h4 = bits & 0x3f;
+
+ // use hexets to index into b64, and append result to encoded string
+ tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
+ } while (i < data.length);
+ enc = tmp_arr.join('');
+ var r = data.length % 3;
+ return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3);
+ }
+ }, {
+ key: "_b64_decode",
+ value: function _b64_decode(data) {
+ // http://kevin.vanzonneveld.net
+ // + original by: Tyler Akins (http://rumkin.com)
+ // + improved by: Thunder.m
+ // + input by: Aman Gupta
+ // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ // + bugfixed by: Onno Marsman
+ // + bugfixed by: Pellentesque Malesuada
+ // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ // + input by: Brett Zamir (http://brett-zamir.me)
+ // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ // * example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA==');
+ // * returns 1: 'Kevin van Zonneveld'
+ // mozilla has this native
+ // - but breaks in 2.0.0.12!
+ //if (typeof this.window['btoa'] == 'function') {
+ // return btoa(data);
+ //}
+ var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz0123456789+/=";
+ var o1,
+ o2,
+ o3,
+ h1,
+ h2,
+ h3,
+ h4,
+ bits,
+ i = 0,
+ ac = 0,
+ dec = "",
+ tmp_arr = [];
+ if (!data) {
+ return data;
+ }
+ data += '';
+ do {
+ // unpack four hexets into three octets using index points in b64
+ h1 = b64.indexOf(data.charAt(i++));
+ h2 = b64.indexOf(data.charAt(i++));
+ h3 = b64.indexOf(data.charAt(i++));
+ h4 = b64.indexOf(data.charAt(i++));
+ bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
+ o1 = bits >> 16 & 0xff;
+ o2 = bits >> 8 & 0xff;
+ o3 = bits & 0xff;
+ if (h3 == 64) {
+ tmp_arr[ac++] = String.fromCharCode(o1);
+ } else if (h4 == 64) {
+ tmp_arr[ac++] = String.fromCharCode(o1, o2);
+ } else {
+ tmp_arr[ac++] = String.fromCharCode(o1, o2, o3);
+ }
+ } while (i < data.length);
+ dec = tmp_arr.join('');
+ return dec;
+ }
+
+ /**
+ * The string representation of this value
+ * @return {String}
+ */
+ }, {
+ key: "toString",
+ value: function toString() {
+ return this.value;
+ }
+ }], [{
+ key: "fromString",
+ value:
+ /**
+ * Creates a binary value from the given string.
+ *
+ * @param {String} aString The binary value string
+ * @return {Binary} The binary value instance
+ */
+ function fromString(aString) {
+ return new Binary(aString);
+ }
+ }]);
+ }();
+
+ var DURATION_LETTERS = /([PDWHMTS]{1,1})/;
+ var DATA_PROPS_TO_COPY = ["weeks", "days", "hours", "minutes", "seconds", "isNegative"];
+
+ /**
+ * This class represents the "duration" value type, with various calculation
+ * and manipulation methods.
+ *
+ * @memberof ICAL
+ */
+ var Duration = /*#__PURE__*/function () {
+ /**
+ * Creates a new ICAL.Duration instance.
+ *
+ * @param {Object} data An object with members of the duration
+ * @param {Number=} data.weeks Duration in weeks
+ * @param {Number=} data.days Duration in days
+ * @param {Number=} data.hours Duration in hours
+ * @param {Number=} data.minutes Duration in minutes
+ * @param {Number=} data.seconds Duration in seconds
+ * @param {Boolean=} data.isNegative If true, the duration is negative
+ */
+ function Duration(data) {
+ _classCallCheck(this, Duration);
+ /**
+ * The weeks in this duration
+ * @type {Number}
+ * @default 0
+ */
+ _defineProperty(this, "weeks", 0);
+ /**
+ * The days in this duration
+ * @type {Number}
+ * @default 0
+ */
+ _defineProperty(this, "days", 0);
+ /**
+ * The days in this duration
+ * @type {Number}
+ * @default 0
+ */
+ _defineProperty(this, "hours", 0);
+ /**
+ * The minutes in this duration
+ * @type {Number}
+ * @default 0
+ */
+ _defineProperty(this, "minutes", 0);
+ /**
+ * The seconds in this duration
+ * @type {Number}
+ * @default 0
+ */
+ _defineProperty(this, "seconds", 0);
+ /**
+ * The seconds in this duration
+ * @type {Boolean}
+ * @default false
+ */
+ _defineProperty(this, "isNegative", false);
+ /**
+ * The class identifier.
+ * @constant
+ * @type {String}
+ * @default "icalduration"
+ */
+ _defineProperty(this, "icalclass", "icalduration");
+ /**
+ * The type name, to be used in the jCal object.
+ * @constant
+ * @type {String}
+ * @default "duration"
+ */
+ _defineProperty(this, "icaltype", "duration");
+ this.wrappedJSObject = this;
+ this.fromData(data);
+ }
+ return _createClass(Duration, [{
+ key: "clone",
+ value:
+ /**
+ * Returns a clone of the duration object.
+ *
+ * @return {Duration} The cloned object
+ */
+ function clone() {
+ return Duration.fromData(this);
+ }
+
+ /**
+ * The duration value expressed as a number of seconds.
+ *
+ * @return {Number} The duration value in seconds
+ */
+ }, {
+ key: "toSeconds",
+ value: function toSeconds() {
+ var seconds = this.seconds + 60 * this.minutes + 3600 * this.hours + 86400 * this.days + 7 * 86400 * this.weeks;
+ return this.isNegative ? -seconds : seconds;
+ }
+
+ /**
+ * Reads the passed seconds value into this duration object. Afterwards,
+ * members like {@link ICAL.Duration#days days} and {@link ICAL.Duration#weeks weeks} will be set up
+ * accordingly.
+ *
+ * @param {Number} aSeconds The duration value in seconds
+ * @return {Duration} Returns this instance
+ */
+ }, {
+ key: "fromSeconds",
+ value: function fromSeconds(aSeconds) {
+ var secs = Math.abs(aSeconds);
+ this.isNegative = aSeconds < 0;
+ this.days = trunc(secs / 86400);
+
+ // If we have a flat number of weeks, use them.
+ if (this.days % 7 == 0) {
+ this.weeks = this.days / 7;
+ this.days = 0;
+ } else {
+ this.weeks = 0;
+ }
+ secs -= (this.days + 7 * this.weeks) * 86400;
+ this.hours = trunc(secs / 3600);
+ secs -= this.hours * 3600;
+ this.minutes = trunc(secs / 60);
+ secs -= this.minutes * 60;
+ this.seconds = secs;
+ return this;
+ }
+
+ /**
+ * Sets up the current instance using members from the passed data object.
+ *
+ * @param {Object} aData An object with members of the duration
+ * @param {Number=} aData.weeks Duration in weeks
+ * @param {Number=} aData.days Duration in days
+ * @param {Number=} aData.hours Duration in hours
+ * @param {Number=} aData.minutes Duration in minutes
+ * @param {Number=} aData.seconds Duration in seconds
+ * @param {Boolean=} aData.isNegative If true, the duration is negative
+ */
+ }, {
+ key: "fromData",
+ value: function fromData(aData) {
+ for (var _i = 0, _DATA_PROPS_TO_COPY = DATA_PROPS_TO_COPY; _i < _DATA_PROPS_TO_COPY.length; _i++) {
+ var prop = _DATA_PROPS_TO_COPY[_i];
+ if (aData && prop in aData) {
+ this[prop] = aData[prop];
+ } else {
+ this[prop] = 0;
+ }
+ }
+ }
+
+ /**
+ * Resets the duration instance to the default values, i.e. PT0S
+ */
+ }, {
+ key: "reset",
+ value: function reset() {
+ this.isNegative = false;
+ this.weeks = 0;
+ this.days = 0;
+ this.hours = 0;
+ this.minutes = 0;
+ this.seconds = 0;
+ }
+
+ /**
+ * Compares the duration instance with another one.
+ *
+ * @param {Duration} aOther The instance to compare with
+ * @return {Number} -1, 0 or 1 for less/equal/greater
+ */
+ }, {
+ key: "compare",
+ value: function compare(aOther) {
+ var thisSeconds = this.toSeconds();
+ var otherSeconds = aOther.toSeconds();
+ return (thisSeconds > otherSeconds) - (thisSeconds < otherSeconds);
+ }
+
+ /**
+ * Normalizes the duration instance. For example, a duration with a value
+ * of 61 seconds will be normalized to 1 minute and 1 second.
+ */
+ }, {
+ key: "normalize",
+ value: function normalize() {
+ this.fromSeconds(this.toSeconds());
+ }
+
+ /**
+ * The string representation of this duration.
+ * @return {String}
+ */
+ }, {
+ key: "toString",
+ value: function toString() {
+ if (this.toSeconds() == 0) {
+ return "PT0S";
+ } else {
+ var str = "";
+ if (this.isNegative) str += "-";
+ str += "P";
+ var hasWeeks = false;
+ if (this.weeks) {
+ if (this.days || this.hours || this.minutes || this.seconds) {
+ str += this.weeks * 7 + this.days + "D";
+ } else {
+ str += this.weeks + "W";
+ hasWeeks = true;
+ }
+ } else if (this.days) {
+ str += this.days + "D";
+ }
+ if (!hasWeeks) {
+ if (this.hours || this.minutes || this.seconds) {
+ str += "T";
+ if (this.hours) {
+ str += this.hours + "H";
+ }
+ if (this.minutes) {
+ str += this.minutes + "M";
+ }
+ if (this.seconds) {
+ str += this.seconds + "S";
+ }
+ }
+ }
+ return str;
+ }
+ }
+
+ /**
+ * The iCalendar string representation of this duration.
+ * @return {String}
+ */
+ }, {
+ key: "toICALString",
+ value: function toICALString() {
+ return this.toString();
+ }
+ }], [{
+ key: "fromSeconds",
+ value:
+ /**
+ * Returns a new ICAL.Duration instance from the passed seconds value.
+ *
+ * @param {Number} aSeconds The seconds to create the instance from
+ * @return {Duration} The newly created duration instance
+ */
+ function fromSeconds(aSeconds) {
+ return new Duration().fromSeconds(aSeconds);
+ }
+
+ /**
+ * Checks if the given string is an iCalendar duration value.
+ *
+ * @param {String} value The raw ical value
+ * @return {Boolean} True, if the given value is of the
+ * duration ical type
+ */
+ }, {
+ key: "isValueString",
+ value: function isValueString(string) {
+ return string[0] === 'P' || string[1] === 'P';
+ }
+
+ /**
+ * Creates a new {@link ICAL.Duration} instance from the passed string.
+ *
+ * @param {String} aStr The string to parse
+ * @return {Duration} The created duration instance
+ */
+ }, {
+ key: "fromString",
+ value: function fromString(aStr) {
+ var pos = 0;
+ var dict = Object.create(null);
+ var chunks = 0;
+ while ((pos = aStr.search(DURATION_LETTERS)) !== -1) {
+ var type = aStr[pos];
+ var numeric = aStr.slice(0, Math.max(0, pos));
+ aStr = aStr.slice(pos + 1);
+ chunks += parseDurationChunk(type, numeric, dict);
+ }
+ if (chunks < 2) {
+ // There must be at least a chunk with "P" and some unit chunk
+ throw new Error('invalid duration value: Not enough duration components in "' + aStr + '"');
+ }
+ return new Duration(dict);
+ }
+
+ /**
+ * Creates a new ICAL.Duration instance from the given data object.
+ *
+ * @param {Object} aData An object with members of the duration
+ * @param {Number=} aData.weeks Duration in weeks
+ * @param {Number=} aData.days Duration in days
+ * @param {Number=} aData.hours Duration in hours
+ * @param {Number=} aData.minutes Duration in minutes
+ * @param {Number=} aData.seconds Duration in seconds
+ * @param {Boolean=} aData.isNegative If true, the duration is negative
+ * @return {Duration} The createad duration instance
+ */
+ }, {
+ key: "fromData",
+ value: function fromData(aData) {
+ return new Duration(aData);
+ }
+ }]);
+ }();
+
+ /**
+ * Internal helper function to handle a chunk of a duration.
+ *
+ * @private
+ * @param {String} letter type of duration chunk
+ * @param {String} number numeric value or -/+
+ * @param {Object} dict target to assign values to
+ */
+ function parseDurationChunk(letter, number, object) {
+ var type;
+ switch (letter) {
+ case 'P':
+ if (number && number === '-') {
+ object.isNegative = true;
+ } else {
+ object.isNegative = false;
+ }
+ // period
+ break;
+ case 'D':
+ type = 'days';
+ break;
+ case 'W':
+ type = 'weeks';
+ break;
+ case 'H':
+ type = 'hours';
+ break;
+ case 'M':
+ type = 'minutes';
+ break;
+ case 'S':
+ type = 'seconds';
+ break;
+ default:
+ // Not a valid chunk
+ return 0;
+ }
+ if (type) {
+ if (!number && number !== 0) {
+ throw new Error('invalid duration value: Missing number before "' + letter + '"');
+ }
+ var num = parseInt(number, 10);
+ if (isStrictlyNaN(num)) {
+ throw new Error('invalid duration value: Invalid number "' + number + '" before "' + letter + '"');
+ }
+ object[type] = num;
+ }
+ return 1;
+ }
+
+ /**
+ * 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").jCalComponent} jCalComponent
+ * Imports the 'occurrenceDetails' type from the "types.js" module
+ */
+
+ /**
+ * This class represents the "period" value type, with various calculation and manipulation methods.
+ *
+ * @memberof ICAL
+ */
+ var Period = /*#__PURE__*/function () {
+ /**
+ * Creates a new ICAL.Period instance. The passed data object cannot contain both and end date and
+ * a duration.
+ *
+ * @param {Object} aData An object with members of the period
+ * @param {Time=} aData.start The start of the period
+ * @param {Time=} aData.end The end of the period
+ * @param {Duration=} aData.duration The duration of the period
+ */
+ function Period(aData) {
+ _classCallCheck(this, Period);
+ /**
+ * The start of the period
+ * @type {Time}
+ */
+ _defineProperty(this, "start", null);
+ /**
+ * The end of the period
+ * @type {Time}
+ */
+ _defineProperty(this, "end", null);
+ /**
+ * The duration of the period
+ * @type {Duration}
+ */
+ _defineProperty(this, "duration", null);
+ /**
+ * The class identifier.
+ * @constant
+ * @type {String}
+ * @default "icalperiod"
+ */
+ _defineProperty(this, "icalclass", "icalperiod");
+ /**
+ * The type name, to be used in the jCal object.
+ * @constant
+ * @type {String}
+ * @default "period"
+ */
+ _defineProperty(this, "icaltype", "period");
+ this.wrappedJSObject = this;
+ if (aData && 'start' in aData) {
+ if (aData.start && !(aData.start instanceof Time)) {
+ throw new TypeError('.start must be an instance of ICAL.Time');
+ }
+ this.start = aData.start;
+ }
+ if (aData && aData.end && aData.duration) {
+ throw new Error('cannot accept both end and duration');
+ }
+ if (aData && 'end' in aData) {
+ if (aData.end && !(aData.end instanceof Time)) {
+ throw new TypeError('.end must be an instance of ICAL.Time');
+ }
+ this.end = aData.end;
+ }
+ if (aData && 'duration' in aData) {
+ if (aData.duration && !(aData.duration instanceof Duration)) {
+ throw new TypeError('.duration must be an instance of ICAL.Duration');
+ }
+ this.duration = aData.duration;
+ }
+ }
+ return _createClass(Period, [{
+ key: "clone",
+ value:
+ /**
+ * Returns a clone of the duration object.
+ *
+ * @return {Period} The cloned object
+ */
+ function clone() {
+ return Period.fromData({
+ start: this.start ? this.start.clone() : null,
+ end: this.end ? this.end.clone() : null,
+ duration: this.duration ? this.duration.clone() : null
+ });
+ }
+
+ /**
+ * Calculates the duration of the period, either directly or by subtracting
+ * start from end date.
+ *
+ * @return {Duration} The calculated duration
+ */
+ }, {
+ key: "getDuration",
+ value: function getDuration() {
+ if (this.duration) {
+ return this.duration;
+ } else {
+ return this.end.subtractDate(this.start);
+ }
+ }
+
+ /**
+ * Calculates the end date of the period, either directly or by adding
+ * duration to start date.
+ *
+ * @return {Time} The calculated end date
+ */
+ }, {
+ key: "getEnd",
+ value: function getEnd() {
+ if (this.end) {
+ return this.end;
+ } else {
+ var end = this.start.clone();
+ end.addDuration(this.duration);
+ return end;
+ }
+ }
+
+ /**
+ * Compare this period with a date or other period. To maintain the logic where a.compare(b)
+ * returns 1 when a > b, this function will return 1 when the period is after the date, 0 when the
+ * date is within the period, and -1 when the period is before the date. When comparing two
+ * periods, as soon as they overlap in any way this will return 0.
+ *
+ * @param {Time|Period} dt The date or other period to compare with
+ */
+ }, {
+ key: "compare",
+ value: function compare(dt) {
+ if (dt.compare(this.start) < 0) {
+ return 1;
+ } else if (dt.compare(this.getEnd()) > 0) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * The string representation of this period.
+ * @return {String}
+ */
+ }, {
+ key: "toString",
+ value: function toString() {
+ return this.start + "/" + (this.end || this.duration);
+ }
+
+ /**
+ * The jCal representation of this period type.
+ * @return {Object}
+ */
+ }, {
+ key: "toJSON",
+ value: function toJSON() {
+ return [this.start.toString(), (this.end || this.duration).toString()];
+ }
+
+ /**
+ * The iCalendar string representation of this period.
+ * @return {String}
+ */
+ }, {
+ key: "toICALString",
+ value: function toICALString() {
+ return this.start.toICALString() + "/" + (this.end || this.duration).toICALString();
+ }
+ }], [{
+ key: "fromString",
+ value:
+ /**
+ * Creates a new {@link ICAL.Period} instance from the passed string.
+ *
+ * @param {String} str The string to parse
+ * @param {Property} prop The property this period will be on
+ * @return {Period} The created period instance
+ */
+ function fromString(str, prop) {
+ var parts = str.split('/');
+ if (parts.length !== 2) {
+ throw new Error('Invalid string value: "' + str + '" must contain a "/" char.');
+ }
+ var options = {
+ start: Time.fromDateTimeString(parts[0], prop)
+ };
+ var end = parts[1];
+ if (Duration.isValueString(end)) {
+ options.duration = Duration.fromString(end);
+ } else {
+ options.end = Time.fromDateTimeString(end, prop);
+ }
+ return new Period(options);
+ }
+
+ /**
+ * Creates a new {@link ICAL.Period} instance from the given data object.
+ * The passed data object cannot contain both and end date and a duration.
+ *
+ * @param {Object} aData An object with members of the period
+ * @param {Time=} aData.start The start of the period
+ * @param {Time=} aData.end The end of the period
+ * @param {Duration=} aData.duration The duration of the period
+ * @return {Period} The period instance
+ */
+ }, {
+ key: "fromData",
+ value: function fromData(aData) {
+ return new Period(aData);
+ }
+
+ /**
+ * Returns a new period instance from the given jCal data array. The first
+ * member is always the start date string, the second member is either a
+ * duration or end date string.
+ *
+ * @param {jCalComponent} aData The jCal data array
+ * @param {Property} aProp The property this jCal data is on
+ * @param {Boolean} aLenient If true, data value can be both date and date-time
+ * @return {Period} The period instance
+ */
+ }, {
+ key: "fromJSON",
+ value: function fromJSON(aData, aProp, aLenient) {
+ function fromDateOrDateTimeString(aValue, dateProp) {
+ if (aLenient) {
+ return Time.fromString(aValue, dateProp);
+ } else {
+ return Time.fromDateTimeString(aValue, dateProp);
+ }
+ }
+ if (Duration.isValueString(aData[1])) {
+ return Period.fromData({
+ start: fromDateOrDateTimeString(aData[0], aProp),
+ duration: Duration.fromString(aData[1])
+ });
+ } else {
+ return Period.fromData({
+ start: fromDateOrDateTimeString(aData[0], aProp),
+ end: fromDateOrDateTimeString(aData[1], aProp)
+ });
+ }
+ }
+ }]);
+ }();
+
+ var _Time;
+
+ /**
+ * 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").weekDay} weekDay
+ * Imports the 'weekDay' type from the "types.js" module
+ *
+ * @ignore
+ * @typedef {import("./types.js").timeInit} timeInit
+ * Imports the 'timeInit' type from the "types.js" module
+ */
+
+ /**
+ * @classdesc
+ * iCalendar Time representation (similar to JS Date object). Fully
+ * independent of system (OS) timezone / time. Unlike JS Date, the month
+ * January is 1, not zero.
+ *
+ * @example
+ * var time = new ICAL.Time({
+ * year: 2012,
+ * month: 10,
+ * day: 11
+ * minute: 0,
+ * second: 0,
+ * isDate: false
+ * });
+ *
+ *
+ * @memberof ICAL
+ */
+ var Time = /*#__PURE__*/function () {
+ // MONDAY
+
+ /**
+ * Creates a new ICAL.Time instance.
+ *
+ * @param {timeInit} data Time initialization
+ * @param {Timezone} zone timezone this position occurs in
+ */
+ function Time(data, zone) {
+ _classCallCheck(this, Time);
+ /**
+ * The class identifier.
+ * @constant
+ * @type {String}
+ * @default "icaltime"
+ */
+ _defineProperty(this, "icalclass", "icaltime");
+ _defineProperty(this, "_cachedUnixTime", null);
+ /**
+ * The timezone for this time.
+ * @type {Timezone}
+ */
+ _defineProperty(this, "zone", null);
+ /**
+ * Internal uses to indicate that a change has been made and the next read
+ * operation must attempt to normalize the value (for example changing the
+ * day to 33).
+ *
+ * @type {Boolean}
+ * @private
+ */
+ _defineProperty(this, "_pendingNormalization", false);
+ this.wrappedJSObject = this;
+
+ /**
+ * @type {timeInit}
+ * @private
+ */
+ this._time = Object.create(null);
+
+ /* time defaults */
+ this._time.year = 0;
+ this._time.month = 1;
+ this._time.day = 1;
+ this._time.hour = 0;
+ this._time.minute = 0;
+ this._time.second = 0;
+ this._time.isDate = false;
+ this.fromData(data, zone);
+ }
+ return _createClass(Time, [{
+ key: "icaltype",
+ get:
+ /**
+ * The type name, to be used in the jCal object. This value may change and
+ * is strictly defined by the {@link ICAL.Time#isDate isDate} member.
+ * @type {String}
+ * @default "date-time"
+ */
+ function get() {
+ return this.isDate ? 'date' : 'date-time';
+ }
+ }, {
+ key: "year",
+ get:
+ /**
+ * The year of this date.
+ * @type {Number}
+ */
+ function get() {
+ return this._getTimeAttr('year');
+ },
+ set: function set(val) {
+ this._setTimeAttr('year', val);
+ }
+
+ /**
+ * The month of this date.
+ * @type {Number}
+ */
+ }, {
+ key: "month",
+ get: function get() {
+ return this._getTimeAttr('month');
+ },
+ set: function set(val) {
+ this._setTimeAttr('month', val);
+ }
+
+ /**
+ * The day of this date.
+ * @type {Number}
+ */
+ }, {
+ key: "day",
+ get: function get() {
+ return this._getTimeAttr('day');
+ },
+ set: function set(val) {
+ this._setTimeAttr('day', val);
+ }
+
+ /**
+ * The hour of this date-time.
+ * @type {Number}
+ */
+ }, {
+ key: "hour",
+ get: function get() {
+ return this._getTimeAttr('hour');
+ },
+ set: function set(val) {
+ this._setTimeAttr('hour', val);
+ }
+
+ /**
+ * The minute of this date-time.
+ * @type {Number}
+ */
+ }, {
+ key: "minute",
+ get: function get() {
+ return this._getTimeAttr('minute');
+ },
+ set: function set(val) {
+ this._setTimeAttr('minute', val);
+ }
+
+ /**
+ * The second of this date-time.
+ * @type {Number}
+ */
+ }, {
+ key: "second",
+ get: function get() {
+ return this._getTimeAttr('second');
+ },
+ set: function set(val) {
+ this._setTimeAttr('second', val);
+ }
+
+ /**
+ * If true, the instance represents a date (as opposed to a date-time)
+ * @type {Boolean}
+ */
+ }, {
+ key: "isDate",
+ get: function get() {
+ return this._getTimeAttr('isDate');
+ },
+ set: function set(val) {
+ this._setTimeAttr('isDate', val);
+ }
+
+ /**
+ * @private
+ * @param {String} attr Attribute to get (one of: year, month,
+ * day, hour, minute, second, isDate)
+ * @return {Number|Boolean} Current value for the attribute
+ */
+ }, {
+ key: "_getTimeAttr",
+ value: function _getTimeAttr(attr) {
+ if (this._pendingNormalization) {
+ this._normalize();
+ this._pendingNormalization = false;
+ }
+ return this._time[attr];
+ }
+
+ /**
+ * @private
+ * @param {String} attr Attribute to set (one of: year, month,
+ * day, hour, minute, second, isDate)
+ * @param {Number|Boolean} val New value for the attribute
+ */
+ }, {
+ key: "_setTimeAttr",
+ value: function _setTimeAttr(attr, val) {
+ // Check if isDate will be set and if was not set to normalize date.
+ // This avoids losing days when seconds, minutes and hours are zeroed
+ // what normalize will do when time is a date.
+ if (attr === "isDate" && val && !this._time.isDate) {
+ this.adjust(0, 0, 0, 0);
+ }
+ this._cachedUnixTime = null;
+ this._pendingNormalization = true;
+ this._time[attr] = val;
+ }
+
+ /**
+ * Returns a clone of the time object.
+ *
+ * @return {Time} The cloned object
+ */
+ }, {
+ key: "clone",
+ value: function clone() {
+ return new Time(this._time, this.zone);
+ }
+
+ /**
+ * Reset the time instance to epoch time
+ */
+ }, {
+ key: "reset",
+ value: function reset() {
+ this.fromData(Time.epochTime);
+ this.zone = Timezone.utcTimezone;
+ }
+
+ /**
+ * Reset the time instance to the given date/time values.
+ *
+ * @param {Number} year The year to set
+ * @param {Number} month The month to set
+ * @param {Number} day The day to set
+ * @param {Number} hour The hour to set
+ * @param {Number} minute The minute to set
+ * @param {Number} second The second to set
+ * @param {Timezone} timezone The timezone to set
+ */
+ }, {
+ key: "resetTo",
+ value: function resetTo(year, month, day, hour, minute, second, timezone) {
+ this.fromData({
+ year: year,
+ month: month,
+ day: day,
+ hour: hour,
+ minute: minute,
+ second: second,
+ zone: timezone
+ });
+ }
+
+ /**
+ * Set up the current instance from the Javascript date value.
+ *
+ * @param {?Date} aDate The Javascript Date to read, or null to reset
+ * @param {Boolean} [useUTC=false] If true, the UTC values of the date will be used
+ */
+ }, {
+ key: "fromJSDate",
+ value: function fromJSDate(aDate, useUTC) {
+ if (!aDate) {
+ this.reset();
+ } else {
+ if (useUTC) {
+ this.zone = Timezone.utcTimezone;
+ this.year = aDate.getUTCFullYear();
+ this.month = aDate.getUTCMonth() + 1;
+ this.day = aDate.getUTCDate();
+ this.hour = aDate.getUTCHours();
+ this.minute = aDate.getUTCMinutes();
+ this.second = aDate.getUTCSeconds();
+ } else {
+ this.zone = Timezone.localTimezone;
+ this.year = aDate.getFullYear();
+ this.month = aDate.getMonth() + 1;
+ this.day = aDate.getDate();
+ this.hour = aDate.getHours();
+ this.minute = aDate.getMinutes();
+ this.second = aDate.getSeconds();
+ }
+ }
+ this._cachedUnixTime = null;
+ return this;
+ }
+
+ /**
+ * Sets up the current instance using members from the passed data object.
+ *
+ * @param {timeInit} aData Time initialization
+ * @param {Timezone=} aZone Timezone this position occurs in
+ */
+ }, {
+ key: "fromData",
+ value: function fromData(aData, aZone) {
+ if (aData) {
+ for (var _i = 0, _Object$entries = Object.entries(aData); _i < _Object$entries.length; _i++) {
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
+ key = _Object$entries$_i[0],
+ value = _Object$entries$_i[1];
+ // ical type cannot be set
+ if (key === 'icaltype') continue;
+ this[key] = value;
+ }
+ }
+ if (aZone) {
+ this.zone = aZone;
+ }
+ if (aData && !("isDate" in aData)) {
+ this.isDate = !("hour" in aData);
+ } else if (aData && "isDate" in aData) {
+ this.isDate = aData.isDate;
+ }
+ if (aData && "timezone" in aData) {
+ var zone = TimezoneService.get(aData.timezone);
+ this.zone = zone || Timezone.localTimezone;
+ }
+ if (aData && "zone" in aData) {
+ this.zone = aData.zone;
+ }
+ if (!this.zone) {
+ this.zone = Timezone.localTimezone;
+ }
+ this._cachedUnixTime = null;
+ return this;
+ }
+
+ /**
+ * Calculate the day of week.
+ * @param {weekDay=} aWeekStart
+ * The week start weekday, defaults to SUNDAY
+ * @return {weekDay}
+ */
+ }, {
+ key: "dayOfWeek",
+ value: function dayOfWeek(aWeekStart) {
+ var firstDow = aWeekStart || Time.SUNDAY;
+ var dowCacheKey = (this.year << 12) + (this.month << 8) + (this.day << 3) + firstDow;
+ if (dowCacheKey in Time._dowCache) {
+ return Time._dowCache[dowCacheKey];
+ }
+
+ // Using Zeller's algorithm
+ var q = this.day;
+ var m = this.month + (this.month < 3 ? 12 : 0);
+ var Y = this.year - (this.month < 3 ? 1 : 0);
+ var h = q + Y + trunc((m + 1) * 26 / 10) + trunc(Y / 4);
+ {
+ // eslint-disable-line no-constant-condition
+ h += trunc(Y / 100) * 6 + trunc(Y / 400);
+ }
+
+ // Normalize to 1 = wkst
+ h = (h + 7 - firstDow) % 7 + 1;
+ Time._dowCache[dowCacheKey] = h;
+ return h;
+ }
+
+ /**
+ * Calculate the day of year.
+ * @return {Number}
+ */
+ }, {
+ key: "dayOfYear",
+ value: function dayOfYear() {
+ var is_leap = Time.isLeapYear(this.year) ? 1 : 0;
+ var diypm = Time.daysInYearPassedMonth;
+ return diypm[is_leap][this.month - 1] + this.day;
+ }
+
+ /**
+ * Returns a copy of the current date/time, rewound to the start of the
+ * week. The resulting ICAL.Time instance is of icaltype date, even if this
+ * is a date-time.
+ *
+ * @param {weekDay=} aWeekStart
+ * The week start weekday, defaults to SUNDAY
+ * @return {Time} The start of the week (cloned)
+ */
+ }, {
+ key: "startOfWeek",
+ value: function startOfWeek(aWeekStart) {
+ var firstDow = aWeekStart || Time.SUNDAY;
+ var result = this.clone();
+ result.day -= (this.dayOfWeek() + 7 - firstDow) % 7;
+ result.isDate = true;
+ result.hour = 0;
+ result.minute = 0;
+ result.second = 0;
+ return result;
+ }
+
+ /**
+ * Returns a copy of the current date/time, shifted to the end of the week.
+ * The resulting ICAL.Time instance is of icaltype date, even if this is a
+ * date-time.
+ *
+ * @param {weekDay=} aWeekStart
+ * The week start weekday, defaults to SUNDAY
+ * @return {Time} The end of the week (cloned)
+ */
+ }, {
+ key: "endOfWeek",
+ value: function endOfWeek(aWeekStart) {
+ var firstDow = aWeekStart || Time.SUNDAY;
+ var result = this.clone();
+ result.day += (7 - this.dayOfWeek() + firstDow - Time.SUNDAY) % 7;
+ result.isDate = true;
+ result.hour = 0;
+ result.minute = 0;
+ result.second = 0;
+ return result;
+ }
+
+ /**
+ * Returns a copy of the current date/time, rewound to the start of the
+ * month. The resulting ICAL.Time instance is of icaltype date, even if
+ * this is a date-time.
+ *
+ * @return {Time} The start of the month (cloned)
+ */
+ }, {
+ key: "startOfMonth",
+ value: function startOfMonth() {
+ var result = this.clone();
+ result.day = 1;
+ result.isDate = true;
+ result.hour = 0;
+ result.minute = 0;
+ result.second = 0;
+ return result;
+ }
+
+ /**
+ * Returns a copy of the current date/time, shifted to the end of the
+ * month. The resulting ICAL.Time instance is of icaltype date, even if
+ * this is a date-time.
+ *
+ * @return {Time} The end of the month (cloned)
+ */
+ }, {
+ key: "endOfMonth",
+ value: function endOfMonth() {
+ var result = this.clone();
+ result.day = Time.daysInMonth(result.month, result.year);
+ result.isDate = true;
+ result.hour = 0;
+ result.minute = 0;
+ result.second = 0;
+ return result;
+ }
+
+ /**
+ * Returns a copy of the current date/time, rewound to the start of the
+ * year. The resulting ICAL.Time instance is of icaltype date, even if
+ * this is a date-time.
+ *
+ * @return {Time} The start of the year (cloned)
+ */
+ }, {
+ key: "startOfYear",
+ value: function startOfYear() {
+ var result = this.clone();
+ result.day = 1;
+ result.month = 1;
+ result.isDate = true;
+ result.hour = 0;
+ result.minute = 0;
+ result.second = 0;
+ return result;
+ }
+
+ /**
+ * Returns a copy of the current date/time, shifted to the end of the
+ * year. The resulting ICAL.Time instance is of icaltype date, even if
+ * this is a date-time.
+ *
+ * @return {Time} The end of the year (cloned)
+ */
+ }, {
+ key: "endOfYear",
+ value: function endOfYear() {
+ var result = this.clone();
+ result.day = 31;
+ result.month = 12;
+ result.isDate = true;
+ result.hour = 0;
+ result.minute = 0;
+ result.second = 0;
+ return result;
+ }
+
+ /**
+ * First calculates the start of the week, then returns the day of year for
+ * this date. If the day falls into the previous year, the day is zero or negative.
+ *
+ * @param {weekDay=} aFirstDayOfWeek
+ * The week start weekday, defaults to SUNDAY
+ * @return {Number} The calculated day of year
+ */
+ }, {
+ key: "startDoyWeek",
+ value: function startDoyWeek(aFirstDayOfWeek) {
+ var firstDow = aFirstDayOfWeek || Time.SUNDAY;
+ var delta = this.dayOfWeek() - firstDow;
+ if (delta < 0) delta += 7;
+ return this.dayOfYear() - delta;
+ }
+
+ /**
+ * Get the dominical letter for the current year. Letters range from A - G
+ * for common years, and AG to GF for leap years.
+ *
+ * @param {Number} yr The year to retrieve the letter for
+ * @return {String} The dominical letter.
+ */
+ }, {
+ key: "getDominicalLetter",
+ value: function getDominicalLetter() {
+ return Time.getDominicalLetter(this.year);
+ }
+
+ /**
+ * Finds the nthWeekDay relative to the current month (not day). The
+ * returned value is a day relative the month that this month belongs to so
+ * 1 would indicate the first of the month and 40 would indicate a day in
+ * the following month.
+ *
+ * @param {Number} aDayOfWeek Day of the week see the day name constants
+ * @param {Number} aPos Nth occurrence of a given week day values
+ * of 1 and 0 both indicate the first weekday of that type. aPos may
+ * be either positive or negative
+ *
+ * @return {Number} numeric value indicating a day relative
+ * to the current month of this time object
+ */
+ }, {
+ key: "nthWeekDay",
+ value: function nthWeekDay(aDayOfWeek, aPos) {
+ var daysInMonth = Time.daysInMonth(this.month, this.year);
+ var weekday;
+ var pos = aPos;
+ var start = 0;
+ var otherDay = this.clone();
+ if (pos >= 0) {
+ otherDay.day = 1;
+
+ // because 0 means no position has been given
+ // 1 and 0 indicate the same day.
+ if (pos != 0) {
+ // remove the extra numeric value
+ pos--;
+ }
+
+ // set current start offset to current day.
+ start = otherDay.day;
+
+ // find the current day of week
+ var startDow = otherDay.dayOfWeek();
+
+ // calculate the difference between current
+ // day of the week and desired day of the week
+ var offset = aDayOfWeek - startDow;
+
+ // if the offset goes into the past
+ // week we add 7 so it goes into the next
+ // week. We only want to go forward in time here.
+ if (offset < 0)
+ // this is really important otherwise we would
+ // end up with dates from in the past.
+ offset += 7;
+
+ // add offset to start so start is the same
+ // day of the week as the desired day of week.
+ start += offset;
+
+ // because we are going to add (and multiply)
+ // the numeric value of the day we subtract it
+ // from the start position so not to add it twice.
+ start -= aDayOfWeek;
+
+ // set week day
+ weekday = aDayOfWeek;
+ } else {
+ // then we set it to the last day in the current month
+ otherDay.day = daysInMonth;
+
+ // find the ends weekday
+ var endDow = otherDay.dayOfWeek();
+ pos++;
+ weekday = endDow - aDayOfWeek;
+ if (weekday < 0) {
+ weekday += 7;
+ }
+ weekday = daysInMonth - weekday;
+ }
+ weekday += pos * 7;
+ return start + weekday;
+ }
+
+ /**
+ * Checks if current time is the nth weekday, relative to the current
+ * month. Will always return false when rule resolves outside of current
+ * month.
+ *
+ * @param {weekDay} aDayOfWeek Day of week to check
+ * @param {Number} aPos Relative position
+ * @return {Boolean} True, if it is the nth weekday
+ */
+ }, {
+ key: "isNthWeekDay",
+ value: function isNthWeekDay(aDayOfWeek, aPos) {
+ var dow = this.dayOfWeek();
+ if (aPos === 0 && dow === aDayOfWeek) {
+ return true;
+ }
+
+ // get pos
+ var day = this.nthWeekDay(aDayOfWeek, aPos);
+ if (day === this.day) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Calculates the ISO 8601 week number. The first week of a year is the
+ * week that contains the first Thursday. The year can have 53 weeks, if
+ * January 1st is a Friday.
+ *
+ * Note there are regions where the first week of the year is the one that
+ * starts on January 1st, which may offset the week number. Also, if a
+ * different week start is specified, this will also affect the week
+ * number.
+ *
+ * @see Time.weekOneStarts
+ * @param {weekDay} aWeekStart The weekday the week starts with
+ * @return {Number} The ISO week number
+ */
+ }, {
+ key: "weekNumber",
+ value: function weekNumber(aWeekStart) {
+ var wnCacheKey = (this.year << 12) + (this.month << 8) + (this.day << 3) + aWeekStart;
+ if (wnCacheKey in Time._wnCache) {
+ return Time._wnCache[wnCacheKey];
+ }
+ // This function courtesty of Julian Bucknall, published under the MIT license
+ // http://www.boyet.com/articles/publishedarticles/calculatingtheisoweeknumb.html
+ // plus some fixes to be able to use different week starts.
+ var week1;
+ var dt = this.clone();
+ dt.isDate = true;
+ var isoyear = this.year;
+ if (dt.month == 12 && dt.day > 25) {
+ week1 = Time.weekOneStarts(isoyear + 1, aWeekStart);
+ if (dt.compare(week1) < 0) {
+ week1 = Time.weekOneStarts(isoyear, aWeekStart);
+ } else {
+ isoyear++;
+ }
+ } else {
+ week1 = Time.weekOneStarts(isoyear, aWeekStart);
+ if (dt.compare(week1) < 0) {
+ week1 = Time.weekOneStarts(--isoyear, aWeekStart);
+ }
+ }
+ var daysBetween = dt.subtractDate(week1).toSeconds() / 86400;
+ var answer = trunc(daysBetween / 7) + 1;
+ Time._wnCache[wnCacheKey] = answer;
+ return answer;
+ }
+
+ /**
+ * Adds the duration to the current time. The instance is modified in
+ * place.
+ *
+ * @param {Duration} aDuration The duration to add
+ */
+ }, {
+ key: "addDuration",
+ value: function addDuration(aDuration) {
+ var mult = aDuration.isNegative ? -1 : 1;
+
+ // because of the duration optimizations it is much
+ // more efficient to grab all the values up front
+ // then set them directly (which will avoid a normalization call).
+ // So we don't actually normalize until we need it.
+ var second = this.second;
+ var minute = this.minute;
+ var hour = this.hour;
+ var day = this.day;
+ second += mult * aDuration.seconds;
+ minute += mult * aDuration.minutes;
+ hour += mult * aDuration.hours;
+ day += mult * aDuration.days;
+ day += mult * 7 * aDuration.weeks;
+ this.second = second;
+ this.minute = minute;
+ this.hour = hour;
+ this.day = day;
+ this._cachedUnixTime = null;
+ }
+
+ /**
+ * Subtract the date details (_excluding_ timezone). Useful for finding
+ * the relative difference between two time objects excluding their
+ * timezone differences.
+ *
+ * @param {Time} aDate The date to subtract
+ * @return {Duration} The difference as a duration
+ */
+ }, {
+ key: "subtractDate",
+ value: function subtractDate(aDate) {
+ var unixTime = this.toUnixTime() + this.utcOffset();
+ var other = aDate.toUnixTime() + aDate.utcOffset();
+ return Duration.fromSeconds(unixTime - other);
+ }
+
+ /**
+ * Subtract the date details, taking timezones into account.
+ *
+ * @param {Time} aDate The date to subtract
+ * @return {Duration} The difference in duration
+ */
+ }, {
+ key: "subtractDateTz",
+ value: function subtractDateTz(aDate) {
+ var unixTime = this.toUnixTime();
+ var other = aDate.toUnixTime();
+ return Duration.fromSeconds(unixTime - other);
+ }
+
+ /**
+ * Compares the ICAL.Time instance with another one, or a period.
+ *
+ * @param {Time|Period} aOther The instance to compare with
+ * @return {Number} -1, 0 or 1 for less/equal/greater
+ */
+ }, {
+ key: "compare",
+ value: function compare(other) {
+ if (other instanceof Period) {
+ return -1 * other.compare(this);
+ } else {
+ var a = this.toUnixTime();
+ var b = other.toUnixTime();
+ if (a > b) return 1;
+ if (b > a) return -1;
+ return 0;
+ }
+ }
+
+ /**
+ * Compares only the date part of this instance with another one.
+ *
+ * @param {Time} other The instance to compare with
+ * @param {Timezone} tz The timezone to compare in
+ * @return {Number} -1, 0 or 1 for less/equal/greater
+ */
+ }, {
+ key: "compareDateOnlyTz",
+ value: function compareDateOnlyTz(other, tz) {
+ var a = this.convertToZone(tz);
+ var b = other.convertToZone(tz);
+ var rc = 0;
+ if ((rc = Time._cmp_attr(a, b, "year")) != 0) return rc;
+ if ((rc = Time._cmp_attr(a, b, "month")) != 0) return rc;
+ if ((rc = Time._cmp_attr(a, b, "day")) != 0) return rc;
+ return rc;
+ }
+
+ /**
+ * Convert the instance into another timezone. The returned ICAL.Time
+ * instance is always a copy.
+ *
+ * @param {Timezone} zone The zone to convert to
+ * @return {Time} The copy, converted to the zone
+ */
+ }, {
+ key: "convertToZone",
+ value: function convertToZone(zone) {
+ var copy = this.clone();
+ var zone_equals = this.zone.tzid == zone.tzid;
+ if (!this.isDate && !zone_equals) {
+ Timezone.convert_time(copy, this.zone, zone);
+ }
+ copy.zone = zone;
+ return copy;
+ }
+
+ /**
+ * Calculates the UTC offset of the current date/time in the timezone it is
+ * in.
+ *
+ * @return {Number} UTC offset in seconds
+ */
+ }, {
+ key: "utcOffset",
+ value: function utcOffset() {
+ if (this.zone == Timezone.localTimezone || this.zone == Timezone.utcTimezone) {
+ return 0;
+ } else {
+ return this.zone.utcOffset(this);
+ }
+ }
+
+ /**
+ * Returns an RFC 5545 compliant ical representation of this object.
+ *
+ * @return {String} ical date/date-time
+ */
+ }, {
+ key: "toICALString",
+ value: function toICALString() {
+ var string = this.toString();
+ if (string.length > 10) {
+ return design.icalendar.value['date-time'].toICAL(string);
+ } else {
+ return design.icalendar.value.date.toICAL(string);
+ }
+ }
+
+ /**
+ * The string representation of this date/time, in jCal form
+ * (including : and - separators).
+ * @return {String}
+ */
+ }, {
+ key: "toString",
+ value: function toString() {
+ var result = this.year + '-' + pad2(this.month) + '-' + pad2(this.day);
+ if (!this.isDate) {
+ result += 'T' + pad2(this.hour) + ':' + pad2(this.minute) + ':' + pad2(this.second);
+ if (this.zone === Timezone.utcTimezone) {
+ result += 'Z';
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Converts the current instance to a Javascript date
+ * @return {Date}
+ */
+ }, {
+ key: "toJSDate",
+ value: function toJSDate() {
+ if (this.zone == Timezone.localTimezone) {
+ if (this.isDate) {
+ return new Date(this.year, this.month - 1, this.day);
+ } else {
+ return new Date(this.year, this.month - 1, this.day, this.hour, this.minute, this.second, 0);
+ }
+ } else {
+ return new Date(this.toUnixTime() * 1000);
+ }
+ }
+ }, {
+ key: "_normalize",
+ value: function _normalize() {
+ if (this._time.isDate) {
+ this._time.hour = 0;
+ this._time.minute = 0;
+ this._time.second = 0;
+ }
+ this.adjust(0, 0, 0, 0);
+ return this;
+ }
+
+ /**
+ * Adjust the date/time by the given offset
+ *
+ * @param {Number} aExtraDays The extra amount of days
+ * @param {Number} aExtraHours The extra amount of hours
+ * @param {Number} aExtraMinutes The extra amount of minutes
+ * @param {Number} aExtraSeconds The extra amount of seconds
+ * @param {Number=} aTime The time to adjust, defaults to the
+ * current instance.
+ */
+ }, {
+ key: "adjust",
+ value: function adjust(aExtraDays, aExtraHours, aExtraMinutes, aExtraSeconds, aTime) {
+ var minutesOverflow,
+ hoursOverflow,
+ daysOverflow = 0,
+ yearsOverflow = 0;
+ var second, minute, hour, day;
+ var daysInMonth;
+ var time = aTime || this._time;
+ if (!time.isDate) {
+ second = time.second + aExtraSeconds;
+ time.second = second % 60;
+ minutesOverflow = trunc(second / 60);
+ if (time.second < 0) {
+ time.second += 60;
+ minutesOverflow--;
+ }
+ minute = time.minute + aExtraMinutes + minutesOverflow;
+ time.minute = minute % 60;
+ hoursOverflow = trunc(minute / 60);
+ if (time.minute < 0) {
+ time.minute += 60;
+ hoursOverflow--;
+ }
+ hour = time.hour + aExtraHours + hoursOverflow;
+ time.hour = hour % 24;
+ daysOverflow = trunc(hour / 24);
+ if (time.hour < 0) {
+ time.hour += 24;
+ daysOverflow--;
+ }
+ }
+
+ // Adjust month and year first, because we need to know what month the day
+ // is in before adjusting it.
+ if (time.month > 12) {
+ yearsOverflow = trunc((time.month - 1) / 12);
+ } else if (time.month < 1) {
+ yearsOverflow = trunc(time.month / 12) - 1;
+ }
+ time.year += yearsOverflow;
+ time.month -= 12 * yearsOverflow;
+
+ // Now take care of the days (and adjust month if needed)
+ day = time.day + aExtraDays + daysOverflow;
+ if (day > 0) {
+ for (;;) {
+ daysInMonth = Time.daysInMonth(time.month, time.year);
+ if (day <= daysInMonth) {
+ break;
+ }
+ time.month++;
+ if (time.month > 12) {
+ time.year++;
+ time.month = 1;
+ }
+ day -= daysInMonth;
+ }
+ } else {
+ while (day <= 0) {
+ if (time.month == 1) {
+ time.year--;
+ time.month = 12;
+ } else {
+ time.month--;
+ }
+ day += Time.daysInMonth(time.month, time.year);
+ }
+ }
+ time.day = day;
+ this._cachedUnixTime = null;
+ return this;
+ }
+
+ /**
+ * Sets up the current instance from unix time, the number of seconds since
+ * January 1st, 1970.
+ *
+ * @param {Number} seconds The seconds to set up with
+ */
+ }, {
+ key: "fromUnixTime",
+ value: function fromUnixTime(seconds) {
+ this.zone = Timezone.utcTimezone;
+ // We could use `fromJSDate` here, but this is about twice as fast.
+ // We could also clone `epochTime` and use `adjust` for a more
+ // ical.js-centric approach, but this is about 100 times as fast.
+ var date = new Date(seconds * 1000);
+ this.year = date.getUTCFullYear();
+ this.month = date.getUTCMonth() + 1;
+ this.day = date.getUTCDate();
+ if (this._time.isDate) {
+ this.hour = 0;
+ this.minute = 0;
+ this.second = 0;
+ } else {
+ this.hour = date.getUTCHours();
+ this.minute = date.getUTCMinutes();
+ this.second = date.getUTCSeconds();
+ }
+ this._cachedUnixTime = null;
+ }
+
+ /**
+ * Converts the current instance to seconds since January 1st 1970.
+ *
+ * @return {Number} Seconds since 1970
+ */
+ }, {
+ key: "toUnixTime",
+ value: function toUnixTime() {
+ if (this._cachedUnixTime !== null) {
+ return this._cachedUnixTime;
+ }
+ var offset = this.utcOffset();
+
+ // we use the offset trick to ensure
+ // that we are getting the actual UTC time
+ var ms = Date.UTC(this.year, this.month - 1, this.day, this.hour, this.minute, this.second - offset);
+
+ // seconds
+ this._cachedUnixTime = ms / 1000;
+ return this._cachedUnixTime;
+ }
+
+ /**
+ * Converts time to into Object which can be serialized then re-created
+ * using the constructor.
+ *
+ * @example
+ * // toJSON will automatically be called
+ * var json = JSON.stringify(mytime);
+ *
+ * var deserialized = JSON.parse(json);
+ *
+ * var time = new ICAL.Time(deserialized);
+ *
+ * @return {Object}
+ */
+ }, {
+ key: "toJSON",
+ value: function toJSON() {
+ var copy = ['year', 'month', 'day', 'hour', 'minute', 'second', 'isDate'];
+ var result = Object.create(null);
+ var i = 0;
+ var len = copy.length;
+ var prop;
+ for (; i < len; i++) {
+ prop = copy[i];
+ result[prop] = this[prop];
+ }
+ if (this.zone) {
+ result.timezone = this.zone.tzid;
+ }
+ return result;
+ }
+ }], [{
+ key: "daysInMonth",
+ value:
+ /**
+ * Returns the days in the given month
+ *
+ * @param {Number} month The month to check
+ * @param {Number} year The year to check
+ * @return {Number} The number of days in the month
+ */
+ function daysInMonth(month, year) {
+ var _daysInMonth = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
+ var days = 30;
+ if (month < 1 || month > 12) return days;
+ days = _daysInMonth[month];
+ if (month == 2) {
+ days += Time.isLeapYear(year);
+ }
+ return days;
+ }
+
+ /**
+ * Checks if the year is a leap year
+ *
+ * @param {Number} year The year to check
+ * @return {Boolean} True, if the year is a leap year
+ */
+ }, {
+ key: "isLeapYear",
+ value: function isLeapYear(year) {
+ if (year <= 1752) {
+ return year % 4 == 0;
+ } else {
+ return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
+ }
+ }
+
+ /**
+ * Create a new ICAL.Time from the day of year and year. The date is returned
+ * in floating timezone.
+ *
+ * @param {Number} aDayOfYear The day of year
+ * @param {Number} aYear The year to create the instance in
+ * @return {Time} The created instance with the calculated date
+ */
+ }, {
+ key: "fromDayOfYear",
+ value: function fromDayOfYear(aDayOfYear, aYear) {
+ var year = aYear;
+ var doy = aDayOfYear;
+ var tt = new Time();
+ tt.auto_normalize = false;
+ var is_leap = Time.isLeapYear(year) ? 1 : 0;
+ if (doy < 1) {
+ year--;
+ is_leap = Time.isLeapYear(year) ? 1 : 0;
+ doy += Time.daysInYearPassedMonth[is_leap][12];
+ return Time.fromDayOfYear(doy, year);
+ } else if (doy > Time.daysInYearPassedMonth[is_leap][12]) {
+ is_leap = Time.isLeapYear(year) ? 1 : 0;
+ doy -= Time.daysInYearPassedMonth[is_leap][12];
+ year++;
+ return Time.fromDayOfYear(doy, year);
+ }
+ tt.year = year;
+ tt.isDate = true;
+ for (var month = 11; month >= 0; month--) {
+ if (doy > Time.daysInYearPassedMonth[is_leap][month]) {
+ tt.month = month + 1;
+ tt.day = doy - Time.daysInYearPassedMonth[is_leap][month];
+ break;
+ }
+ }
+ tt.auto_normalize = true;
+ return tt;
+ }
+
+ /**
+ * Returns a new ICAL.Time instance from a date string, e.g 2015-01-02.
+ *
+ * @deprecated Use {@link ICAL.Time.fromDateString} instead
+ * @param {String} str The string to create from
+ * @return {Time} The date/time instance
+ */
+ }, {
+ key: "fromStringv2",
+ value: function fromStringv2(str) {
+ return new Time({
+ year: parseInt(str.slice(0, 4), 10),
+ month: parseInt(str.slice(5, 7), 10),
+ day: parseInt(str.slice(8, 10), 10),
+ isDate: true
+ });
+ }
+
+ /**
+ * Returns a new ICAL.Time instance from a date string, e.g 2015-01-02.
+ *
+ * @param {String} aValue The string to create from
+ * @return {Time} The date/time instance
+ */
+ }, {
+ key: "fromDateString",
+ value: function fromDateString(aValue) {
+ // Dates should have no timezone.
+ // Google likes to sometimes specify Z on dates
+ // we specifically ignore that to avoid issues.
+
+ // YYYY-MM-DD
+ // 2012-10-10
+ return new Time({
+ year: strictParseInt(aValue.slice(0, 4)),
+ month: strictParseInt(aValue.slice(5, 7)),
+ day: strictParseInt(aValue.slice(8, 10)),
+ isDate: true
+ });
+ }
+
+ /**
+ * Returns a new ICAL.Time instance from a date-time string, e.g
+ * 2015-01-02T03:04:05. If a property is specified, the timezone is set up
+ * from the property's TZID parameter.
+ *
+ * @param {String} aValue The string to create from
+ * @param {Property=} prop The property the date belongs to
+ * @return {Time} The date/time instance
+ */
+ }, {
+ key: "fromDateTimeString",
+ value: function fromDateTimeString(aValue, prop) {
+ if (aValue.length < 19) {
+ throw new Error('invalid date-time value: "' + aValue + '"');
+ }
+ var zone;
+ var zoneId;
+ if (aValue.slice(-1) === 'Z') {
+ zone = Timezone.utcTimezone;
+ } else if (prop) {
+ zoneId = prop.getParameter('tzid');
+ if (prop.parent) {
+ if (prop.parent.name === 'standard' || prop.parent.name === 'daylight') {
+ // Per RFC 5545 3.8.2.4 and 3.8.2.2, start/end date-times within
+ // these components MUST be specified in local time.
+ zone = Timezone.localTimezone;
+ } else if (zoneId) {
+ // If the desired time zone is defined within the component tree,
+ // fetch its definition and prefer that.
+ zone = prop.parent.getTimeZoneByID(zoneId);
+ }
+ }
+ }
+ var timeData = {
+ year: strictParseInt(aValue.slice(0, 4)),
+ month: strictParseInt(aValue.slice(5, 7)),
+ day: strictParseInt(aValue.slice(8, 10)),
+ hour: strictParseInt(aValue.slice(11, 13)),
+ minute: strictParseInt(aValue.slice(14, 16)),
+ second: strictParseInt(aValue.slice(17, 19))
+ };
+
+ // Although RFC 5545 requires that all TZIDs used within a file have a
+ // corresponding time zone definition, we may not be parsing the full file
+ // or we may be dealing with a non-compliant file; in either case, we can
+ // check our own time zone service for the TZID in a last-ditch effort.
+ if (zoneId && !zone) {
+ timeData.timezone = zoneId;
+ }
+
+ // 2012-10-10T10:10:10(Z)?
+ return new Time(timeData, zone);
+ }
+
+ /**
+ * Returns a new ICAL.Time instance from a date or date-time string,
+ *
+ * @param {String} aValue The string to create from
+ * @param {Property=} prop The property the date belongs to
+ * @return {Time} The date/time instance
+ */
+ }, {
+ key: "fromString",
+ value: function fromString(aValue, aProperty) {
+ if (aValue.length > 10) {
+ return Time.fromDateTimeString(aValue, aProperty);
+ } else {
+ return Time.fromDateString(aValue);
+ }
+ }
+
+ /**
+ * Creates a new ICAL.Time instance from the given Javascript Date.
+ *
+ * @param {?Date} aDate The Javascript Date to read, or null to reset
+ * @param {Boolean} [useUTC=false] If true, the UTC values of the date will be used
+ */
+ }, {
+ key: "fromJSDate",
+ value: function fromJSDate(aDate, useUTC) {
+ var tt = new Time();
+ return tt.fromJSDate(aDate, useUTC);
+ }
+
+ /**
+ * Creates a new ICAL.Time instance from the the passed data object.
+ *
+ * @param {timeInit} aData Time initialization
+ * @param {Timezone=} aZone Timezone this position occurs in
+ */
+ }, {
+ key: "now",
+ value:
+ /**
+ * Creates a new ICAL.Time instance from the current moment.
+ * The instance is “floating” - has no timezone relation.
+ * To create an instance considering the time zone, call
+ * ICAL.Time.fromJSDate(new Date(), true)
+ * @return {Time}
+ */
+ function now() {
+ return Time.fromJSDate(new Date(), false);
+ }
+
+ /**
+ * Returns the date on which ISO week number 1 starts.
+ *
+ * @see Time#weekNumber
+ * @param {Number} aYear The year to search in
+ * @param {weekDay=} aWeekStart The week start weekday, used for calculation.
+ * @return {Time} The date on which week number 1 starts
+ */
+ }, {
+ key: "weekOneStarts",
+ value: function weekOneStarts(aYear, aWeekStart) {
+ var t = Time.fromData({
+ year: aYear,
+ month: 1,
+ day: 1,
+ isDate: true
+ });
+ var dow = t.dayOfWeek();
+ var wkst = aWeekStart || Time.DEFAULT_WEEK_START;
+ if (dow > Time.THURSDAY) {
+ t.day += 7;
+ }
+ if (wkst > Time.THURSDAY) {
+ t.day -= 7;
+ }
+ t.day -= dow - wkst;
+ return t;
+ }
+
+ /**
+ * Get the dominical letter for the given year. Letters range from A - G for
+ * common years, and AG to GF for leap years.
+ *
+ * @param {Number} yr The year to retrieve the letter for
+ * @return {String} The dominical letter.
+ */
+ }, {
+ key: "getDominicalLetter",
+ value: function getDominicalLetter(yr) {
+ var LTRS = "GFEDCBA";
+ var dom = (yr + (yr / 4 | 0) + (yr / 400 | 0) - (yr / 100 | 0) - 1) % 7;
+ var isLeap = Time.isLeapYear(yr);
+ if (isLeap) {
+ return LTRS[(dom + 6) % 7] + LTRS[dom];
+ } else {
+ return LTRS[dom];
+ }
+ }
+ }, {
+ key: "epochTime",
+ get:
+ /**
+ * January 1st, 1970 as an ICAL.Time.
+ * @type {Time}
+ * @constant
+ * @instance
+ */
+ function get() {
+ if (!_assertClassBrand(Time, this, _epochTime)._) {
+ _epochTime._ = _assertClassBrand(Time, this, Time.fromData({
+ year: 1970,
+ month: 1,
+ day: 1,
+ hour: 0,
+ minute: 0,
+ second: 0,
+ isDate: false,
+ timezone: "Z"
+ }));
+ }
+ return _assertClassBrand(Time, this, _epochTime)._;
+ }
+ }, {
+ key: "_cmp_attr",
+ value: function _cmp_attr(a, b, attr) {
+ if (a[attr] > b[attr]) return 1;
+ if (a[attr] < b[attr]) return -1;
+ return 0;
+ }
+
+ /**
+ * The days that have passed in the year after a given month. The array has
+ * two members, one being an array of passed days for non-leap years, the
+ * other analog for leap years.
+ * @example
+ * var isLeapYear = ICAL.Time.isLeapYear(year);
+ * var passedDays = ICAL.Time.daysInYearPassedMonth[isLeapYear][month];
+ * @type {Array.>}
+ */
+ }]);
+ }();
+ _Time = Time;
+ _defineProperty(Time, "_dowCache", {});
+ _defineProperty(Time, "_wnCache", {});
+ _defineProperty(Time, "fromData", function fromData(aData, aZone) {
+ var t = new _Time();
+ return t.fromData(aData, aZone);
+ });
+ var _epochTime = {
+ _: null
+ };
+ _defineProperty(Time, "daysInYearPassedMonth", [[0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365], [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]]);
+ _defineProperty(Time, "SUNDAY", 1);
+ _defineProperty(Time, "MONDAY", 2);
+ _defineProperty(Time, "TUESDAY", 3);
+ _defineProperty(Time, "WEDNESDAY", 4);
+ _defineProperty(Time, "THURSDAY", 5);
+ _defineProperty(Time, "FRIDAY", 6);
+ _defineProperty(Time, "SATURDAY", 7);
+ /**
+ * The default weekday for the WKST part.
+ * @constant
+ * @default ICAL.Time.MONDAY
+ */
+ _defineProperty(Time, "DEFAULT_WEEK_START", 2);
+
+ /**
+ * 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").parserState} parserState
+ * Imports the 'parserState' type from the "types.js" module
+ * @typedef {import("./types.js").designSet} designSet
+ * Imports the 'designSet' type from the "types.js" module
+ */
+
+ var CHAR = /[^ \t]/;
+ var VALUE_DELIMITER = ':';
+ var PARAM_DELIMITER = ';';
+ var PARAM_NAME_DELIMITER = '=';
+ var DEFAULT_VALUE_TYPE$1 = 'unknown';
+ var DEFAULT_PARAM_TYPE = 'text';
+ var RFC6868_REPLACE_MAP$1 = {
+ "^'": '"',
+ "^n": "\n",
+ "^^": "^"
+ };
+
+ /**
+ * Parses iCalendar or vCard data into a raw jCal object. Consult
+ * documentation on the {@tutorial layers|layers of parsing} for more
+ * details.
+ *
+ * @function ICAL.parse
+ * @memberof ICAL
+ * @variation function
+ * @todo Fix the API to be more clear on the return type
+ * @param {String} input The string data to parse
+ * @return {Object|Object[]} A single jCal object, or an array thereof
+ */
+ function parse(input) {
+ var state = {};
+ var root = state.component = [];
+ state.stack = [root];
+ parse._eachLine(input, function (err, line) {
+ parse._handleContentLine(line, state);
+ });
+
+ // when there are still items on the stack
+ // throw a fatal error, a component was not closed
+ // correctly in that case.
+ if (state.stack.length > 1) {
+ throw new ParserError('invalid ical body. component began but did not end');
+ }
+ state = null;
+ return root.length == 1 ? root[0] : root;
+ }
+
+ /**
+ * Parse an iCalendar property value into the jCal for a single property
+ *
+ * @function ICAL.parse.property
+ * @param {String} str
+ * The iCalendar property string to parse
+ * @param {designSet=} designSet
+ * The design data to use for this property
+ * @return {Object}
+ * The jCal Object containing the property
+ */
+ parse.property = function (str, designSet) {
+ var state = {
+ component: [[], []],
+ designSet: designSet || design.defaultSet
+ };
+ parse._handleContentLine(str, state);
+ return state.component[1][0];
+ };
+
+ /**
+ * Convenience method to parse a component. You can use ICAL.parse() directly
+ * instead.
+ *
+ * @function ICAL.parse.component
+ * @see ICAL.parse(function)
+ * @param {String} str The iCalendar component string to parse
+ * @return {Object} The jCal Object containing the component
+ */
+ parse.component = function (str) {
+ return parse(str);
+ };
+
+ /**
+ * An error that occurred during parsing.
+ *
+ * @param {String} message The error message
+ * @memberof ICAL.parse
+ * @extends {Error}
+ */
+ var ParserError = /*#__PURE__*/function (_Error) {
+ function ParserError() {
+ var _this;
+ _classCallCheck(this, ParserError);
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+ _this = _callSuper(this, ParserError, [].concat(args));
+ _defineProperty(_this, "name", _this.constructor.name);
+ return _this;
+ }
+ _inherits(ParserError, _Error);
+ return _createClass(ParserError);
+ }(/*#__PURE__*/_wrapNativeSuper(Error)); // classes & constants
+ parse.ParserError = ParserError;
+
+ /**
+ * Handles a single line of iCalendar/vCard, updating the state.
+ *
+ * @private
+ * @function ICAL.parse._handleContentLine
+ * @param {String} line The content line to process
+ * @param {parserState} state The current state of the line parsing
+ */
+ parse._handleContentLine = function (line, state) {
+ // break up the parts of the line
+ var valuePos = line.indexOf(VALUE_DELIMITER);
+ var paramPos = line.indexOf(PARAM_DELIMITER);
+ var lastParamIndex;
+ var lastValuePos;
+
+ // name of property or begin/end
+ var name;
+ var value;
+ // params is only overridden if paramPos !== -1.
+ // we can't do params = params || {} later on
+ // because it sacrifices ops.
+ var params = {};
+
+ /**
+ * Different property cases
+ *
+ *
+ * 1. RRULE:FREQ=foo
+ * // FREQ= is not a param but the value
+ *
+ * 2. ATTENDEE;ROLE=REQ-PARTICIPANT;
+ * // ROLE= is a param because : has not happened yet
+ */
+ // when the parameter delimiter is after the
+ // value delimiter then it is not a parameter.
+
+ if (paramPos !== -1 && valuePos !== -1) {
+ // when the parameter delimiter is after the
+ // value delimiter then it is not a parameter.
+ if (paramPos > valuePos) {
+ paramPos = -1;
+ }
+ }
+ var parsedParams;
+ if (paramPos !== -1) {
+ name = line.slice(0, Math.max(0, paramPos)).toLowerCase();
+ parsedParams = parse._parseParameters(line.slice(Math.max(0, paramPos)), 0, state.designSet);
+ if (parsedParams[2] == -1) {
+ throw new ParserError("Invalid parameters in '" + line + "'");
+ }
+ params = parsedParams[0];
+ // Handle parameter values with multiple entries
+ var parsedParamLength;
+ if (typeof parsedParams[1] === 'string') {
+ parsedParamLength = parsedParams[1].length;
+ } else {
+ parsedParamLength = parsedParams[1].reduce(function (accumulator, currentValue) {
+ return accumulator + currentValue.length;
+ }, 0);
+ }
+ lastParamIndex = parsedParamLength + parsedParams[2] + paramPos;
+ if ((lastValuePos = line.slice(Math.max(0, lastParamIndex)).indexOf(VALUE_DELIMITER)) !== -1) {
+ value = line.slice(Math.max(0, lastParamIndex + lastValuePos + 1));
+ } else {
+ throw new ParserError("Missing parameter value in '" + line + "'");
+ }
+ } else if (valuePos !== -1) {
+ // without parmeters (BEGIN:VCAENDAR, CLASS:PUBLIC)
+ name = line.slice(0, Math.max(0, valuePos)).toLowerCase();
+ value = line.slice(Math.max(0, valuePos + 1));
+ if (name === 'begin') {
+ var newComponent = [value.toLowerCase(), [], []];
+ if (state.stack.length === 1) {
+ state.component.push(newComponent);
+ } else {
+ state.component[2].push(newComponent);
+ }
+ state.stack.push(state.component);
+ state.component = newComponent;
+ if (!state.designSet) {
+ state.designSet = design.getDesignSet(state.component[0]);
+ }
+ return;
+ } else if (name === 'end') {
+ state.component = state.stack.pop();
+ return;
+ }
+ // If it is not begin/end, then this is a property with an empty value,
+ // which should be considered valid.
+ } else {
+ /**
+ * Invalid line.
+ * The rational to throw an error is we will
+ * never be certain that the rest of the file
+ * is sane and it is unlikely that we can serialize
+ * the result correctly either.
+ */
+ throw new ParserError('invalid line (no token ";" or ":") "' + line + '"');
+ }
+ var valueType;
+ var multiValue = false;
+ var structuredValue = false;
+ var propertyDetails;
+ var splitName;
+ var ungroupedName;
+
+ // fetch the ungrouped part of the name
+ if (state.designSet.propertyGroups && name.indexOf('.') !== -1) {
+ splitName = name.split('.');
+ params.group = splitName[0];
+ ungroupedName = splitName[1];
+ } else {
+ ungroupedName = name;
+ }
+ if (ungroupedName in state.designSet.property) {
+ propertyDetails = state.designSet.property[ungroupedName];
+ if ('multiValue' in propertyDetails) {
+ multiValue = propertyDetails.multiValue;
+ }
+ if ('structuredValue' in propertyDetails) {
+ structuredValue = propertyDetails.structuredValue;
+ }
+ if (value && 'detectType' in propertyDetails) {
+ valueType = propertyDetails.detectType(value);
+ }
+ }
+
+ // attempt to determine value
+ if (!valueType) {
+ if (!('value' in params)) {
+ if (propertyDetails) {
+ valueType = propertyDetails.defaultType;
+ } else {
+ valueType = DEFAULT_VALUE_TYPE$1;
+ }
+ } else {
+ // possible to avoid this?
+ valueType = params.value.toLowerCase();
+ }
+ }
+ delete params.value;
+
+ /**
+ * Note on `var result` juggling:
+ *
+ * I observed that building the array in pieces has adverse
+ * effects on performance, so where possible we inline the creation.
+ * It is a little ugly but resulted in ~2000 additional ops/sec.
+ */
+
+ var result;
+ if (multiValue && structuredValue) {
+ value = parse._parseMultiValue(value, structuredValue, valueType, [], multiValue, state.designSet, structuredValue);
+ result = [ungroupedName, params, valueType, value];
+ } else if (multiValue) {
+ result = [ungroupedName, params, valueType];
+ parse._parseMultiValue(value, multiValue, valueType, result, null, state.designSet, false);
+ } else if (structuredValue) {
+ value = parse._parseMultiValue(value, structuredValue, valueType, [], null, state.designSet, structuredValue);
+ result = [ungroupedName, params, valueType, value];
+ } else {
+ value = parse._parseValue(value, valueType, state.designSet, false);
+ result = [ungroupedName, params, valueType, value];
+ }
+ // rfc6350 requires that in vCard 4.0 the first component is the VERSION
+ // component with as value 4.0, note that 3.0 does not have this requirement.
+ if (state.component[0] === 'vcard' && state.component[1].length === 0 && !(name === 'version' && value === '4.0')) {
+ state.designSet = design.getDesignSet("vcard3");
+ }
+ state.component[1].push(result);
+ };
+
+ /**
+ * Parse a value from the raw value into the jCard/jCal value.
+ *
+ * @private
+ * @function ICAL.parse._parseValue
+ * @param {String} value Original value
+ * @param {String} type Type of value
+ * @param {Object} designSet The design data to use for this value
+ * @return {Object} varies on type
+ */
+ parse._parseValue = function (value, type, designSet, structuredValue) {
+ if (type in designSet.value && 'fromICAL' in designSet.value[type]) {
+ return designSet.value[type].fromICAL(value, structuredValue);
+ }
+ return value;
+ };
+
+ /**
+ * Parse parameters from a string to object.
+ *
+ * @function ICAL.parse._parseParameters
+ * @private
+ * @param {String} line A single unfolded line
+ * @param {Number} start Position to start looking for properties
+ * @param {Object} designSet The design data to use for this property
+ * @return {Array} Array containing key/valye pairs of parsed parameters, the
+ * parsed value, and the position of the last parameter found
+ */
+ parse._parseParameters = function (line, start, designSet) {
+ var lastParam = start;
+ var pos = 0;
+ var delim = PARAM_NAME_DELIMITER;
+ var result = {};
+ var name, lcname;
+ var value,
+ valuePos = -1;
+ var type, multiValue, mvdelim;
+
+ // find the next '=' sign
+ // use lastParam and pos to find name
+ // check if " is used if so get value from "->"
+ // then increment pos to find next ;
+
+ while (pos !== false && (pos = line.indexOf(delim, pos + 1)) !== -1) {
+ name = line.slice(lastParam + 1, pos);
+ if (name.length == 0) {
+ throw new ParserError("Empty parameter name in '" + line + "'");
+ }
+ lcname = name.toLowerCase();
+ mvdelim = false;
+ multiValue = false;
+ if (lcname in designSet.param && designSet.param[lcname].valueType) {
+ type = designSet.param[lcname].valueType;
+ } else {
+ type = DEFAULT_PARAM_TYPE;
+ }
+ if (lcname in designSet.param) {
+ multiValue = designSet.param[lcname].multiValue;
+ if (designSet.param[lcname].multiValueSeparateDQuote) {
+ mvdelim = parse._rfc6868Escape('"' + multiValue + '"');
+ }
+ }
+ var nextChar = line[pos + 1];
+ if (nextChar === '"') {
+ valuePos = pos + 2;
+ pos = line.indexOf('"', valuePos);
+ if (multiValue && pos != -1) {
+ var extendedValue = true;
+ while (extendedValue) {
+ if (line[pos + 1] == multiValue && line[pos + 2] == '"') {
+ pos = line.indexOf('"', pos + 3);
+ } else {
+ extendedValue = false;
+ }
+ }
+ }
+ if (pos === -1) {
+ throw new ParserError('invalid line (no matching double quote) "' + line + '"');
+ }
+ value = line.slice(valuePos, pos);
+ lastParam = line.indexOf(PARAM_DELIMITER, pos);
+ var propValuePos = line.indexOf(VALUE_DELIMITER, pos);
+ // if either no next parameter or delimeter in property value, let's stop here
+ if (lastParam === -1 || propValuePos !== -1 && lastParam > propValuePos) {
+ pos = false;
+ }
+ } else {
+ valuePos = pos + 1;
+
+ // move to next ";"
+ var nextPos = line.indexOf(PARAM_DELIMITER, valuePos);
+ var _propValuePos = line.indexOf(VALUE_DELIMITER, valuePos);
+ if (_propValuePos !== -1 && nextPos > _propValuePos) {
+ // this is a delimiter in the property value, let's stop here
+ nextPos = _propValuePos;
+ pos = false;
+ } else if (nextPos === -1) {
+ // no ";"
+ if (_propValuePos === -1) {
+ nextPos = line.length;
+ } else {
+ nextPos = _propValuePos;
+ }
+ pos = false;
+ } else {
+ lastParam = nextPos;
+ pos = nextPos;
+ }
+ value = line.slice(valuePos, nextPos);
+ }
+ var length_before = value.length;
+ value = parse._rfc6868Escape(value);
+ valuePos += length_before - value.length;
+ if (multiValue) {
+ var delimiter = mvdelim || multiValue;
+ value = parse._parseMultiValue(value, delimiter, type, [], null, designSet);
+ } else {
+ value = parse._parseValue(value, type, designSet);
+ }
+ if (multiValue && lcname in result) {
+ if (Array.isArray(result[lcname])) {
+ result[lcname].push(value);
+ } else {
+ result[lcname] = [result[lcname], value];
+ }
+ } else {
+ result[lcname] = value;
+ }
+ }
+ return [result, value, valuePos];
+ };
+
+ /**
+ * Internal helper for rfc6868. Exposing this on ICAL.parse so that
+ * hackers can disable the rfc6868 parsing if the really need to.
+ *
+ * @function ICAL.parse._rfc6868Escape
+ * @param {String} val The value to escape
+ * @return {String} The escaped value
+ */
+ parse._rfc6868Escape = function (val) {
+ return val.replace(/\^['n^]/g, function (x) {
+ return RFC6868_REPLACE_MAP$1[x];
+ });
+ };
+
+ /**
+ * Parse a multi value string. This function is used either for parsing
+ * actual multi-value property's values, or for handling parameter values. It
+ * can be used for both multi-value properties and structured value properties.
+ *
+ * @private
+ * @function ICAL.parse._parseMultiValue
+ * @param {String} buffer The buffer containing the full value
+ * @param {String} delim The multi-value delimiter
+ * @param {String} type The value type to be parsed
+ * @param {Array.>} result The array to append results to, varies on value type
+ * @param {String} innerMulti The inner delimiter to split each value with
+ * @param {designSet} designSet The design data for this value
+ * @return {?|Array.>} Either an array of results, or the first result
+ */
+ parse._parseMultiValue = function (buffer, delim, type, result, innerMulti, designSet, structuredValue) {
+ var pos = 0;
+ var lastPos = 0;
+ var value;
+ if (delim.length === 0) {
+ return buffer;
+ }
+
+ // split each piece
+ while ((pos = unescapedIndexOf(buffer, delim, lastPos)) !== -1) {
+ value = buffer.slice(lastPos, pos);
+ if (innerMulti) {
+ value = parse._parseMultiValue(value, innerMulti, type, [], null, designSet, structuredValue);
+ } else {
+ value = parse._parseValue(value, type, designSet, structuredValue);
+ }
+ result.push(value);
+ lastPos = pos + delim.length;
+ }
+
+ // on the last piece take the rest of string
+ value = buffer.slice(lastPos);
+ if (innerMulti) {
+ value = parse._parseMultiValue(value, innerMulti, type, [], null, designSet, structuredValue);
+ } else {
+ value = parse._parseValue(value, type, designSet, structuredValue);
+ }
+ result.push(value);
+ return result.length == 1 ? result[0] : result;
+ };
+
+ /**
+ * Process a complete buffer of iCalendar/vCard data line by line, correctly
+ * unfolding content. Each line will be processed with the given callback
+ *
+ * @private
+ * @function ICAL.parse._eachLine
+ * @param {String} buffer The buffer to process
+ * @param {function(?String, String)} callback The callback for each line
+ */
+ parse._eachLine = function (buffer, callback) {
+ var len = buffer.length;
+ var lastPos = buffer.search(CHAR);
+ var pos = lastPos;
+ var line;
+ var firstChar;
+ var newlineOffset;
+ do {
+ pos = buffer.indexOf('\n', lastPos) + 1;
+ if (pos > 1 && buffer[pos - 2] === '\r') {
+ newlineOffset = 2;
+ } else {
+ newlineOffset = 1;
+ }
+ if (pos === 0) {
+ pos = len;
+ newlineOffset = 0;
+ }
+ firstChar = buffer[lastPos];
+ if (firstChar === ' ' || firstChar === '\t') {
+ // add to line
+ line += buffer.slice(lastPos + 1, pos - newlineOffset);
+ } else {
+ if (line) callback(null, line);
+ // push line
+ line = buffer.slice(lastPos, pos - newlineOffset);
+ }
+ lastPos = pos;
+ } while (pos !== len);
+
+ // extra ending line
+ line = line.trim();
+ if (line.length) callback(null, line);
+ };
+
+ var OPTIONS = ["tzid", "location", "tznames", "latitude", "longitude"];
+
+ /**
+ * Timezone representation.
+ *
+ * @example
+ * var vcalendar;
+ * var timezoneComp = vcalendar.getFirstSubcomponent('vtimezone');
+ * var tzid = timezoneComp.getFirstPropertyValue('tzid');
+ *
+ * var timezone = new ICAL.Timezone({
+ * component: timezoneComp,
+ * tzid
+ * });
+ *
+ * @memberof ICAL
+ */
+ var Timezone = /*#__PURE__*/function () {
+ /**
+ * Creates a new ICAL.Timezone instance, by passing in a tzid and component.
+ *
+ * @param {Component|Object} data options for class
+ * @param {String|Component} data.component
+ * If data is a simple object, then this member can be set to either a
+ * string containing the component data, or an already parsed
+ * ICAL.Component
+ * @param {String} data.tzid The timezone identifier
+ * @param {String} data.location The timezone locationw
+ * @param {String} data.tznames An alternative string representation of the
+ * timezone
+ * @param {Number} data.latitude The latitude of the timezone
+ * @param {Number} data.longitude The longitude of the timezone
+ */
+ function Timezone(data) {
+ _classCallCheck(this, Timezone);
+ /**
+ * Timezone identifier
+ * @type {String}
+ */
+ _defineProperty(this, "tzid", "");
+ /**
+ * Timezone location
+ * @type {String}
+ */
+ _defineProperty(this, "location", "");
+ /**
+ * Alternative timezone name, for the string representation
+ * @type {String}
+ */
+ _defineProperty(this, "tznames", "");
+ /**
+ * The primary latitude for the timezone.
+ * @type {Number}
+ */
+ _defineProperty(this, "latitude", 0.0);
+ /**
+ * The primary longitude for the timezone.
+ * @type {Number}
+ */
+ _defineProperty(this, "longitude", 0.0);
+ /**
+ * The vtimezone component for this timezone.
+ * @type {Component}
+ */
+ _defineProperty(this, "component", null);
+ /**
+ * The year this timezone has been expanded to. All timezone transition
+ * dates until this year are known and can be used for calculation
+ *
+ * @private
+ * @type {Number}
+ */
+ _defineProperty(this, "expandedUntilYear", 0);
+ /**
+ * The class identifier.
+ * @constant
+ * @type {String}
+ * @default "icaltimezone"
+ */
+ _defineProperty(this, "icalclass", "icaltimezone");
+ this.wrappedJSObject = this;
+ this.fromData(data);
+ }
+ return _createClass(Timezone, [{
+ key: "fromData",
+ value:
+ /**
+ * Sets up the current instance using members from the passed data object.
+ *
+ * @param {Component|Object} aData options for class
+ * @param {String|Component} aData.component
+ * If aData is a simple object, then this member can be set to either a
+ * string containing the component data, or an already parsed
+ * ICAL.Component
+ * @param {String} aData.tzid The timezone identifier
+ * @param {String} aData.location The timezone locationw
+ * @param {String} aData.tznames An alternative string representation of the
+ * timezone
+ * @param {Number} aData.latitude The latitude of the timezone
+ * @param {Number} aData.longitude The longitude of the timezone
+ */
+ function fromData(aData) {
+ this.expandedUntilYear = 0;
+ this.changes = [];
+ if (aData instanceof Component) {
+ // Either a component is passed directly
+ this.component = aData;
+ } else {
+ // Otherwise the component may be in the data object
+ if (aData && "component" in aData) {
+ if (typeof aData.component == "string") {
+ // If a string was passed, parse it as a component
+ var jCal = parse(aData.component);
+ this.component = new Component(jCal);
+ } else if (aData.component instanceof Component) {
+ // If it was a component already, then just set it
+ this.component = aData.component;
+ } else {
+ // Otherwise just null out the component
+ this.component = null;
+ }
+ }
+
+ // Copy remaining passed properties
+ for (var _i = 0, _OPTIONS = OPTIONS; _i < _OPTIONS.length; _i++) {
+ var prop = _OPTIONS[_i];
+ if (aData && prop in aData) {
+ this[prop] = aData[prop];
+ }
+ }
+ }
+
+ // If we have a component but no TZID, attempt to get it from the
+ // component's properties.
+ if (this.component instanceof Component && !this.tzid) {
+ this.tzid = this.component.getFirstPropertyValue('tzid');
+ }
+ return this;
+ }
+
+ /**
+ * Finds the utcOffset the given time would occur in this timezone.
+ *
+ * @param {Time} tt The time to check for
+ * @return {Number} utc offset in seconds
+ */
+ }, {
+ key: "utcOffset",
+ value: function utcOffset(tt) {
+ if (this == Timezone.utcTimezone || this == Timezone.localTimezone) {
+ return 0;
+ }
+ this._ensureCoverage(tt.year);
+ if (!this.changes.length) {
+ return 0;
+ }
+ var tt_change = {
+ year: tt.year,
+ month: tt.month,
+ day: tt.day,
+ hour: tt.hour,
+ minute: tt.minute,
+ second: tt.second
+ };
+ var change_num = this._findNearbyChange(tt_change);
+ var change_num_to_use = -1;
+ var step = 1;
+
+ // TODO: replace with bin search?
+ for (;;) {
+ var change = clone(this.changes[change_num], true);
+ if (change.utcOffset < change.prevUtcOffset) {
+ Timezone.adjust_change(change, 0, 0, 0, change.utcOffset);
+ } else {
+ Timezone.adjust_change(change, 0, 0, 0, change.prevUtcOffset);
+ }
+ var cmp = Timezone._compare_change_fn(tt_change, change);
+ if (cmp >= 0) {
+ change_num_to_use = change_num;
+ } else {
+ step = -1;
+ }
+ if (step == -1 && change_num_to_use != -1) {
+ break;
+ }
+ change_num += step;
+ if (change_num < 0) {
+ return 0;
+ }
+ if (change_num >= this.changes.length) {
+ break;
+ }
+ }
+ var zone_change = this.changes[change_num_to_use];
+ var utcOffset_change = zone_change.utcOffset - zone_change.prevUtcOffset;
+ if (utcOffset_change < 0 && change_num_to_use > 0) {
+ var tmp_change = clone(zone_change, true);
+ Timezone.adjust_change(tmp_change, 0, 0, 0, tmp_change.prevUtcOffset);
+ if (Timezone._compare_change_fn(tt_change, tmp_change) < 0) {
+ var prev_zone_change = this.changes[change_num_to_use - 1];
+ var want_daylight = false; // TODO
+
+ if (zone_change.is_daylight != want_daylight && prev_zone_change.is_daylight == want_daylight) {
+ zone_change = prev_zone_change;
+ }
+ }
+ }
+
+ // TODO return is_daylight?
+ return zone_change.utcOffset;
+ }
+ }, {
+ key: "_findNearbyChange",
+ value: function _findNearbyChange(change) {
+ // find the closest match
+ var idx = binsearchInsert(this.changes, change, Timezone._compare_change_fn);
+ if (idx >= this.changes.length) {
+ return this.changes.length - 1;
+ }
+ return idx;
+ }
+ }, {
+ key: "_ensureCoverage",
+ value: function _ensureCoverage(aYear) {
+ if (Timezone._minimumExpansionYear == -1) {
+ var today = Time.now();
+ Timezone._minimumExpansionYear = today.year;
+ }
+ var changesEndYear = aYear;
+ if (changesEndYear < Timezone._minimumExpansionYear) {
+ changesEndYear = Timezone._minimumExpansionYear;
+ }
+ changesEndYear += Timezone.EXTRA_COVERAGE;
+ if (!this.changes.length || this.expandedUntilYear < aYear) {
+ var subcomps = this.component.getAllSubcomponents();
+ var compLen = subcomps.length;
+ var compIdx = 0;
+ for (; compIdx < compLen; compIdx++) {
+ this._expandComponent(subcomps[compIdx], changesEndYear, this.changes);
+ }
+ this.changes.sort(Timezone._compare_change_fn);
+ this.expandedUntilYear = changesEndYear;
+ }
+ }
+ }, {
+ key: "_expandComponent",
+ value: function _expandComponent(aComponent, aYear, changes) {
+ if (!aComponent.hasProperty("dtstart") || !aComponent.hasProperty("tzoffsetto") || !aComponent.hasProperty("tzoffsetfrom")) {
+ return null;
+ }
+ var dtstart = aComponent.getFirstProperty("dtstart").getFirstValue();
+ var change;
+ function convert_tzoffset(offset) {
+ return offset.factor * (offset.hours * 3600 + offset.minutes * 60);
+ }
+ function init_changes() {
+ var changebase = {};
+ changebase.is_daylight = aComponent.name == "daylight";
+ changebase.utcOffset = convert_tzoffset(aComponent.getFirstProperty("tzoffsetto").getFirstValue());
+ changebase.prevUtcOffset = convert_tzoffset(aComponent.getFirstProperty("tzoffsetfrom").getFirstValue());
+ return changebase;
+ }
+ if (!aComponent.hasProperty("rrule") && !aComponent.hasProperty("rdate")) {
+ change = init_changes();
+ change.year = dtstart.year;
+ change.month = dtstart.month;
+ change.day = dtstart.day;
+ change.hour = dtstart.hour;
+ change.minute = dtstart.minute;
+ change.second = dtstart.second;
+ Timezone.adjust_change(change, 0, 0, 0, -change.prevUtcOffset);
+ changes.push(change);
+ } else {
+ var props = aComponent.getAllProperties("rdate");
+ var _iterator = _createForOfIteratorHelper(props),
+ _step;
+ try {
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
+ var rdate = _step.value;
+ var time = rdate.getFirstValue();
+ change = init_changes();
+ change.year = time.year;
+ change.month = time.month;
+ change.day = time.day;
+ if (time.isDate) {
+ change.hour = dtstart.hour;
+ change.minute = dtstart.minute;
+ change.second = dtstart.second;
+ if (dtstart.zone != Timezone.utcTimezone) {
+ Timezone.adjust_change(change, 0, 0, 0, -change.prevUtcOffset);
+ }
+ } else {
+ change.hour = time.hour;
+ change.minute = time.minute;
+ change.second = time.second;
+ if (time.zone != Timezone.utcTimezone) {
+ Timezone.adjust_change(change, 0, 0, 0, -change.prevUtcOffset);
+ }
+ }
+ changes.push(change);
+ }
+ } catch (err) {
+ _iterator.e(err);
+ } finally {
+ _iterator.f();
+ }
+ var rrule = aComponent.getFirstProperty("rrule");
+ if (rrule) {
+ rrule = rrule.getFirstValue();
+ change = init_changes();
+ if (rrule.until && rrule.until.zone == Timezone.utcTimezone) {
+ rrule.until.adjust(0, 0, 0, change.prevUtcOffset);
+ rrule.until.zone = Timezone.localTimezone;
+ }
+ var iterator = rrule.iterator(dtstart);
+ var occ;
+ while (occ = iterator.next()) {
+ change = init_changes();
+ if (occ.year > aYear || !occ) {
+ break;
+ }
+ change.year = occ.year;
+ change.month = occ.month;
+ change.day = occ.day;
+ change.hour = occ.hour;
+ change.minute = occ.minute;
+ change.second = occ.second;
+ change.isDate = occ.isDate;
+ Timezone.adjust_change(change, 0, 0, 0, -change.prevUtcOffset);
+ changes.push(change);
+ }
+ }
+ }
+ return changes;
+ }
+
+ /**
+ * The string representation of this timezone.
+ * @return {String}
+ */
+ }, {
+ key: "toString",
+ value: function toString() {
+ return this.tznames ? this.tznames : this.tzid;
+ }
+ }], [{
+ key: "_compare_change_fn",
+ value: function _compare_change_fn(a, b) {
+ if (a.year < b.year) return -1;else if (a.year > b.year) return 1;
+ if (a.month < b.month) return -1;else if (a.month > b.month) return 1;
+ if (a.day < b.day) return -1;else if (a.day > b.day) return 1;
+ if (a.hour < b.hour) return -1;else if (a.hour > b.hour) return 1;
+ if (a.minute < b.minute) return -1;else if (a.minute > b.minute) return 1;
+ if (a.second < b.second) return -1;else if (a.second > b.second) return 1;
+ return 0;
+ }
+
+ /**
+ * Convert the date/time from one zone to the next.
+ *
+ * @param {Time} tt The time to convert
+ * @param {Timezone} from_zone The source zone to convert from
+ * @param {Timezone} to_zone The target zone to convert to
+ * @return {Time} The converted date/time object
+ */
+ }, {
+ key: "convert_time",
+ value: function convert_time(tt, from_zone, to_zone) {
+ if (tt.isDate || from_zone.tzid == to_zone.tzid || from_zone == Timezone.localTimezone || to_zone == Timezone.localTimezone) {
+ tt.zone = to_zone;
+ return tt;
+ }
+ var utcOffset = from_zone.utcOffset(tt);
+ tt.adjust(0, 0, 0, -utcOffset);
+ utcOffset = to_zone.utcOffset(tt);
+ tt.adjust(0, 0, 0, utcOffset);
+ return null;
+ }
+
+ /**
+ * Creates a new ICAL.Timezone instance from the passed data object.
+ *
+ * @param {Component|Object} aData options for class
+ * @param {String|Component} aData.component
+ * If aData is a simple object, then this member can be set to either a
+ * string containing the component data, or an already parsed
+ * ICAL.Component
+ * @param {String} aData.tzid The timezone identifier
+ * @param {String} aData.location The timezone locationw
+ * @param {String} aData.tznames An alternative string representation of the
+ * timezone
+ * @param {Number} aData.latitude The latitude of the timezone
+ * @param {Number} aData.longitude The longitude of the timezone
+ */
+ }, {
+ key: "fromData",
+ value: function fromData(aData) {
+ var tt = new Timezone();
+ return tt.fromData(aData);
+ }
+
+ /**
+ * The instance describing the UTC timezone
+ * @type {Timezone}
+ * @constant
+ * @instance
+ */
+ }, {
+ key: "utcTimezone",
+ get: function get() {
+ if (!_assertClassBrand(Timezone, this, _utcTimezone)._) {
+ _utcTimezone._ = _assertClassBrand(Timezone, this, Timezone.fromData({
+ tzid: "UTC"
+ }));
+ }
+ return _assertClassBrand(Timezone, this, _utcTimezone)._;
+ }
+
+ /**
+ * The instance describing the local timezone
+ * @type {Timezone}
+ * @constant
+ * @instance
+ */
+ }, {
+ key: "localTimezone",
+ get: function get() {
+ if (!_assertClassBrand(Timezone, this, _localTimezone)._) {
+ _localTimezone._ = _assertClassBrand(Timezone, this, Timezone.fromData({
+ tzid: "floating"
+ }));
+ }
+ return _assertClassBrand(Timezone, this, _localTimezone)._;
+ }
+
+ /**
+ * Adjust a timezone change object.
+ * @private
+ * @param {Object} change The timezone change object
+ * @param {Number} days The extra amount of days
+ * @param {Number} hours The extra amount of hours
+ * @param {Number} minutes The extra amount of minutes
+ * @param {Number} seconds The extra amount of seconds
+ */
+ }, {
+ key: "adjust_change",
+ value: function adjust_change(change, days, hours, minutes, seconds) {
+ return Time.prototype.adjust.call(change, days, hours, minutes, seconds, change);
+ }
+ }]);
+ }();
+ var _utcTimezone = {
+ _: null
+ };
+ var _localTimezone = {
+ _: null
+ };
+ _defineProperty(Timezone, "_minimumExpansionYear", -1);
+ _defineProperty(Timezone, "EXTRA_COVERAGE", 5);
+
+ /* 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 */
+
+ var zones = null;
+
+ /**
+ * @classdesc
+ * Singleton class to contain timezones. Right now it is all manual registry in
+ * the future we may use this class to download timezone information or handle
+ * loading pre-expanded timezones.
+ *
+ * @exports module:ICAL.TimezoneService
+ * @memberof ICAL
+ */
+ var TimezoneService = {
+ get count() {
+ if (zones === null) {
+ return 0;
+ }
+ return Object.keys(zones).length;
+ },
+ reset: function reset() {
+ zones = Object.create(null);
+ var utc = Timezone.utcTimezone;
+ zones.Z = utc;
+ zones.UTC = utc;
+ zones.GMT = utc;
+ },
+ _hard_reset: function _hard_reset() {
+ zones = null;
+ },
+ /**
+ * Checks if timezone id has been registered.
+ *
+ * @param {String} tzid Timezone identifier (e.g. America/Los_Angeles)
+ * @return {Boolean} False, when not present
+ */
+ has: function has(tzid) {
+ if (zones === null) {
+ return false;
+ }
+ return !!zones[tzid];
+ },
+ /**
+ * Returns a timezone by its tzid if present.
+ *
+ * @param {String} tzid Timezone identifier (e.g. America/Los_Angeles)
+ * @return {Timezone | undefined} The timezone, or undefined if not found
+ */
+ get: function get(tzid) {
+ if (zones === null) {
+ this.reset();
+ }
+ return zones[tzid];
+ },
+ /**
+ * Registers a timezone object or component.
+ *
+ * @param {Component|Timezone} timezone
+ * The initialized zone or vtimezone.
+ *
+ * @param {String=} name
+ * The name of the timezone. Defaults to the component's TZID if not
+ * passed.
+ */
+ register: function register(timezone, name) {
+ if (zones === null) {
+ this.reset();
+ }
+
+ // This avoids a breaking change by the change of argument order
+ // TODO remove in v3
+ if (typeof timezone === "string" && name instanceof Timezone) {
+ var _ref = [name, timezone];
+ timezone = _ref[0];
+ name = _ref[1];
+ }
+ if (!name) {
+ if (timezone instanceof Timezone) {
+ name = timezone.tzid;
+ } else {
+ if (timezone.name === 'vtimezone') {
+ timezone = new Timezone(timezone);
+ name = timezone.tzid;
+ }
+ }
+ }
+ if (!name) {
+ throw new TypeError("Neither a timezone nor a name was passed");
+ }
+ if (timezone instanceof Timezone) {
+ zones[name] = timezone;
+ } else {
+ throw new TypeError('timezone must be ICAL.Timezone or ICAL.Component');
+ }
+ },
+ /**
+ * Removes a timezone by its tzid from the list.
+ *
+ * @param {String} tzid Timezone identifier (e.g. America/Los_Angeles)
+ * @return {?Timezone} The removed timezone, or null if not registered
+ */
+ remove: function remove(tzid) {
+ if (zones === null) {
+ return null;
+ }
+ return delete zones[tzid];
+ }
+ };
+
+ /**
+ * Helper functions used in various places within ical.js
+ * @module ICAL.helpers
+ */
+
+ /**
+ * Compiles a list of all referenced TZIDs in all subcomponents and
+ * removes any extra VTIMEZONE subcomponents. In addition, if any TZIDs
+ * are referenced by a component, but a VTIMEZONE does not exist,
+ * an attempt will be made to generate a VTIMEZONE using ICAL.TimezoneService.
+ *
+ * @param {Component} vcal The top-level VCALENDAR component.
+ * @return {Component} The ICAL.Component that was passed in.
+ */
+ function updateTimezones(vcal) {
+ var allsubs, properties, vtimezones, reqTzid, i;
+ if (!vcal || vcal.name !== "vcalendar") {
+ //not a top-level vcalendar component
+ return vcal;
+ }
+
+ //Store vtimezone subcomponents in an object reference by tzid.
+ //Store properties from everything else in another array
+ allsubs = vcal.getAllSubcomponents();
+ properties = [];
+ vtimezones = {};
+ for (i = 0; i < allsubs.length; i++) {
+ if (allsubs[i].name === "vtimezone") {
+ var tzid = allsubs[i].getFirstProperty("tzid").getFirstValue();
+ vtimezones[tzid] = allsubs[i];
+ } else {
+ properties = properties.concat(allsubs[i].getAllProperties());
+ }
+ }
+
+ //create an object with one entry for each required tz
+ reqTzid = {};
+ for (i = 0; i < properties.length; i++) {
+ var _tzid = properties[i].getParameter("tzid");
+ if (_tzid) {
+ reqTzid[_tzid] = true;
+ }
+ }
+
+ //delete any vtimezones that are not on the reqTzid list.
+ for (var _i = 0, _Object$entries = Object.entries(vtimezones); _i < _Object$entries.length; _i++) {
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
+ _tzid2 = _Object$entries$_i[0],
+ comp = _Object$entries$_i[1];
+ if (!reqTzid[_tzid2]) {
+ vcal.removeSubcomponent(comp);
+ }
+ }
+
+ //create any missing, but registered timezones
+ for (var _i2 = 0, _Object$keys = Object.keys(reqTzid); _i2 < _Object$keys.length; _i2++) {
+ var _tzid3 = _Object$keys[_i2];
+ if (!vtimezones[_tzid3] && TimezoneService.has(_tzid3)) {
+ vcal.addSubcomponent(TimezoneService.get(_tzid3).component);
+ }
+ }
+ return vcal;
+ }
+
+ /**
+ * Checks if the given type is of the number type and also NaN.
+ *
+ * @param {Number} number The number to check
+ * @return {Boolean} True, if the number is strictly NaN
+ */
+ function isStrictlyNaN(number) {
+ return typeof number === 'number' && isNaN(number);
+ }
+
+ /**
+ * Parses a string value that is expected to be an integer, when the valid is
+ * not an integer throws a decoration error.
+ *
+ * @param {String} string Raw string input
+ * @return {Number} Parsed integer
+ */
+ function strictParseInt(string) {
+ var result = parseInt(string, 10);
+ if (isStrictlyNaN(result)) {
+ throw new Error('Could not extract integer from "' + string + '"');
+ }
+ return result;
+ }
+
+ /**
+ * Creates or returns a class instance of a given type with the initialization
+ * data if the data is not already an instance of the given type.
+ *
+ * @example
+ * var time = new ICAL.Time(...);
+ * var result = ICAL.helpers.formatClassType(time, ICAL.Time);
+ *
+ * (result instanceof ICAL.Time)
+ * // => true
+ *
+ * result = ICAL.helpers.formatClassType({}, ICAL.Time);
+ * (result isntanceof ICAL.Time)
+ * // => true
+ *
+ *
+ * @param {Object} data object initialization data
+ * @param {Object} type object type (like ICAL.Time)
+ * @return {?} An instance of the found type.
+ */
+ function formatClassType(data, type) {
+ if (typeof data === 'undefined') {
+ return undefined;
+ }
+ if (data instanceof type) {
+ return data;
+ }
+ return new type(data);
+ }
+
+ /**
+ * Identical to indexOf but will only match values when they are not preceded
+ * by a backslash character.
+ *
+ * @param {String} buffer String to search
+ * @param {String} search Value to look for
+ * @param {Number} pos Start position
+ * @return {Number} The position, or -1 if not found
+ */
+ function unescapedIndexOf(buffer, search, pos) {
+ while ((pos = buffer.indexOf(search, pos)) !== -1) {
+ if (pos > 0 && buffer[pos - 1] === '\\') {
+ pos += 1;
+ } else {
+ return pos;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Find the index for insertion using binary search.
+ *
+ * @param {Array} list The list to search
+ * @param {?} seekVal The value to insert
+ * @param {function(?,?)} cmpfunc The comparison func, that can
+ * compare two seekVals
+ * @return {Number} The insert position
+ */
+ function binsearchInsert(list, seekVal, cmpfunc) {
+ if (!list.length) return 0;
+ var low = 0,
+ high = list.length - 1,
+ mid,
+ cmpval;
+ while (low <= high) {
+ mid = low + Math.floor((high - low) / 2);
+ cmpval = cmpfunc(seekVal, list[mid]);
+ if (cmpval < 0) high = mid - 1;else if (cmpval > 0) low = mid + 1;else break;
+ }
+ if (cmpval < 0) return mid; // insertion is displacing, so use mid outright.
+ else if (cmpval > 0) return mid + 1;else return mid;
+ }
+
+ /**
+ * Clone the passed object or primitive. By default a shallow clone will be
+ * executed.
+ *
+ * @param {*} aSrc The thing to clone
+ * @param {Boolean=} aDeep If true, a deep clone will be performed
+ * @return {*} The copy of the thing
+ */
+ function clone(aSrc, aDeep) {
+ if (!aSrc || _typeof(aSrc) != "object") {
+ return aSrc;
+ } else if (aSrc instanceof Date) {
+ return new Date(aSrc.getTime());
+ } else if ("clone" in aSrc) {
+ return aSrc.clone();
+ } else if (Array.isArray(aSrc)) {
+ var arr = [];
+ for (var i = 0; i < aSrc.length; i++) {
+ arr.push(aDeep ? clone(aSrc[i], true) : aSrc[i]);
+ }
+ return arr;
+ } else {
+ var obj = {};
+ for (var _i3 = 0, _Object$entries2 = Object.entries(aSrc); _i3 < _Object$entries2.length; _i3++) {
+ var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i3], 2),
+ name = _Object$entries2$_i[0],
+ value = _Object$entries2$_i[1];
+ if (aDeep) {
+ obj[name] = clone(value, true);
+ } else {
+ obj[name] = value;
+ }
+ }
+ return obj;
+ }
+ }
+
+ /**
+ * Performs iCalendar line folding. A line ending character is inserted and
+ * the next line begins with a whitespace.
+ *
+ * @example
+ * SUMMARY:This line will be fold
+ * ed right in the middle of a word.
+ *
+ * @param {String} aLine The line to fold
+ * @return {String} The folded line
+ */
+ function foldline(aLine) {
+ var result = "";
+ var line = aLine || "",
+ pos = 0,
+ line_length = 0;
+ //pos counts position in line for the UTF-16 presentation
+ //line_length counts the bytes for the UTF-8 presentation
+ while (line.length) {
+ var cp = line.codePointAt(pos);
+ if (cp < 128) ++line_length;else if (cp < 2048) line_length += 2; //needs 2 UTF-8 bytes
+ else if (cp < 65536) line_length += 3;else line_length += 4; //cp is less than 1114112
+ if (line_length < ICALmodule.foldLength + 1) pos += cp > 65535 ? 2 : 1;else {
+ result += ICALmodule.newLineChar + " " + line.slice(0, Math.max(0, pos));
+ line = line.slice(Math.max(0, pos));
+ pos = line_length = 0;
+ }
+ }
+ return result.slice(ICALmodule.newLineChar.length + 1);
+ }
+
+ /**
+ * Pads the given string or number with zeros so it will have at least two
+ * characters.
+ *
+ * @param {String|Number} data The string or number to pad
+ * @return {String} The number padded as a string
+ */
+ function pad2(data) {
+ if (typeof data !== 'string') {
+ // handle fractions.
+ if (typeof data === 'number') {
+ data = parseInt(data);
+ }
+ data = String(data);
+ }
+ var len = data.length;
+ switch (len) {
+ case 0:
+ return '00';
+ case 1:
+ return '0' + data;
+ default:
+ return data;
+ }
+ }
+
+ /**
+ * Truncates the given number, correctly handling negative numbers.
+ *
+ * @param {Number} number The number to truncate
+ * @return {Number} The truncated number
+ */
+ function trunc(number) {
+ return number < 0 ? Math.ceil(number) : Math.floor(number);
+ }
+
+ /**
+ * Poor-man's cross-browser object extension. Doesn't support all the
+ * features, but enough for our usage. Note that the target's properties are
+ * not overwritten with the source properties.
+ *
+ * @example
+ * var child = ICAL.helpers.extend(parent, {
+ * "bar": 123
+ * });
+ *
+ * @param {Object} source The object to extend
+ * @param {Object} target The object to extend with
+ * @return {Object} Returns the target.
+ */
+ function extend(source, target) {
+ for (var key in source) {
+ var descr = Object.getOwnPropertyDescriptor(source, key);
+ if (descr && !Object.getOwnPropertyDescriptor(target, key)) {
+ Object.defineProperty(target, key, descr);
+ }
+ }
+ return target;
+ }
+
+ var helpers = /*#__PURE__*/Object.freeze({
+ __proto__: null,
+ binsearchInsert: binsearchInsert,
+ clone: clone,
+ extend: extend,
+ foldline: foldline,
+ formatClassType: formatClassType,
+ isStrictlyNaN: isStrictlyNaN,
+ pad2: pad2,
+ strictParseInt: strictParseInt,
+ trunc: trunc,
+ unescapedIndexOf: unescapedIndexOf,
+ updateTimezones: updateTimezones
+ });
+
+ /**
+ * This class represents the "utc-offset" value type, with various calculation and manipulation
+ * methods.
+ *
+ * @memberof ICAL
+ */
+ var UtcOffset = /*#__PURE__*/function () {
+ /**
+ * Creates a new ICAL.UtcOffset instance.
+ *
+ * @param {Object} aData An object with members of the utc offset
+ * @param {Number=} aData.hours The hours for the utc offset
+ * @param {Number=} aData.minutes The minutes in the utc offset
+ * @param {Number=} aData.factor The factor for the utc-offset, either -1 or 1
+ */
+ function UtcOffset(aData) {
+ _classCallCheck(this, UtcOffset);
+ /**
+ * The hours in the utc-offset
+ * @type {Number}
+ */
+ _defineProperty(this, "hours", 0);
+ /**
+ * The minutes in the utc-offset
+ * @type {Number}
+ */
+ _defineProperty(this, "minutes", 0);
+ /**
+ * The sign of the utc offset, 1 for positive offset, -1 for negative
+ * offsets.
+ * @type {Number}
+ */
+ _defineProperty(this, "factor", 1);
+ /**
+ * The type name, to be used in the jCal object.
+ * @constant
+ * @type {String}
+ * @default "utc-offset"
+ */
+ _defineProperty(this, "icaltype", "utc-offset");
+ this.fromData(aData);
+ }
+ return _createClass(UtcOffset, [{
+ key: "clone",
+ value:
+ /**
+ * Returns a clone of the utc offset object.
+ *
+ * @return {UtcOffset} The cloned object
+ */
+ function clone() {
+ return UtcOffset.fromSeconds(this.toSeconds());
+ }
+
+ /**
+ * Sets up the current instance using members from the passed data object.
+ *
+ * @param {Object} aData An object with members of the utc offset
+ * @param {Number=} aData.hours The hours for the utc offset
+ * @param {Number=} aData.minutes The minutes in the utc offset
+ * @param {Number=} aData.factor The factor for the utc-offset, either -1 or 1
+ */
+ }, {
+ key: "fromData",
+ value: function fromData(aData) {
+ if (aData) {
+ for (var _i = 0, _Object$entries = Object.entries(aData); _i < _Object$entries.length; _i++) {
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
+ key = _Object$entries$_i[0],
+ value = _Object$entries$_i[1];
+ this[key] = value;
+ }
+ }
+ this._normalize();
+ }
+
+ /**
+ * Sets up the current instance from the given seconds value. The seconds
+ * value is truncated to the minute. Offsets are wrapped when the world
+ * ends, the hour after UTC+14:00 is UTC-12:00.
+ *
+ * @param {Number} aSeconds The seconds to convert into an offset
+ */
+ }, {
+ key: "fromSeconds",
+ value: function fromSeconds(aSeconds) {
+ var secs = Math.abs(aSeconds);
+ this.factor = aSeconds < 0 ? -1 : 1;
+ this.hours = trunc(secs / 3600);
+ secs -= this.hours * 3600;
+ this.minutes = trunc(secs / 60);
+ return this;
+ }
+
+ /**
+ * Convert the current offset to a value in seconds
+ *
+ * @return {Number} The offset in seconds
+ */
+ }, {
+ key: "toSeconds",
+ value: function toSeconds() {
+ return this.factor * (60 * this.minutes + 3600 * this.hours);
+ }
+
+ /**
+ * Compare this utc offset with another one.
+ *
+ * @param {UtcOffset} other The other offset to compare with
+ * @return {Number} -1, 0 or 1 for less/equal/greater
+ */
+ }, {
+ key: "compare",
+ value: function compare(other) {
+ var a = this.toSeconds();
+ var b = other.toSeconds();
+ return (a > b) - (b > a);
+ }
+ }, {
+ key: "_normalize",
+ value: function _normalize() {
+ // Range: 97200 seconds (with 1 hour inbetween)
+ var secs = this.toSeconds();
+ var factor = this.factor;
+ while (secs < -43200) {
+ // = UTC-12:00
+ secs += 97200;
+ }
+ while (secs > 50400) {
+ // = UTC+14:00
+ secs -= 97200;
+ }
+ this.fromSeconds(secs);
+
+ // Avoid changing the factor when on zero seconds
+ if (secs == 0) {
+ this.factor = factor;
+ }
+ }
+
+ /**
+ * The iCalendar string representation of this utc-offset.
+ * @return {String}
+ */
+ }, {
+ key: "toICALString",
+ value: function toICALString() {
+ return design.icalendar.value['utc-offset'].toICAL(this.toString());
+ }
+
+ /**
+ * The string representation of this utc-offset.
+ * @return {String}
+ */
+ }, {
+ key: "toString",
+ value: function toString() {
+ return (this.factor == 1 ? "+" : "-") + pad2(this.hours) + ':' + pad2(this.minutes);
+ }
+ }], [{
+ key: "fromString",
+ value:
+ /**
+ * Creates a new {@link ICAL.UtcOffset} instance from the passed string.
+ *
+ * @param {String} aString The string to parse
+ * @return {Duration} The created utc-offset instance
+ */
+ function fromString(aString) {
+ // -05:00
+ var options = {};
+ //TODO: support seconds per rfc5545 ?
+ options.factor = aString[0] === '+' ? 1 : -1;
+ options.hours = strictParseInt(aString.slice(1, 3));
+ options.minutes = strictParseInt(aString.slice(4, 6));
+ return new UtcOffset(options);
+ }
+
+ /**
+ * Creates a new {@link ICAL.UtcOffset} instance from the passed seconds
+ * value.
+ *
+ * @param {Number} aSeconds The number of seconds to convert
+ */
+ }, {
+ key: "fromSeconds",
+ value: function fromSeconds(aSeconds) {
+ var instance = new UtcOffset();
+ instance.fromSeconds(aSeconds);
+ return instance;
+ }
+ }]);
+ }();
+
+ /**
+ * Describes a vCard time, which has slight differences to the ICAL.Time.
+ * Properties can be null if not specified, for example for dates with
+ * reduced accuracy or truncation.
+ *
+ * Note that currently not all methods are correctly re-implemented for
+ * VCardTime. For example, comparison will have undefined results when some
+ * members are null.
+ *
+ * Also, normalization is not yet implemented for this class!
+ *
+ * @memberof ICAL
+ * @extends {Time}
+ */
+ var VCardTime = /*#__PURE__*/function (_Time) {
+ /**
+ * Creates a new ICAL.VCardTime instance.
+ *
+ * @param {Object} data The data for the time instance
+ * @param {Number=} data.year The year for this date
+ * @param {Number=} data.month The month for this date
+ * @param {Number=} data.day The day for this date
+ * @param {Number=} data.hour The hour for this date
+ * @param {Number=} data.minute The minute for this date
+ * @param {Number=} data.second The second for this date
+ * @param {Timezone|UtcOffset} zone The timezone to use
+ * @param {String} icaltype The type for this date/time object
+ */
+ function VCardTime(data, zone, icaltype) {
+ var _this;
+ _classCallCheck(this, VCardTime);
+ _this = _callSuper(this, VCardTime, [data, zone]);
+ /**
+ * The class identifier.
+ * @constant
+ * @type {String}
+ * @default "vcardtime"
+ */
+ _defineProperty(_this, "icalclass", "vcardtime");
+ /**
+ * The type name, to be used in the jCal object.
+ * @type {String}
+ * @default "date-and-or-time"
+ */
+ _defineProperty(_this, "icaltype", "date-and-or-time");
+ _this.icaltype = icaltype || "date-and-or-time";
+ return _this;
+ }
+ _inherits(VCardTime, _Time);
+ return _createClass(VCardTime, [{
+ key: "clone",
+ value:
+ /**
+ * Returns a clone of the vcard date/time object.
+ *
+ * @return {VCardTime} The cloned object
+ */
+ function clone() {
+ return new VCardTime(this._time, this.zone, this.icaltype);
+ }
+ }, {
+ key: "_normalize",
+ value: function _normalize() {
+ return this;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ }, {
+ key: "utcOffset",
+ value: function utcOffset() {
+ if (this.zone instanceof UtcOffset) {
+ return this.zone.toSeconds();
+ } else {
+ return Time.prototype.utcOffset.apply(this, arguments);
+ }
+ }
+
+ /**
+ * Returns an RFC 6350 compliant representation of this object.
+ *
+ * @return {String} vcard date/time string
+ */
+ }, {
+ key: "toICALString",
+ value: function toICALString() {
+ return design.vcard.value[this.icaltype].toICAL(this.toString());
+ }
+
+ /**
+ * The string representation of this date/time, in jCard form
+ * (including : and - separators).
+ * @return {String}
+ */
+ }, {
+ key: "toString",
+ value: function toString() {
+ var y = this.year,
+ m = this.month,
+ d = this.day;
+ var h = this.hour,
+ mm = this.minute,
+ s = this.second;
+ var hasYear = y !== null,
+ hasMonth = m !== null,
+ hasDay = d !== null;
+ var hasHour = h !== null,
+ hasMinute = mm !== null,
+ hasSecond = s !== null;
+ var datepart = (hasYear ? pad2(y) + (hasMonth || hasDay ? '-' : '') : hasMonth || hasDay ? '--' : '') + (hasMonth ? pad2(m) : '') + (hasDay ? '-' + pad2(d) : '');
+ var timepart = (hasHour ? pad2(h) : '-') + (hasHour && hasMinute ? ':' : '') + (hasMinute ? pad2(mm) : '') + (!hasHour && !hasMinute ? '-' : '') + (hasMinute && hasSecond ? ':' : '') + (hasSecond ? pad2(s) : '');
+ var zone;
+ if (this.zone === Timezone.utcTimezone) {
+ zone = 'Z';
+ } else if (this.zone instanceof UtcOffset) {
+ zone = this.zone.toString();
+ } else if (this.zone === Timezone.localTimezone) {
+ zone = '';
+ } else if (this.zone instanceof Timezone) {
+ var offset = UtcOffset.fromSeconds(this.zone.utcOffset(this));
+ zone = offset.toString();
+ } else {
+ zone = '';
+ }
+ switch (this.icaltype) {
+ case "time":
+ return timepart + zone;
+ case "date-and-or-time":
+ case "date-time":
+ return datepart + (timepart == '--' ? '' : 'T' + timepart + zone);
+ case "date":
+ return datepart;
+ }
+ return null;
+ }
+ }], [{
+ key: "fromDateAndOrTimeString",
+ value:
+ /**
+ * Returns a new ICAL.VCardTime instance from a date and/or time string.
+ *
+ * @param {String} aValue The string to create from
+ * @param {String} aIcalType The type for this instance, e.g. date-and-or-time
+ * @return {VCardTime} The date/time instance
+ */
+ function fromDateAndOrTimeString(aValue, aIcalType) {
+ function part(v, s, e) {
+ return v ? strictParseInt(v.slice(s, s + e)) : null;
+ }
+ var parts = aValue.split('T');
+ var dt = parts[0],
+ tmz = parts[1];
+ var splitzone = tmz ? design.vcard.value.time._splitZone(tmz) : [];
+ var zone = splitzone[0],
+ tm = splitzone[1];
+ var dtlen = dt ? dt.length : 0;
+ var tmlen = tm ? tm.length : 0;
+ var hasDashDate = dt && dt[0] == '-' && dt[1] == '-';
+ var hasDashTime = tm && tm[0] == '-';
+ var o = {
+ year: hasDashDate ? null : part(dt, 0, 4),
+ month: hasDashDate && (dtlen == 4 || dtlen == 7) ? part(dt, 2, 2) : dtlen == 7 ? part(dt, 5, 2) : dtlen == 10 ? part(dt, 5, 2) : null,
+ day: dtlen == 5 ? part(dt, 3, 2) : dtlen == 7 && hasDashDate ? part(dt, 5, 2) : dtlen == 10 ? part(dt, 8, 2) : null,
+ hour: hasDashTime ? null : part(tm, 0, 2),
+ minute: hasDashTime && tmlen == 3 ? part(tm, 1, 2) : tmlen > 4 ? hasDashTime ? part(tm, 1, 2) : part(tm, 3, 2) : null,
+ second: tmlen == 4 ? part(tm, 2, 2) : tmlen == 6 ? part(tm, 4, 2) : tmlen == 8 ? part(tm, 6, 2) : null
+ };
+ if (zone == 'Z') {
+ zone = Timezone.utcTimezone;
+ } else if (zone && zone[3] == ':') {
+ zone = UtcOffset.fromString(zone);
+ } else {
+ zone = null;
+ }
+ return new VCardTime(o, zone, aIcalType);
+ }
+ }]);
+ }(Time);
+
+ /**
+ * 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").weekDay} weekDay
+ * Imports the 'weekDay' type from the "types.js" module
+ */
+
+ /**
+ * An iterator for a single recurrence rule. This class usually doesn't have to be instanciated
+ * directly, the convenience method {@link ICAL.Recur#iterator} can be used.
+ *
+ * @memberof ICAL
+ */
+ var RecurIterator = /*#__PURE__*/function () {
+ /**
+ * Creates a new ICAL.RecurIterator instance. The options object may contain additional members
+ * when resuming iteration from a previous run.
+ *
+ * @param {Object} options The iterator options
+ * @param {Recur} options.rule The rule to iterate.
+ * @param {Time} options.dtstart The start date of the event.
+ * @param {Boolean=} options.initialized When true, assume that options are
+ * from a previously constructed iterator. Initialization will not be
+ * repeated.
+ */
+ function RecurIterator(options) {
+ _classCallCheck(this, RecurIterator);
+ /**
+ * True when iteration is finished.
+ * @type {Boolean}
+ */
+ _defineProperty(this, "completed", false);
+ /**
+ * The rule that is being iterated
+ * @type {Recur}
+ */
+ _defineProperty(this, "rule", null);
+ /**
+ * The start date of the event being iterated.
+ * @type {Time}
+ */
+ _defineProperty(this, "dtstart", null);
+ /**
+ * The last occurrence that was returned from the
+ * {@link RecurIterator#next} method.
+ * @type {Time}
+ */
+ _defineProperty(this, "last", null);
+ /**
+ * The sequence number from the occurrence
+ * @type {Number}
+ */
+ _defineProperty(this, "occurrence_number", 0);
+ /**
+ * The indices used for the {@link ICAL.RecurIterator#by_data} object.
+ * @type {Object}
+ * @private
+ */
+ _defineProperty(this, "by_indices", null);
+ /**
+ * If true, the iterator has already been initialized
+ * @type {Boolean}
+ * @private
+ */
+ _defineProperty(this, "initialized", false);
+ /**
+ * The initializd by-data.
+ * @type {Object}
+ * @private
+ */
+ _defineProperty(this, "by_data", null);
+ /**
+ * The expanded yeardays
+ * @type {Array}
+ * @private
+ */
+ _defineProperty(this, "days", null);
+ /**
+ * The index in the {@link ICAL.RecurIterator#days} array.
+ * @type {Number}
+ * @private
+ */
+ _defineProperty(this, "days_index", 0);
+ this.fromData(options);
+ }
+ return _createClass(RecurIterator, [{
+ key: "fromData",
+ value:
+ /**
+ * Initialize the recurrence iterator from the passed data object. This
+ * method is usually not called directly, you can initialize the iterator
+ * through the constructor.
+ *
+ * @param {Object} options The iterator options
+ * @param {Recur} options.rule The rule to iterate.
+ * @param {Time} options.dtstart The start date of the event.
+ * @param {Boolean=} options.initialized When true, assume that options are
+ * from a previously constructed iterator. Initialization will not be
+ * repeated.
+ */
+ function fromData(options) {
+ this.rule = formatClassType(options.rule, Recur);
+ if (!this.rule) {
+ throw new Error('iterator requires a (ICAL.Recur) rule');
+ }
+ this.dtstart = formatClassType(options.dtstart, Time);
+ if (!this.dtstart) {
+ throw new Error('iterator requires a (ICAL.Time) dtstart');
+ }
+ if (options.by_data) {
+ this.by_data = options.by_data;
+ } else {
+ this.by_data = clone(this.rule.parts, true);
+ }
+ if (options.occurrence_number) this.occurrence_number = options.occurrence_number;
+ this.days = options.days || [];
+ if (options.last) {
+ this.last = formatClassType(options.last, Time);
+ }
+ this.by_indices = options.by_indices;
+ if (!this.by_indices) {
+ this.by_indices = {
+ "BYSECOND": 0,
+ "BYMINUTE": 0,
+ "BYHOUR": 0,
+ "BYDAY": 0,
+ "BYMONTH": 0,
+ "BYWEEKNO": 0,
+ "BYMONTHDAY": 0
+ };
+ }
+ this.initialized = options.initialized || false;
+ if (!this.initialized) {
+ try {
+ this.init();
+ } catch (e) {
+ if (e instanceof InvalidRecurrenceRuleError) {
+ // Init may error if there are no possible recurrence instances from
+ // the rule, but we don't want to bubble this error up. Instead, we
+ // create an empty iterator.
+ this.completed = true;
+ } else {
+ // Propagate other errors to consumers.
+ throw e;
+ }
+ }
+ }
+ }
+
+ /**
+ * Initialize the iterator
+ * @private
+ */
+ }, {
+ key: "init",
+ value: function init() {
+ var _this = this;
+ this.initialized = true;
+ this.last = this.dtstart.clone();
+ var parts = this.by_data;
+ if ("BYDAY" in parts) {
+ // libical does this earlier when the rule is loaded, but we postpone to
+ // now so we can preserve the original order.
+ this.sort_byday_rules(parts.BYDAY);
+ }
+
+ // The BYYEARDAY may only appear with BYDAY
+ if ("BYYEARDAY" in parts) {
+ if ("BYMONTH" in parts || "BYWEEKNO" in parts || "BYMONTHDAY" in parts) {
+ throw new Error("Invalid BYYEARDAY rule");
+ }
+ }
+
+ // BYWEEKNO and BYMONTHDAY rule parts may not both appear
+ if ("BYWEEKNO" in parts && "BYMONTHDAY" in parts) {
+ throw new Error("BYWEEKNO does not fit to BYMONTHDAY");
+ }
+
+ // For MONTHLY recurrences (FREQ=MONTHLY) neither BYYEARDAY nor
+ // BYWEEKNO may appear.
+ if (this.rule.freq == "MONTHLY" && ("BYYEARDAY" in parts || "BYWEEKNO" in parts)) {
+ throw new Error("For MONTHLY recurrences neither BYYEARDAY nor BYWEEKNO may appear");
+ }
+
+ // For WEEKLY recurrences (FREQ=WEEKLY) neither BYMONTHDAY nor
+ // BYYEARDAY may appear.
+ if (this.rule.freq == "WEEKLY" && ("BYYEARDAY" in parts || "BYMONTHDAY" in parts)) {
+ throw new Error("For WEEKLY recurrences neither BYMONTHDAY nor BYYEARDAY may appear");
+ }
+
+ // BYYEARDAY may only appear in YEARLY rules
+ if (this.rule.freq != "YEARLY" && "BYYEARDAY" in parts) {
+ throw new Error("BYYEARDAY may only appear in YEARLY rules");
+ }
+ this.last.second = this.setup_defaults("BYSECOND", "SECONDLY", this.dtstart.second);
+ this.last.minute = this.setup_defaults("BYMINUTE", "MINUTELY", this.dtstart.minute);
+ this.last.hour = this.setup_defaults("BYHOUR", "HOURLY", this.dtstart.hour);
+ this.last.day = this.setup_defaults("BYMONTHDAY", "DAILY", this.dtstart.day);
+ this.last.month = this.setup_defaults("BYMONTH", "MONTHLY", this.dtstart.month);
+ if (this.rule.freq == "WEEKLY") {
+ if ("BYDAY" in parts) {
+ var _this$ruleDayOfWeek = this.ruleDayOfWeek(parts.BYDAY[0], this.rule.wkst),
+ _this$ruleDayOfWeek2 = _slicedToArray(_this$ruleDayOfWeek, 2),
+ dow = _this$ruleDayOfWeek2[1];
+ var wkdy = dow - this.last.dayOfWeek(this.rule.wkst);
+ if (this.last.dayOfWeek(this.rule.wkst) < dow && wkdy >= 0 || wkdy < 0) {
+ // Initial time is after first day of BYDAY data
+ this.last.day += wkdy;
+ }
+ } else {
+ var dayName = Recur.numericDayToIcalDay(this.dtstart.dayOfWeek());
+ parts.BYDAY = [dayName];
+ }
+ }
+ if (this.rule.freq == "YEARLY") {
+ // Some yearly recurrence rules may be specific enough to not actually
+ // occur on a yearly basis, e.g. the 29th day of February or the fifth
+ // Monday of a given month. The standard isn't clear on the intended
+ // behavior in these cases, but `libical` at least will iterate until it
+ // finds a matching year.
+ // CAREFUL: Some rules may specify an occurrence that can never happen,
+ // e.g. the first Monday of April so long as it falls on the 15th
+ // through the 21st. Detecting these is non-trivial, so ensure that we
+ // stop iterating at some point.
+ var untilYear = this.rule.until ? this.rule.until.year : 20000;
+ while (this.last.year <= untilYear) {
+ this.expand_year_days(this.last.year);
+ if (this.days.length > 0) {
+ break;
+ }
+ this.increment_year(this.rule.interval);
+ }
+ if (this.days.length == 0) {
+ throw new InvalidRecurrenceRuleError();
+ }
+
+ // If there's no occurrence in this year, try the following years. This
+ // would only happen looking for day 366 or -366.
+ if (!this._nextByYearDay() && !this.next_year() && !this.next_year() && !this.next_year()) {
+ // This should not be possible, but just in case it is, stop.
+ throw new InvalidRecurrenceRuleError();
+ }
+ }
+ if (this.rule.freq == "MONTHLY") {
+ if (this.has_by_data("BYDAY")) {
+ var tempLast = null;
+ var initLast = this.last.clone();
+ var daysInMonth = Time.daysInMonth(this.last.month, this.last.year);
+
+ // Check every weekday in BYDAY with relative dow and pos.
+ var _iterator = _createForOfIteratorHelper(this.by_data.BYDAY),
+ _step;
+ try {
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
+ var bydow = _step.value;
+ this.last = initLast.clone();
+ var _this$ruleDayOfWeek3 = this.ruleDayOfWeek(bydow),
+ _this$ruleDayOfWeek4 = _slicedToArray(_this$ruleDayOfWeek3, 2),
+ pos = _this$ruleDayOfWeek4[0],
+ _dow = _this$ruleDayOfWeek4[1];
+ var dayOfMonth = this.last.nthWeekDay(_dow, pos);
+
+ // If |pos| >= 6, the byday is invalid for a monthly rule.
+ if (pos >= 6 || pos <= -6) {
+ throw new Error("Malformed values in BYDAY part");
+ }
+
+ // If a Byday with pos=+/-5 is not in the current month it
+ // must be searched in the next months.
+ if (dayOfMonth > daysInMonth || dayOfMonth <= 0) {
+ // Skip if we have already found a "last" in this month.
+ if (tempLast && tempLast.month == initLast.month) {
+ continue;
+ }
+ while (dayOfMonth > daysInMonth || dayOfMonth <= 0) {
+ this.increment_month();
+ daysInMonth = Time.daysInMonth(this.last.month, this.last.year);
+ dayOfMonth = this.last.nthWeekDay(_dow, pos);
+ }
+ }
+ this.last.day = dayOfMonth;
+ if (!tempLast || this.last.compare(tempLast) < 0) {
+ tempLast = this.last.clone();
+ }
+ }
+ } catch (err) {
+ _iterator.e(err);
+ } finally {
+ _iterator.f();
+ }
+ this.last = tempLast.clone();
+
+ //XXX: This feels like a hack, but we need to initialize
+ // the BYMONTHDAY case correctly and byDayAndMonthDay handles
+ // this case. It accepts a special flag which will avoid incrementing
+ // the initial value without the flag days that match the start time
+ // would be missed.
+ if (this.has_by_data('BYMONTHDAY')) {
+ this._byDayAndMonthDay(true);
+ }
+ if (this.last.day > daysInMonth || this.last.day == 0) {
+ throw new Error("Malformed values in BYDAY part");
+ }
+ } else if (this.has_by_data("BYMONTHDAY")) {
+ // Change the day value so that normalisation won't change the month.
+ this.last.day = 1;
+
+ // Get a sorted list of days in the starting month that match the rule.
+ var normalized = this.normalizeByMonthDayRules(this.last.year, this.last.month, this.rule.parts.BYMONTHDAY).filter(function (d) {
+ return d >= _this.last.day;
+ });
+ if (normalized.length) {
+ // There's at least one valid day, use it.
+ this.last.day = normalized[0];
+ this.by_data.BYMONTHDAY = normalized;
+ } else {
+ // There's no occurrence in this month, find the next valid month.
+ // The longest possible sequence of skipped months is February-April-June,
+ // so we might need to call next_month up to three times.
+ if (!this.next_month() && !this.next_month() && !this.next_month()) {
+ throw new InvalidRecurrenceRuleError();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Retrieve the next occurrence from the iterator.
+ * @return {Time}
+ */
+ }, {
+ key: "next",
+ value: function next() {
+ var again = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
+ var before = this.last ? this.last.clone() : null;
+ if (this.rule.count && this.occurrence_number >= this.rule.count || this.rule.until && this.last.compare(this.rule.until) > 0) {
+ this.completed = true;
+ }
+ if (this.completed) {
+ return null;
+ }
+ if (this.occurrence_number == 0 && this.last.compare(this.dtstart) >= 0) {
+ // First of all, give the instance that was initialized
+ this.occurrence_number++;
+ return this.last;
+ }
+ var valid;
+ var invalid_count = 0;
+ do {
+ valid = 1;
+ switch (this.rule.freq) {
+ case "SECONDLY":
+ this.next_second();
+ break;
+ case "MINUTELY":
+ this.next_minute();
+ break;
+ case "HOURLY":
+ this.next_hour();
+ break;
+ case "DAILY":
+ this.next_day();
+ break;
+ case "WEEKLY":
+ this.next_week();
+ break;
+ case "MONTHLY":
+ valid = this.next_month();
+ if (valid) {
+ invalid_count = 0;
+ } else if (++invalid_count == 336) {
+ // We've been through all 91 month variations and not found a recurrence. Stop.
+ // (12 months and 29-day February × 7 starting days.)
+ this.completed = true;
+ return null;
+ }
+ break;
+ case "YEARLY":
+ valid = this.next_year();
+ if (valid) {
+ invalid_count = 0;
+ } else if (++invalid_count == 28) {
+ // We've been through all 14 year variations and not found a recurrence. Stop.
+ // (365-day and 366-day years × 7 starting days.)
+ this.completed = true;
+ return null;
+ }
+ break;
+ default:
+ return null;
+ }
+ } while (!this.check_contracting_rules() || this.last.compare(this.dtstart) < 0 || !valid);
+ if (this.last.compare(before) == 0) {
+ if (again) {
+ throw new Error("Same occurrence found twice, protecting you from death by recursion");
+ }
+ this.next(true);
+ }
+ if (this.rule.until && this.last.compare(this.rule.until) > 0) {
+ this.completed = true;
+ return null;
+ } else {
+ this.occurrence_number++;
+ return this.last;
+ }
+ }
+ }, {
+ key: "next_second",
+ value: function next_second() {
+ return this.next_generic("BYSECOND", "SECONDLY", "second", "minute");
+ }
+ }, {
+ key: "increment_second",
+ value: function increment_second(inc) {
+ return this.increment_generic(inc, "second", 60, "minute");
+ }
+ }, {
+ key: "next_minute",
+ value: function next_minute() {
+ return this.next_generic("BYMINUTE", "MINUTELY", "minute", "hour", "next_second");
+ }
+ }, {
+ key: "increment_minute",
+ value: function increment_minute(inc) {
+ return this.increment_generic(inc, "minute", 60, "hour");
+ }
+ }, {
+ key: "next_hour",
+ value: function next_hour() {
+ return this.next_generic("BYHOUR", "HOURLY", "hour", "monthday", "next_minute");
+ }
+ }, {
+ key: "increment_hour",
+ value: function increment_hour(inc) {
+ this.increment_generic(inc, "hour", 24, "monthday");
+ }
+ }, {
+ key: "next_day",
+ value: function next_day() {
+ var this_freq = this.rule.freq == "DAILY";
+ if (this.next_hour() == 0) {
+ return 0;
+ }
+ if (this_freq) {
+ this.increment_monthday(this.rule.interval);
+ } else {
+ this.increment_monthday(1);
+ }
+ return 0;
+ }
+ }, {
+ key: "next_week",
+ value: function next_week() {
+ var end_of_data = 0;
+ if (this.next_weekday_by_week() == 0) {
+ return end_of_data;
+ }
+ if (this.has_by_data("BYWEEKNO")) {
+ this.by_indices.BYWEEKNO++;
+ if (this.by_indices.BYWEEKNO == this.by_data.BYWEEKNO.length) {
+ this.by_indices.BYWEEKNO = 0;
+ end_of_data = 1;
+ }
+
+ // HACK should be first month of the year
+ this.last.month = 1;
+ this.last.day = 1;
+ var week_no = this.by_data.BYWEEKNO[this.by_indices.BYWEEKNO];
+ this.last.day += 7 * week_no;
+ if (end_of_data) {
+ this.increment_year(1);
+ }
+ } else {
+ // Jump to the next week
+ this.increment_monthday(7 * this.rule.interval);
+ }
+ return end_of_data;
+ }
+
+ /**
+ * Normalize each by day rule for a given year/month.
+ * Takes into account ordering and negative rules
+ *
+ * @private
+ * @param {Number} year Current year.
+ * @param {Number} month Current month.
+ * @param {Array} rules Array of rules.
+ *
+ * @return {Array} sorted and normalized rules.
+ * Negative rules will be expanded to their
+ * correct positive values for easier processing.
+ */
+ }, {
+ key: "normalizeByMonthDayRules",
+ value: function normalizeByMonthDayRules(year, month, rules) {
+ var daysInMonth = Time.daysInMonth(month, year);
+
+ // XXX: This is probably bad for performance to allocate
+ // a new array for each month we scan, if possible
+ // we should try to optimize this...
+ var newRules = [];
+ var ruleIdx = 0;
+ var len = rules.length;
+ var rule;
+ for (; ruleIdx < len; ruleIdx++) {
+ rule = parseInt(rules[ruleIdx], 10);
+ if (isNaN(rule)) {
+ throw new Error('Invalid BYMONTHDAY value');
+ }
+
+ // if this rule falls outside of given
+ // month discard it.
+ if (Math.abs(rule) > daysInMonth) {
+ continue;
+ }
+
+ // negative case
+ if (rule < 0) {
+ // we add (not subtract it is a negative number)
+ // one from the rule because 1 === last day of month
+ rule = daysInMonth + (rule + 1);
+ } else if (rule === 0) {
+ // skip zero: it is invalid.
+ continue;
+ }
+
+ // only add unique items...
+ if (newRules.indexOf(rule) === -1) {
+ newRules.push(rule);
+ }
+ }
+
+ // unique and sort
+ return newRules.sort(function (a, b) {
+ return a - b;
+ });
+ }
+
+ /**
+ * NOTES:
+ * We are given a list of dates in the month (BYMONTHDAY) (23, etc..)
+ * Also we are given a list of days (BYDAY) (MO, 2SU, etc..) when
+ * both conditions match a given date (this.last.day) iteration stops.
+ *
+ * @private
+ * @param {Boolean=} isInit When given true will not increment the
+ * current day (this.last).
+ */
+ }, {
+ key: "_byDayAndMonthDay",
+ value: function _byDayAndMonthDay(isInit) {
+ var byMonthDay; // setup in initMonth
+ var byDay = this.by_data.BYDAY;
+ var date;
+ var dateIdx = 0;
+ var dateLen; // setup in initMonth
+ var dayLen = byDay.length;
+
+ // we are not valid by default
+ var dataIsValid = 0;
+ var daysInMonth;
+ var self = this;
+ // we need a copy of this, because a DateTime gets normalized
+ // automatically if the day is out of range. At some points we
+ // set the last day to 0 to start counting.
+ var lastDay = this.last.day;
+ function initMonth() {
+ daysInMonth = Time.daysInMonth(self.last.month, self.last.year);
+ byMonthDay = self.normalizeByMonthDayRules(self.last.year, self.last.month, self.by_data.BYMONTHDAY);
+ dateLen = byMonthDay.length;
+
+ // For the case of more than one occurrence in one month
+ // we have to be sure to start searching after the last
+ // found date or at the last BYMONTHDAY, unless we are
+ // initializing the iterator because in this case we have
+ // to consider the last found date too.
+ while (byMonthDay[dateIdx] <= lastDay && !(isInit && byMonthDay[dateIdx] == lastDay) && dateIdx < dateLen - 1) {
+ dateIdx++;
+ }
+ }
+ function nextMonth() {
+ // since the day is incremented at the start
+ // of the loop below, we need to start at 0
+ lastDay = 0;
+ self.increment_month();
+ dateIdx = 0;
+ initMonth();
+ }
+ initMonth();
+
+ // should come after initMonth
+ if (isInit) {
+ lastDay -= 1;
+ }
+
+ // Use a counter to avoid an infinite loop with malformed rules.
+ // Stop checking after 4 years so we consider also a leap year.
+ var monthsCounter = 48;
+ while (!dataIsValid && monthsCounter) {
+ monthsCounter--;
+ // increment the current date. This is really
+ // important otherwise we may fall into the infinite
+ // loop trap. The initial date takes care of the case
+ // where the current date is the date we are looking
+ // for.
+ date = lastDay + 1;
+ if (date > daysInMonth) {
+ nextMonth();
+ continue;
+ }
+
+ // find next date
+ var next = byMonthDay[dateIdx++];
+
+ // this logic is dependent on the BYMONTHDAYS
+ // being in order (which is done by #normalizeByMonthDayRules)
+ if (next >= date) {
+ // if the next month day is in the future jump to it.
+ lastDay = next;
+ } else {
+ // in this case the 'next' monthday has past
+ // we must move to the month.
+ nextMonth();
+ continue;
+ }
+
+ // Now we can loop through the day rules to see
+ // if one matches the current month date.
+ for (var dayIdx = 0; dayIdx < dayLen; dayIdx++) {
+ var parts = this.ruleDayOfWeek(byDay[dayIdx]);
+ var pos = parts[0];
+ var dow = parts[1];
+ this.last.day = lastDay;
+ if (this.last.isNthWeekDay(dow, pos)) {
+ // when we find the valid one we can mark
+ // the conditions as met and break the loop.
+ // (Because we have this condition above
+ // it will also break the parent loop).
+ dataIsValid = 1;
+ break;
+ }
+ }
+
+ // It is completely possible that the combination
+ // cannot be matched in the current month.
+ // When we reach the end of possible combinations
+ // in the current month we iterate to the next one.
+ // since dateIdx is incremented right after getting
+ // "next", we don't need dateLen -1 here.
+ if (!dataIsValid && dateIdx === dateLen) {
+ nextMonth();
+ continue;
+ }
+ }
+ if (monthsCounter <= 0) {
+ // Checked 4 years without finding a Byday that matches
+ // a Bymonthday. Maybe the rule is not correct.
+ throw new Error("Malformed values in BYDAY combined with BYMONTHDAY parts");
+ }
+ return dataIsValid;
+ }
+ }, {
+ key: "next_month",
+ value: function next_month() {
+ var data_valid = 1;
+ if (this.next_hour() == 0) {
+ return data_valid;
+ }
+ if (this.has_by_data("BYDAY") && this.has_by_data("BYMONTHDAY")) {
+ data_valid = this._byDayAndMonthDay();
+ } else if (this.has_by_data("BYDAY")) {
+ var daysInMonth = Time.daysInMonth(this.last.month, this.last.year);
+ var setpos = 0;
+ var setpos_total = 0;
+ if (this.has_by_data("BYSETPOS")) {
+ var last_day = this.last.day;
+ for (var _day = 1; _day <= daysInMonth; _day++) {
+ this.last.day = _day;
+ if (this.is_day_in_byday(this.last)) {
+ setpos_total++;
+ if (_day <= last_day) {
+ setpos++;
+ }
+ }
+ }
+ this.last.day = last_day;
+ }
+ data_valid = 0;
+ var day;
+ for (day = this.last.day + 1; day <= daysInMonth; day++) {
+ this.last.day = day;
+ if (this.is_day_in_byday(this.last)) {
+ if (!this.has_by_data("BYSETPOS") || this.check_set_position(++setpos) || this.check_set_position(setpos - setpos_total - 1)) {
+ data_valid = 1;
+ break;
+ }
+ }
+ }
+ if (day > daysInMonth) {
+ this.last.day = 1;
+ this.increment_month();
+ if (this.is_day_in_byday(this.last)) {
+ if (!this.has_by_data("BYSETPOS") || this.check_set_position(1)) {
+ data_valid = 1;
+ }
+ } else {
+ data_valid = 0;
+ }
+ }
+ } else if (this.has_by_data("BYMONTHDAY")) {
+ this.by_indices.BYMONTHDAY++;
+ if (this.by_indices.BYMONTHDAY >= this.by_data.BYMONTHDAY.length) {
+ this.by_indices.BYMONTHDAY = 0;
+ this.increment_month();
+ if (this.by_indices.BYMONTHDAY >= this.by_data.BYMONTHDAY.length) {
+ return 0;
+ }
+ }
+ var _daysInMonth = Time.daysInMonth(this.last.month, this.last.year);
+ var _day2 = this.by_data.BYMONTHDAY[this.by_indices.BYMONTHDAY];
+ if (_day2 < 0) {
+ _day2 = _daysInMonth + _day2 + 1;
+ }
+ if (_day2 > _daysInMonth) {
+ this.last.day = 1;
+ data_valid = this.is_day_in_byday(this.last);
+ } else {
+ this.last.day = _day2;
+ }
+ } else {
+ this.increment_month();
+ var _daysInMonth2 = Time.daysInMonth(this.last.month, this.last.year);
+ if (this.by_data.BYMONTHDAY[0] > _daysInMonth2) {
+ data_valid = 0;
+ } else {
+ this.last.day = this.by_data.BYMONTHDAY[0];
+ }
+ }
+ return data_valid;
+ }
+ }, {
+ key: "next_weekday_by_week",
+ value: function next_weekday_by_week() {
+ var end_of_data = 0;
+ if (this.next_hour() == 0) {
+ return end_of_data;
+ }
+ if (!this.has_by_data("BYDAY")) {
+ return 1;
+ }
+ for (;;) {
+ var tt = new Time();
+ this.by_indices.BYDAY++;
+ if (this.by_indices.BYDAY == Object.keys(this.by_data.BYDAY).length) {
+ this.by_indices.BYDAY = 0;
+ end_of_data = 1;
+ }
+ var coded_day = this.by_data.BYDAY[this.by_indices.BYDAY];
+ var parts = this.ruleDayOfWeek(coded_day);
+ var dow = parts[1];
+ dow -= this.rule.wkst;
+ if (dow < 0) {
+ dow += 7;
+ }
+ tt.year = this.last.year;
+ tt.month = this.last.month;
+ tt.day = this.last.day;
+ var startOfWeek = tt.startDoyWeek(this.rule.wkst);
+ if (dow + startOfWeek < 1) {
+ // The selected date is in the previous year
+ if (!end_of_data) {
+ continue;
+ }
+ }
+ var next = Time.fromDayOfYear(startOfWeek + dow, this.last.year);
+
+ /**
+ * The normalization horrors below are due to
+ * the fact that when the year/month/day changes
+ * it can effect the other operations that come after.
+ */
+ this.last.year = next.year;
+ this.last.month = next.month;
+ this.last.day = next.day;
+ return end_of_data;
+ }
+ }
+ }, {
+ key: "next_year",
+ value: function next_year() {
+ if (this.next_hour() == 0) {
+ return 0;
+ }
+ if (this.days.length == 0 || ++this.days_index == this.days.length) {
+ this.days_index = 0;
+ this.increment_year(this.rule.interval);
+ if (this.has_by_data("BYMONTHDAY")) {
+ this.by_data.BYMONTHDAY = this.normalizeByMonthDayRules(this.last.year, this.last.month, this.rule.parts.BYMONTHDAY);
+ }
+ this.expand_year_days(this.last.year);
+ if (this.days.length == 0) {
+ return 0;
+ }
+ }
+ return this._nextByYearDay();
+ }
+ }, {
+ key: "_nextByYearDay",
+ value: function _nextByYearDay() {
+ var doy = this.days[this.days_index];
+ var year = this.last.year;
+ if (Math.abs(doy) == 366 && !Time.isLeapYear(this.last.year)) {
+ return 0;
+ }
+ if (doy < 1) {
+ // Time.fromDayOfYear(doy, year) indexes relative to the
+ // start of the given year. That is different from the
+ // semantics of BYYEARDAY where negative indexes are an
+ // offset from the end of the given year.
+ doy += 1;
+ year += 1;
+ }
+ var next = Time.fromDayOfYear(doy, year);
+ this.last.day = next.day;
+ this.last.month = next.month;
+ return 1;
+ }
+
+ /**
+ * @param dow (eg: '1TU', '-1MO')
+ * @param {weekDay=} aWeekStart The week start weekday
+ * @return [pos, numericDow] (eg: [1, 3]) numericDow is relative to aWeekStart
+ */
+ }, {
+ key: "ruleDayOfWeek",
+ value: function ruleDayOfWeek(dow, aWeekStart) {
+ var matches = dow.match(/([+-]?[0-9])?(MO|TU|WE|TH|FR|SA|SU)/);
+ if (matches) {
+ var pos = parseInt(matches[1] || 0, 10);
+ dow = Recur.icalDayToNumericDay(matches[2], aWeekStart);
+ return [pos, dow];
+ } else {
+ return [0, 0];
+ }
+ }
+ }, {
+ key: "next_generic",
+ value: function next_generic(aRuleType, aInterval, aDateAttr, aFollowingAttr, aPreviousIncr) {
+ var has_by_rule = aRuleType in this.by_data;
+ var this_freq = this.rule.freq == aInterval;
+ var end_of_data = 0;
+ if (aPreviousIncr && this[aPreviousIncr]() == 0) {
+ return end_of_data;
+ }
+ if (has_by_rule) {
+ this.by_indices[aRuleType]++;
+ var dta = this.by_data[aRuleType];
+ if (this.by_indices[aRuleType] == dta.length) {
+ this.by_indices[aRuleType] = 0;
+ end_of_data = 1;
+ }
+ this.last[aDateAttr] = dta[this.by_indices[aRuleType]];
+ } else if (this_freq) {
+ this["increment_" + aDateAttr](this.rule.interval);
+ }
+ if (has_by_rule && end_of_data && this_freq) {
+ this["increment_" + aFollowingAttr](1);
+ }
+ return end_of_data;
+ }
+ }, {
+ key: "increment_monthday",
+ value: function increment_monthday(inc) {
+ for (var i = 0; i < inc; i++) {
+ var daysInMonth = Time.daysInMonth(this.last.month, this.last.year);
+ this.last.day++;
+ if (this.last.day > daysInMonth) {
+ this.last.day -= daysInMonth;
+ this.increment_month();
+ }
+ }
+ }
+ }, {
+ key: "increment_month",
+ value: function increment_month() {
+ this.last.day = 1;
+ if (this.has_by_data("BYMONTH")) {
+ this.by_indices.BYMONTH++;
+ if (this.by_indices.BYMONTH == this.by_data.BYMONTH.length) {
+ this.by_indices.BYMONTH = 0;
+ this.increment_year(1);
+ }
+ this.last.month = this.by_data.BYMONTH[this.by_indices.BYMONTH];
+ } else {
+ if (this.rule.freq == "MONTHLY") {
+ this.last.month += this.rule.interval;
+ } else {
+ this.last.month++;
+ }
+ this.last.month--;
+ var years = trunc(this.last.month / 12);
+ this.last.month %= 12;
+ this.last.month++;
+ if (years != 0) {
+ this.increment_year(years);
+ }
+ }
+ if (this.has_by_data("BYMONTHDAY")) {
+ this.by_data.BYMONTHDAY = this.normalizeByMonthDayRules(this.last.year, this.last.month, this.rule.parts.BYMONTHDAY);
+ }
+ }
+ }, {
+ key: "increment_year",
+ value: function increment_year(inc) {
+ // Don't jump into the next month if this.last is Feb 29.
+ this.last.day = 1;
+ this.last.year += inc;
+ }
+ }, {
+ key: "increment_generic",
+ value: function increment_generic(inc, aDateAttr, aFactor, aNextIncrement) {
+ this.last[aDateAttr] += inc;
+ var nextunit = trunc(this.last[aDateAttr] / aFactor);
+ this.last[aDateAttr] %= aFactor;
+ if (nextunit != 0) {
+ this["increment_" + aNextIncrement](nextunit);
+ }
+ }
+ }, {
+ key: "has_by_data",
+ value: function has_by_data(aRuleType) {
+ return aRuleType in this.rule.parts;
+ }
+ }, {
+ key: "expand_year_days",
+ value: function expand_year_days(aYear) {
+ var t = new Time();
+ this.days = [];
+
+ // We need our own copy with a few keys set
+ var parts = {};
+ var rules = ["BYDAY", "BYWEEKNO", "BYMONTHDAY", "BYMONTH", "BYYEARDAY"];
+ for (var _i = 0, _rules = rules; _i < _rules.length; _i++) {
+ var part = _rules[_i];
+ if (part in this.rule.parts) {
+ parts[part] = this.rule.parts[part];
+ }
+ }
+ if ("BYMONTH" in parts && "BYWEEKNO" in parts) {
+ var valid = 1;
+ var validWeeks = {};
+ t.year = aYear;
+ t.isDate = true;
+ for (var monthIdx = 0; monthIdx < this.by_data.BYMONTH.length; monthIdx++) {
+ var month = this.by_data.BYMONTH[monthIdx];
+ t.month = month;
+ t.day = 1;
+ var first_week = t.weekNumber(this.rule.wkst);
+ t.day = Time.daysInMonth(month, aYear);
+ var last_week = t.weekNumber(this.rule.wkst);
+ for (monthIdx = first_week; monthIdx < last_week; monthIdx++) {
+ validWeeks[monthIdx] = 1;
+ }
+ }
+ for (var weekIdx = 0; weekIdx < this.by_data.BYWEEKNO.length && valid; weekIdx++) {
+ var weekno = this.by_data.BYWEEKNO[weekIdx];
+ if (weekno < 52) {
+ valid &= validWeeks[weekIdx];
+ } else {
+ valid = 0;
+ }
+ }
+ if (valid) {
+ delete parts.BYMONTH;
+ } else {
+ delete parts.BYWEEKNO;
+ }
+ }
+ var partCount = Object.keys(parts).length;
+ if (partCount == 0) {
+ var t1 = this.dtstart.clone();
+ t1.year = this.last.year;
+ this.days.push(t1.dayOfYear());
+ } else if (partCount == 1 && "BYMONTH" in parts) {
+ var _iterator2 = _createForOfIteratorHelper(this.by_data.BYMONTH),
+ _step2;
+ try {
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
+ var _month = _step2.value;
+ var t2 = this.dtstart.clone();
+ t2.year = aYear;
+ t2.month = _month;
+ t2.isDate = true;
+ this.days.push(t2.dayOfYear());
+ }
+ } catch (err) {
+ _iterator2.e(err);
+ } finally {
+ _iterator2.f();
+ }
+ } else if (partCount == 1 && "BYMONTHDAY" in parts) {
+ var _iterator3 = _createForOfIteratorHelper(this.by_data.BYMONTHDAY),
+ _step3;
+ try {
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
+ var monthday = _step3.value;
+ var t3 = this.dtstart.clone();
+ if (monthday < 0) {
+ var daysInMonth = Time.daysInMonth(t3.month, aYear);
+ monthday = monthday + daysInMonth + 1;
+ }
+ t3.day = monthday;
+ t3.year = aYear;
+ t3.isDate = true;
+ this.days.push(t3.dayOfYear());
+ }
+ } catch (err) {
+ _iterator3.e(err);
+ } finally {
+ _iterator3.f();
+ }
+ } else if (partCount == 2 && "BYMONTHDAY" in parts && "BYMONTH" in parts) {
+ var _iterator4 = _createForOfIteratorHelper(this.by_data.BYMONTH),
+ _step4;
+ try {
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
+ var _month2 = _step4.value;
+ var _daysInMonth3 = Time.daysInMonth(_month2, aYear);
+ var _iterator5 = _createForOfIteratorHelper(this.by_data.BYMONTHDAY),
+ _step5;
+ try {
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
+ var _monthday = _step5.value;
+ if (_monthday < 0) {
+ _monthday = _monthday + _daysInMonth3 + 1;
+ }
+ t.day = _monthday;
+ t.month = _month2;
+ t.year = aYear;
+ t.isDate = true;
+ this.days.push(t.dayOfYear());
+ }
+ } catch (err) {
+ _iterator5.e(err);
+ } finally {
+ _iterator5.f();
+ }
+ }
+ } catch (err) {
+ _iterator4.e(err);
+ } finally {
+ _iterator4.f();
+ }
+ } else if (partCount == 1 && "BYWEEKNO" in parts) ; else if (partCount == 2 && "BYWEEKNO" in parts && "BYMONTHDAY" in parts) ; else if (partCount == 1 && "BYDAY" in parts) {
+ this.days = this.days.concat(this.expand_by_day(aYear));
+ } else if (partCount == 2 && "BYDAY" in parts && "BYMONTH" in parts) {
+ var _iterator6 = _createForOfIteratorHelper(this.by_data.BYMONTH),
+ _step6;
+ try {
+ for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
+ var _month3 = _step6.value;
+ var _daysInMonth4 = Time.daysInMonth(_month3, aYear);
+ t.year = aYear;
+ t.month = _month3;
+ t.day = 1;
+ t.isDate = true;
+ var first_dow = t.dayOfWeek();
+ var doy_offset = t.dayOfYear() - 1;
+ t.day = _daysInMonth4;
+ var last_dow = t.dayOfWeek();
+ if (this.has_by_data("BYSETPOS")) {
+ var by_month_day = [];
+ for (var day = 1; day <= _daysInMonth4; day++) {
+ t.day = day;
+ if (this.is_day_in_byday(t)) {
+ by_month_day.push(day);
+ }
+ }
+ for (var spIndex = 0; spIndex < by_month_day.length; spIndex++) {
+ if (this.check_set_position(spIndex + 1) || this.check_set_position(spIndex - by_month_day.length)) {
+ this.days.push(doy_offset + by_month_day[spIndex]);
+ }
+ }
+ } else {
+ var _iterator7 = _createForOfIteratorHelper(this.by_data.BYDAY),
+ _step7;
+ try {
+ for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
+ var coded_day = _step7.value;
+ var bydayParts = this.ruleDayOfWeek(coded_day);
+ var pos = bydayParts[0];
+ var dow = bydayParts[1];
+ var month_day = void 0;
+ var first_matching_day = (dow + 7 - first_dow) % 7 + 1;
+ var last_matching_day = _daysInMonth4 - (last_dow + 7 - dow) % 7;
+ if (pos == 0) {
+ for (var _day3 = first_matching_day; _day3 <= _daysInMonth4; _day3 += 7) {
+ this.days.push(doy_offset + _day3);
+ }
+ } else if (pos > 0) {
+ month_day = first_matching_day + (pos - 1) * 7;
+ if (month_day <= _daysInMonth4) {
+ this.days.push(doy_offset + month_day);
+ }
+ } else {
+ month_day = last_matching_day + (pos + 1) * 7;
+ if (month_day > 0) {
+ this.days.push(doy_offset + month_day);
+ }
+ }
+ }
+ } catch (err) {
+ _iterator7.e(err);
+ } finally {
+ _iterator7.f();
+ }
+ }
+ }
+ // Return dates in order of occurrence (1,2,3,...) instead
+ // of by groups of weekdays (1,8,15,...,2,9,16,...).
+ } catch (err) {
+ _iterator6.e(err);
+ } finally {
+ _iterator6.f();
+ }
+ this.days.sort(function (a, b) {
+ return a - b;
+ }); // Comparator function allows to sort numbers.
+ } else if (partCount == 2 && "BYDAY" in parts && "BYMONTHDAY" in parts) {
+ var expandedDays = this.expand_by_day(aYear);
+ var _iterator8 = _createForOfIteratorHelper(expandedDays),
+ _step8;
+ try {
+ for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
+ var _day4 = _step8.value;
+ var tt = Time.fromDayOfYear(_day4, aYear);
+ if (this.by_data.BYMONTHDAY.indexOf(tt.day) >= 0) {
+ this.days.push(_day4);
+ }
+ }
+ } catch (err) {
+ _iterator8.e(err);
+ } finally {
+ _iterator8.f();
+ }
+ } else if (partCount == 3 && "BYDAY" in parts && "BYMONTHDAY" in parts && "BYMONTH" in parts) {
+ var _expandedDays = this.expand_by_day(aYear);
+ var _iterator9 = _createForOfIteratorHelper(_expandedDays),
+ _step9;
+ try {
+ for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
+ var _day5 = _step9.value;
+ var _tt = Time.fromDayOfYear(_day5, aYear);
+ if (this.by_data.BYMONTH.indexOf(_tt.month) >= 0 && this.by_data.BYMONTHDAY.indexOf(_tt.day) >= 0) {
+ this.days.push(_day5);
+ }
+ }
+ } catch (err) {
+ _iterator9.e(err);
+ } finally {
+ _iterator9.f();
+ }
+ } else if (partCount == 2 && "BYDAY" in parts && "BYWEEKNO" in parts) {
+ var _expandedDays2 = this.expand_by_day(aYear);
+ var _iterator0 = _createForOfIteratorHelper(_expandedDays2),
+ _step0;
+ try {
+ for (_iterator0.s(); !(_step0 = _iterator0.n()).done;) {
+ var _day6 = _step0.value;
+ var _tt2 = Time.fromDayOfYear(_day6, aYear);
+ var _weekno = _tt2.weekNumber(this.rule.wkst);
+ if (this.by_data.BYWEEKNO.indexOf(_weekno)) {
+ this.days.push(_day6);
+ }
+ }
+ } catch (err) {
+ _iterator0.e(err);
+ } finally {
+ _iterator0.f();
+ }
+ } else if (partCount == 3 && "BYDAY" in parts && "BYWEEKNO" in parts && "BYMONTHDAY" in parts) ; else if (partCount == 1 && "BYYEARDAY" in parts) {
+ this.days = this.days.concat(this.by_data.BYYEARDAY);
+ } else if (partCount == 2 && "BYYEARDAY" in parts && "BYDAY" in parts) {
+ var _daysInYear = Time.isLeapYear(aYear) ? 366 : 365;
+ var _expandedDays3 = new Set(this.expand_by_day(aYear));
+ var _iterator1 = _createForOfIteratorHelper(this.by_data.BYYEARDAY),
+ _step1;
+ try {
+ for (_iterator1.s(); !(_step1 = _iterator1.n()).done;) {
+ var doy = _step1.value;
+ if (doy < 0) {
+ doy += _daysInYear + 1;
+ }
+ if (_expandedDays3.has(doy)) {
+ this.days.push(doy);
+ }
+ }
+ } catch (err) {
+ _iterator1.e(err);
+ } finally {
+ _iterator1.f();
+ }
+ } else {
+ this.days = [];
+ }
+ var daysInYear = Time.isLeapYear(aYear) ? 366 : 365;
+ this.days.sort(function (a, b) {
+ if (a < 0) a += daysInYear + 1;
+ if (b < 0) b += daysInYear + 1;
+ return a - b;
+ });
+ return 0;
+ }
+ }, {
+ key: "expand_by_day",
+ value: function expand_by_day(aYear) {
+ var days_list = [];
+ var tmp = this.last.clone();
+ tmp.year = aYear;
+ tmp.month = 1;
+ tmp.day = 1;
+ tmp.isDate = true;
+ var start_dow = tmp.dayOfWeek();
+ tmp.month = 12;
+ tmp.day = 31;
+ tmp.isDate = true;
+ var end_dow = tmp.dayOfWeek();
+ var end_year_day = tmp.dayOfYear();
+ var _iterator10 = _createForOfIteratorHelper(this.by_data.BYDAY),
+ _step10;
+ try {
+ for (_iterator10.s(); !(_step10 = _iterator10.n()).done;) {
+ var day = _step10.value;
+ var parts = this.ruleDayOfWeek(day);
+ var pos = parts[0];
+ var dow = parts[1];
+ if (pos == 0) {
+ var tmp_start_doy = (dow + 7 - start_dow) % 7 + 1;
+ for (var doy = tmp_start_doy; doy <= end_year_day; doy += 7) {
+ days_list.push(doy);
+ }
+ } else if (pos > 0) {
+ var first = void 0;
+ if (dow >= start_dow) {
+ first = dow - start_dow + 1;
+ } else {
+ first = dow - start_dow + 8;
+ }
+ days_list.push(first + (pos - 1) * 7);
+ } else {
+ var last = void 0;
+ pos = -pos;
+ if (dow <= end_dow) {
+ last = end_year_day - end_dow + dow;
+ } else {
+ last = end_year_day - end_dow + dow - 7;
+ }
+ days_list.push(last - (pos - 1) * 7);
+ }
+ }
+ } catch (err) {
+ _iterator10.e(err);
+ } finally {
+ _iterator10.f();
+ }
+ return days_list;
+ }
+ }, {
+ key: "is_day_in_byday",
+ value: function is_day_in_byday(tt) {
+ if (this.by_data.BYDAY) {
+ var _iterator11 = _createForOfIteratorHelper(this.by_data.BYDAY),
+ _step11;
+ try {
+ for (_iterator11.s(); !(_step11 = _iterator11.n()).done;) {
+ var day = _step11.value;
+ var parts = this.ruleDayOfWeek(day);
+ var pos = parts[0];
+ var dow = parts[1];
+ var this_dow = tt.dayOfWeek();
+ if (pos == 0 && dow == this_dow || tt.nthWeekDay(dow, pos) == tt.day) {
+ return 1;
+ }
+ }
+ } catch (err) {
+ _iterator11.e(err);
+ } finally {
+ _iterator11.f();
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Checks if given value is in BYSETPOS.
+ *
+ * @private
+ * @param {Numeric} aPos position to check for.
+ * @return {Boolean} false unless BYSETPOS rules exist
+ * and the given value is present in rules.
+ */
+ }, {
+ key: "check_set_position",
+ value: function check_set_position(aPos) {
+ if (this.has_by_data('BYSETPOS')) {
+ var idx = this.by_data.BYSETPOS.indexOf(aPos);
+ // negative numbers are not false-y
+ return idx !== -1;
+ }
+ return false;
+ }
+ }, {
+ key: "sort_byday_rules",
+ value: function sort_byday_rules(aRules) {
+ for (var i = 0; i < aRules.length; i++) {
+ for (var j = 0; j < i; j++) {
+ var one = this.ruleDayOfWeek(aRules[j], this.rule.wkst)[1];
+ var two = this.ruleDayOfWeek(aRules[i], this.rule.wkst)[1];
+ if (one > two) {
+ var tmp = aRules[i];
+ aRules[i] = aRules[j];
+ aRules[j] = tmp;
+ }
+ }
+ }
+ }
+ }, {
+ key: "check_contract_restriction",
+ value: function check_contract_restriction(aRuleType, v) {
+ var indexMapValue = RecurIterator._indexMap[aRuleType];
+ var ruleMapValue = RecurIterator._expandMap[this.rule.freq][indexMapValue];
+ var pass = false;
+ if (aRuleType in this.by_data && ruleMapValue == RecurIterator.CONTRACT) {
+ var ruleType = this.by_data[aRuleType];
+ var _iterator12 = _createForOfIteratorHelper(ruleType),
+ _step12;
+ try {
+ for (_iterator12.s(); !(_step12 = _iterator12.n()).done;) {
+ var bydata = _step12.value;
+ if (bydata == v) {
+ pass = true;
+ break;
+ }
+ }
+ } catch (err) {
+ _iterator12.e(err);
+ } finally {
+ _iterator12.f();
+ }
+ } else {
+ // Not a contracting byrule or has no data, test passes
+ pass = true;
+ }
+ return pass;
+ }
+ }, {
+ key: "check_contracting_rules",
+ value: function check_contracting_rules() {
+ var dow = this.last.dayOfWeek();
+ var weekNo = this.last.weekNumber(this.rule.wkst);
+ var doy = this.last.dayOfYear();
+ return this.check_contract_restriction("BYSECOND", this.last.second) && this.check_contract_restriction("BYMINUTE", this.last.minute) && this.check_contract_restriction("BYHOUR", this.last.hour) && this.check_contract_restriction("BYDAY", Recur.numericDayToIcalDay(dow)) && this.check_contract_restriction("BYWEEKNO", weekNo) && this.check_contract_restriction("BYMONTHDAY", this.last.day) && this.check_contract_restriction("BYMONTH", this.last.month) && this.check_contract_restriction("BYYEARDAY", doy);
+ }
+ }, {
+ key: "setup_defaults",
+ value: function setup_defaults(aRuleType, req, deftime) {
+ var indexMapValue = RecurIterator._indexMap[aRuleType];
+ var ruleMapValue = RecurIterator._expandMap[this.rule.freq][indexMapValue];
+ if (ruleMapValue != RecurIterator.CONTRACT) {
+ if (!(aRuleType in this.by_data)) {
+ this.by_data[aRuleType] = [deftime];
+ }
+ if (this.rule.freq != req) {
+ return this.by_data[aRuleType][0];
+ }
+ }
+ return deftime;
+ }
+
+ /**
+ * Convert iterator into a serialize-able object. Will preserve current
+ * iteration sequence to ensure the seamless continuation of the recurrence
+ * rule.
+ * @return {Object}
+ */
+ }, {
+ key: "toJSON",
+ value: function toJSON() {
+ var result = Object.create(null);
+ result.initialized = this.initialized;
+ result.rule = this.rule.toJSON();
+ result.dtstart = this.dtstart.toJSON();
+ result.by_data = this.by_data;
+ result.days = this.days;
+ result.last = this.last.toJSON();
+ result.by_indices = this.by_indices;
+ result.occurrence_number = this.occurrence_number;
+ return result;
+ }
+ }]);
+ }();
+ /**
+ * An error indicating that a recurrence rule is invalid and produces no
+ * occurrences.
+ *
+ * @extends {Error}
+ * @class
+ */
+ _defineProperty(RecurIterator, "_indexMap", {
+ "BYSECOND": 0,
+ "BYMINUTE": 1,
+ "BYHOUR": 2,
+ "BYDAY": 3,
+ "BYMONTHDAY": 4,
+ "BYYEARDAY": 5,
+ "BYWEEKNO": 6,
+ "BYMONTH": 7,
+ "BYSETPOS": 8
+ });
+ _defineProperty(RecurIterator, "_expandMap", {
+ "SECONDLY": [1, 1, 1, 1, 1, 1, 1, 1],
+ "MINUTELY": [2, 1, 1, 1, 1, 1, 1, 1],
+ "HOURLY": [2, 2, 1, 1, 1, 1, 1, 1],
+ "DAILY": [2, 2, 2, 1, 1, 1, 1, 1],
+ "WEEKLY": [2, 2, 2, 2, 3, 3, 1, 1],
+ "MONTHLY": [2, 2, 2, 2, 2, 3, 3, 1],
+ "YEARLY": [2, 2, 2, 2, 2, 2, 2, 2]
+ });
+ _defineProperty(RecurIterator, "UNKNOWN", 0);
+ _defineProperty(RecurIterator, "CONTRACT", 1);
+ _defineProperty(RecurIterator, "EXPAND", 2);
+ _defineProperty(RecurIterator, "ILLEGAL", 3);
+ var InvalidRecurrenceRuleError = /*#__PURE__*/function (_Error) {
+ function InvalidRecurrenceRuleError() {
+ _classCallCheck(this, InvalidRecurrenceRuleError);
+ return _callSuper(this, InvalidRecurrenceRuleError, ["Recurrence rule has no valid occurrences"]);
+ }
+ _inherits(InvalidRecurrenceRuleError, _Error);
+ return _createClass(InvalidRecurrenceRuleError);
+ }(/*#__PURE__*/_wrapNativeSuper(Error));
+
+ /**
+ * 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").weekDay} weekDay
+ * Imports the 'weekDay' type from the "types.js" module
+ * @typedef {import("./types.js").frequencyValues} frequencyValues
+ * Imports the 'frequencyValues' type from the "types.js" module
+ */
+
+ var VALID_DAY_NAMES = /^(SU|MO|TU|WE|TH|FR|SA)$/;
+ var VALID_BYDAY_PART = /^([+-])?(5[0-3]|[1-4][0-9]|[1-9])?(SU|MO|TU|WE|TH|FR|SA)$/;
+ var DOW_MAP = {
+ SU: Time.SUNDAY,
+ MO: Time.MONDAY,
+ TU: Time.TUESDAY,
+ WE: Time.WEDNESDAY,
+ TH: Time.THURSDAY,
+ FR: Time.FRIDAY,
+ SA: Time.SATURDAY
+ };
+ var REVERSE_DOW_MAP = Object.fromEntries(Object.entries(DOW_MAP).map(function (entry) {
+ return entry.reverse();
+ }));
+ var ALLOWED_FREQ = ['SECONDLY', 'MINUTELY', 'HOURLY', 'DAILY', 'WEEKLY', 'MONTHLY', 'YEARLY'];
+
+ /**
+ * This class represents the "recur" value type, used for example by RRULE. It provides methods to
+ * calculate occurrences among others.
+ *
+ * @memberof ICAL
+ */
+ var Recur = /*#__PURE__*/function () {
+ /**
+ * Create a new instance of the Recur class.
+ *
+ * @param {Object} data An object with members of the recurrence
+ * @param {frequencyValues=} data.freq The frequency value
+ * @param {Number=} data.interval The INTERVAL value
+ * @param {weekDay=} data.wkst The week start value
+ * @param {Time=} data.until The end of the recurrence set
+ * @param {Number=} data.count The number of occurrences
+ * @param {Array.=} data.bysecond The seconds for the BYSECOND part
+ * @param {Array.=} data.byminute The minutes for the BYMINUTE part
+ * @param {Array.=} data.byhour The hours for the BYHOUR part
+ * @param {Array.=} data.byday The BYDAY values
+ * @param {Array.=} data.bymonthday The days for the BYMONTHDAY part
+ * @param {Array.=} data.byyearday The days for the BYYEARDAY part
+ * @param {Array.=} data.byweekno The weeks for the BYWEEKNO part
+ * @param {Array.=} data.bymonth The month for the BYMONTH part
+ * @param {Array.=} data.bysetpos The positionals for the BYSETPOS part
+ */
+ function Recur(data) {
+ _classCallCheck(this, Recur);
+ /**
+ * An object holding the BY-parts of the recurrence rule
+ * @memberof ICAL.Recur
+ * @typedef {Object} byParts
+ * @property {Array.=} BYSECOND The seconds for the BYSECOND part
+ * @property {Array.=} BYMINUTE The minutes for the BYMINUTE part
+ * @property {Array.=} BYHOUR The hours for the BYHOUR part
+ * @property {Array.=} BYDAY The BYDAY values
+ * @property {Array.=} BYMONTHDAY The days for the BYMONTHDAY part
+ * @property {Array.=} BYYEARDAY The days for the BYYEARDAY part
+ * @property {Array.=} BYWEEKNO The weeks for the BYWEEKNO part
+ * @property {Array.=} BYMONTH The month for the BYMONTH part
+ * @property {Array.=} BYSETPOS The positionals for the BYSETPOS part
+ */
+ /**
+ * An object holding the BY-parts of the recurrence rule
+ * @type {byParts}
+ */
+ _defineProperty(this, "parts", null);
+ /**
+ * The interval value for the recurrence rule.
+ * @type {Number}
+ */
+ _defineProperty(this, "interval", 1);
+ /**
+ * The week start day
+ *
+ * @type {weekDay}
+ * @default ICAL.Time.MONDAY
+ */
+ _defineProperty(this, "wkst", Time.MONDAY);
+ /**
+ * The end of the recurrence
+ * @type {?Time}
+ */
+ _defineProperty(this, "until", null);
+ /**
+ * The maximum number of occurrences
+ * @type {?Number}
+ */
+ _defineProperty(this, "count", null);
+ /**
+ * The frequency value.
+ * @type {frequencyValues}
+ */
+ _defineProperty(this, "freq", null);
+ /**
+ * The class identifier.
+ * @constant
+ * @type {String}
+ * @default "icalrecur"
+ */
+ _defineProperty(this, "icalclass", "icalrecur");
+ /**
+ * The type name, to be used in the jCal object.
+ * @constant
+ * @type {String}
+ * @default "recur"
+ */
+ _defineProperty(this, "icaltype", "recur");
+ this.wrappedJSObject = this;
+ this.parts = {};
+ if (data && _typeof(data) === 'object') {
+ this.fromData(data);
+ }
+ }
+ return _createClass(Recur, [{
+ key: "iterator",
+ value:
+ /**
+ * Create a new iterator for this recurrence rule. The passed start date
+ * must be the start date of the event, not the start of the range to
+ * search in.
+ *
+ * @example
+ * let recur = comp.getFirstPropertyValue('rrule');
+ * let dtstart = comp.getFirstPropertyValue('dtstart');
+ * let iter = recur.iterator(dtstart);
+ * for (let next = iter.next(); next; next = iter.next()) {
+ * if (next.compare(rangeStart) < 0) {
+ * continue;
+ * }
+ * console.log(next.toString());
+ * }
+ *
+ * @param {Time} aStart The item's start date
+ * @return {RecurIterator} The recurrence iterator
+ */
+ function iterator(aStart) {
+ return new RecurIterator({
+ rule: this,
+ dtstart: aStart
+ });
+ }
+
+ /**
+ * Returns a clone of the recurrence object.
+ *
+ * @return {Recur} The cloned object
+ */
+ }, {
+ key: "clone",
+ value: function clone() {
+ return new Recur(this.toJSON());
+ }
+
+ /**
+ * Checks if the current rule is finite, i.e. has a count or until part.
+ *
+ * @return {Boolean} True, if the rule is finite
+ */
+ }, {
+ key: "isFinite",
+ value: function isFinite() {
+ return !!(this.count || this.until);
+ }
+
+ /**
+ * Checks if the current rule has a count part, and not limited by an until
+ * part.
+ *
+ * @return {Boolean} True, if the rule is by count
+ */
+ }, {
+ key: "isByCount",
+ value: function isByCount() {
+ return !!(this.count && !this.until);
+ }
+
+ /**
+ * Adds a component (part) to the recurrence rule. This is not a component
+ * in the sense of {@link ICAL.Component}, but a part of the recurrence
+ * rule, i.e. BYMONTH.
+ *
+ * @param {String} aType The name of the component part
+ * @param {Array|String} aValue The component value
+ */
+ }, {
+ key: "addComponent",
+ value: function addComponent(aType, aValue) {
+ var ucname = aType.toUpperCase();
+ if (ucname in this.parts) {
+ this.parts[ucname].push(aValue);
+ } else {
+ this.parts[ucname] = [aValue];
+ }
+ }
+
+ /**
+ * Sets the component value for the given by-part.
+ *
+ * @param {String} aType The component part name
+ * @param {Array} aValues The component values
+ */
+ }, {
+ key: "setComponent",
+ value: function setComponent(aType, aValues) {
+ this.parts[aType.toUpperCase()] = aValues.slice();
+ }
+
+ /**
+ * Gets (a copy) of the requested component value.
+ *
+ * @param {String} aType The component part name
+ * @return {Array} The component part value
+ */
+ }, {
+ key: "getComponent",
+ value: function getComponent(aType) {
+ var ucname = aType.toUpperCase();
+ return ucname in this.parts ? this.parts[ucname].slice() : [];
+ }
+
+ /**
+ * Retrieves the next occurrence after the given recurrence id. See the
+ * guide on {@tutorial terminology} for more details.
+ *
+ * NOTE: Currently, this method iterates all occurrences from the start
+ * date. It should not be called in a loop for performance reasons. If you
+ * would like to get more than one occurrence, you can iterate the
+ * occurrences manually, see the example on the
+ * {@link ICAL.Recur#iterator iterator} method.
+ *
+ * @param {Time} aStartTime The start of the event series
+ * @param {Time} aRecurrenceId The date of the last occurrence
+ * @return {Time} The next occurrence after
+ */
+ }, {
+ key: "getNextOccurrence",
+ value: function getNextOccurrence(aStartTime, aRecurrenceId) {
+ var iter = this.iterator(aStartTime);
+ var next;
+ do {
+ next = iter.next();
+ } while (next && next.compare(aRecurrenceId) <= 0);
+ if (next && aRecurrenceId.zone) {
+ next.zone = aRecurrenceId.zone;
+ }
+ return next;
+ }
+
+ /**
+ * Sets up the current instance using members from the passed data object.
+ *
+ * @param {Object} data An object with members of the recurrence
+ * @param {frequencyValues=} data.freq The frequency value
+ * @param {Number=} data.interval The INTERVAL value
+ * @param {weekDay=} data.wkst The week start value
+ * @param {Time=} data.until The end of the recurrence set
+ * @param {Number=} data.count The number of occurrences
+ * @param {Array.=} data.bysecond The seconds for the BYSECOND part
+ * @param {Array.=} data.byminute The minutes for the BYMINUTE part
+ * @param {Array.=} data.byhour The hours for the BYHOUR part
+ * @param {Array.=} data.byday The BYDAY values
+ * @param {Array.=} data.bymonthday The days for the BYMONTHDAY part
+ * @param {Array.=} data.byyearday The days for the BYYEARDAY part
+ * @param {Array.=} data.byweekno The weeks for the BYWEEKNO part
+ * @param {Array.=} data.bymonth The month for the BYMONTH part
+ * @param {Array.=} data.bysetpos The positionals for the BYSETPOS part
+ */
+ }, {
+ key: "fromData",
+ value: function fromData(data) {
+ for (var key in data) {
+ var uckey = key.toUpperCase();
+ if (uckey in partDesign) {
+ if (Array.isArray(data[key])) {
+ this.parts[uckey] = data[key];
+ } else {
+ this.parts[uckey] = [data[key]];
+ }
+ } else {
+ this[key] = data[key];
+ }
+ }
+ if (this.interval && typeof this.interval != "number") {
+ optionDesign.INTERVAL(this.interval, this);
+ }
+ if (this.wkst && typeof this.wkst != "number") {
+ this.wkst = Recur.icalDayToNumericDay(this.wkst);
+ }
+ if (this.until && !(this.until instanceof Time)) {
+ this.until = Time.fromString(this.until);
+ }
+ }
+
+ /**
+ * The jCal representation of this recurrence type.
+ * @return {Object}
+ */
+ }, {
+ key: "toJSON",
+ value: function toJSON() {
+ var res = Object.create(null);
+ res.freq = this.freq;
+ if (this.count) {
+ res.count = this.count;
+ }
+ if (this.interval > 1) {
+ res.interval = this.interval;
+ }
+ for (var _i = 0, _Object$entries = Object.entries(this.parts); _i < _Object$entries.length; _i++) {
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
+ k = _Object$entries$_i[0],
+ kparts = _Object$entries$_i[1];
+ if (Array.isArray(kparts) && kparts.length == 1) {
+ res[k.toLowerCase()] = kparts[0];
+ } else {
+ res[k.toLowerCase()] = clone(kparts);
+ }
+ }
+ if (this.until) {
+ res.until = this.until.toString();
+ }
+ if ('wkst' in this && this.wkst !== Time.DEFAULT_WEEK_START) {
+ res.wkst = Recur.numericDayToIcalDay(this.wkst);
+ }
+ return res;
+ }
+
+ /**
+ * The string representation of this recurrence rule.
+ * @return {String}
+ */
+ }, {
+ key: "toString",
+ value: function toString() {
+ // TODO retain order
+ var str = "FREQ=" + this.freq;
+ if (this.count) {
+ str += ";COUNT=" + this.count;
+ }
+ if (this.interval > 1) {
+ str += ";INTERVAL=" + this.interval;
+ }
+ for (var _i2 = 0, _Object$entries2 = Object.entries(this.parts); _i2 < _Object$entries2.length; _i2++) {
+ var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i2], 2),
+ k = _Object$entries2$_i[0],
+ v = _Object$entries2$_i[1];
+ str += ";" + k + "=" + v;
+ }
+ if (this.until) {
+ str += ';UNTIL=' + this.until.toICALString();
+ }
+ if ('wkst' in this && this.wkst !== Time.DEFAULT_WEEK_START) {
+ str += ';WKST=' + Recur.numericDayToIcalDay(this.wkst);
+ }
+ return str;
+ }
+ }], [{
+ key: "fromString",
+ value:
+ /**
+ * Creates a new {@link ICAL.Recur} instance from the passed string.
+ *
+ * @param {String} string The string to parse
+ * @return {Recur} The created recurrence instance
+ */
+ function fromString(string) {
+ var data = this._stringToData(string, false);
+ return new Recur(data);
+ }
+
+ /**
+ * Creates a new {@link ICAL.Recur} instance using members from the passed
+ * data object.
+ *
+ * @param {Object} aData An object with members of the recurrence
+ * @param {frequencyValues=} aData.freq The frequency value
+ * @param {Number=} aData.interval The INTERVAL value
+ * @param {weekDay=} aData.wkst The week start value
+ * @param {Time=} aData.until The end of the recurrence set
+ * @param {Number=} aData.count The number of occurrences
+ * @param {Array.=} aData.bysecond The seconds for the BYSECOND part
+ * @param {Array.=} aData.byminute The minutes for the BYMINUTE part
+ * @param {Array.=} aData.byhour The hours for the BYHOUR part
+ * @param {Array.=} aData.byday The BYDAY values
+ * @param {Array.=} aData.bymonthday The days for the BYMONTHDAY part
+ * @param {Array.=} aData.byyearday The days for the BYYEARDAY part
+ * @param {Array.=} aData.byweekno The weeks for the BYWEEKNO part
+ * @param {Array.=} aData.bymonth The month for the BYMONTH part
+ * @param {Array.=} aData.bysetpos The positionals for the BYSETPOS part
+ */
+ }, {
+ key: "fromData",
+ value: function fromData(aData) {
+ return new Recur(aData);
+ }
+
+ /**
+ * Converts a recurrence string to a data object, suitable for the fromData
+ * method.
+ *
+ * @private
+ * @param {String} string The string to parse
+ * @param {Boolean} fmtIcal If true, the string is considered to be an
+ * iCalendar string
+ * @return {Recur} The recurrence instance
+ */
+ }, {
+ key: "_stringToData",
+ value: function _stringToData(string, fmtIcal) {
+ var dict = Object.create(null);
+
+ // split is slower in FF but fast enough.
+ // v8 however this is faster then manual split?
+ var values = string.split(';');
+ var len = values.length;
+ for (var i = 0; i < len; i++) {
+ var parts = values[i].split('=');
+ var ucname = parts[0].toUpperCase();
+ var lcname = parts[0].toLowerCase();
+ var name = fmtIcal ? lcname : ucname;
+ var value = parts[1];
+ if (ucname in partDesign) {
+ var partArr = value.split(',');
+ var partSet = new Set();
+ var _iterator = _createForOfIteratorHelper(partArr),
+ _step;
+ try {
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
+ var part = _step.value;
+ partSet.add(partDesign[ucname](part));
+ }
+ } catch (err) {
+ _iterator.e(err);
+ } finally {
+ _iterator.f();
+ }
+ partArr = _toConsumableArray(partSet);
+ dict[name] = partArr.length == 1 ? partArr[0] : partArr;
+ } else if (ucname in optionDesign) {
+ optionDesign[ucname](value, dict, fmtIcal);
+ } else {
+ // Don't swallow unknown values. Just set them as they are.
+ dict[lcname] = value;
+ }
+ }
+ return dict;
+ }
+
+ /**
+ * Convert an ical representation of a day (SU, MO, etc..)
+ * into a numeric value of that day.
+ *
+ * @param {String} string The iCalendar day name
+ * @param {weekDay=} aWeekStart
+ * The week start weekday, defaults to SUNDAY
+ * @return {Number} Numeric value of given day
+ */
+ }, {
+ key: "icalDayToNumericDay",
+ value: function icalDayToNumericDay(string, aWeekStart) {
+ //XXX: this is here so we can deal
+ // with possibly invalid string values.
+ var firstDow = aWeekStart || Time.SUNDAY;
+ return (DOW_MAP[string] - firstDow + 7) % 7 + 1;
+ }
+
+ /**
+ * Convert a numeric day value into its ical representation (SU, MO, etc..)
+ *
+ * @param {Number} num Numeric value of given day
+ * @param {weekDay=} aWeekStart
+ * The week start weekday, defaults to SUNDAY
+ * @return {String} The ICAL day value, e.g SU,MO,...
+ */
+ }, {
+ key: "numericDayToIcalDay",
+ value: function numericDayToIcalDay(num, aWeekStart) {
+ //XXX: this is here so we can deal with possibly invalid number values.
+ // Also, this allows consistent mapping between day numbers and day
+ // names for external users.
+ var firstDow = aWeekStart || Time.SUNDAY;
+ var dow = num + firstDow - Time.SUNDAY;
+ if (dow > 7) {
+ dow -= 7;
+ }
+ return REVERSE_DOW_MAP[dow];
+ }
+ }]);
+ }();
+ function parseNumericValue(type, min, max, value) {
+ var result = value;
+ if (value[0] === '+') {
+ result = value.slice(1);
+ }
+ result = strictParseInt(result);
+ if (min !== undefined && value < min) {
+ throw new Error(type + ': invalid value "' + value + '" must be > ' + min);
+ }
+ if (max !== undefined && value > max) {
+ throw new Error(type + ': invalid value "' + value + '" must be < ' + min);
+ }
+ return result;
+ }
+ var optionDesign = {
+ FREQ: function FREQ(value, dict, fmtIcal) {
+ // yes this is actually equal or faster then regex.
+ // upside here is we can enumerate the valid values.
+ if (ALLOWED_FREQ.indexOf(value) !== -1) {
+ dict.freq = value;
+ } else {
+ throw new Error('invalid frequency "' + value + '" expected: "' + ALLOWED_FREQ.join(', ') + '"');
+ }
+ },
+ COUNT: function COUNT(value, dict, fmtIcal) {
+ dict.count = strictParseInt(value);
+ },
+ INTERVAL: function INTERVAL(value, dict, fmtIcal) {
+ dict.interval = strictParseInt(value);
+ if (dict.interval < 1) {
+ // 0 or negative values are not allowed, some engines seem to generate
+ // it though. Assume 1 instead.
+ dict.interval = 1;
+ }
+ },
+ UNTIL: function UNTIL(value, dict, fmtIcal) {
+ if (value.length > 10) {
+ dict.until = design.icalendar.value['date-time'].fromICAL(value);
+ } else {
+ dict.until = design.icalendar.value.date.fromICAL(value);
+ }
+ if (!fmtIcal) {
+ dict.until = Time.fromString(dict.until);
+ }
+ },
+ WKST: function WKST(value, dict, fmtIcal) {
+ if (VALID_DAY_NAMES.test(value)) {
+ dict.wkst = Recur.icalDayToNumericDay(value);
+ } else {
+ throw new Error('invalid WKST value "' + value + '"');
+ }
+ }
+ };
+ var partDesign = {
+ BYSECOND: parseNumericValue.bind(undefined, 'BYSECOND', 0, 60),
+ BYMINUTE: parseNumericValue.bind(undefined, 'BYMINUTE', 0, 59),
+ BYHOUR: parseNumericValue.bind(undefined, 'BYHOUR', 0, 23),
+ BYDAY: function BYDAY(value) {
+ if (VALID_BYDAY_PART.test(value)) {
+ return value;
+ } else {
+ throw new Error('invalid BYDAY value "' + value + '"');
+ }
+ },
+ BYMONTHDAY: parseNumericValue.bind(undefined, 'BYMONTHDAY', -31, 31),
+ BYYEARDAY: parseNumericValue.bind(undefined, 'BYYEARDAY', -366, 366),
+ BYWEEKNO: parseNumericValue.bind(undefined, 'BYWEEKNO', -53, 53),
+ BYMONTH: parseNumericValue.bind(undefined, 'BYMONTH', 1, 12),
+ BYSETPOS: parseNumericValue.bind(undefined, 'BYSETPOS', -366, 366)
+ };
+
+ /**
+ * 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 */
+
+ var FROM_ICAL_NEWLINE = /\\\\|\\;|\\,|\\[Nn]/g;
+ var TO_ICAL_NEWLINE = /\\|;|,|\n/g;
+ var FROM_VCARD_NEWLINE = /\\\\|\\,|\\[Nn]/g;
+ var TO_VCARD_NEWLINE = /\\|,|\n/g;
+ function createTextType(fromNewline, toNewline) {
+ var result = {
+ matches: /.*/,
+ fromICAL: function fromICAL(aValue, structuredEscape) {
+ return replaceNewline(aValue, fromNewline, structuredEscape);
+ },
+ toICAL: function toICAL(aValue, structuredEscape) {
+ var 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
+ var DEFAULT_TYPE_TEXT = {
+ defaultType: "text"
+ };
+ var DEFAULT_TYPE_TEXT_MULTI = {
+ defaultType: "text",
+ multiValue: ","
+ };
+ var DEFAULT_TYPE_TEXT_STRUCTURED = {
+ defaultType: "text",
+ structuredValue: ";"
+ };
+ var DEFAULT_TYPE_INTEGER = {
+ defaultType: "integer"
+ };
+ var DEFAULT_TYPE_DATETIME_DATE = {
+ defaultType: "date-time",
+ allowedTypes: ["date-time", "date"]
+ };
+ var DEFAULT_TYPE_DATETIME = {
+ defaultType: "date-time"
+ };
+ var DEFAULT_TYPE_URI = {
+ defaultType: "uri"
+ };
+ var DEFAULT_TYPE_UTCOFFSET = {
+ defaultType: "utc-offset"
+ };
+ var DEFAULT_TYPE_RECUR = {
+ defaultType: "recur"
+ };
+ var 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);
+ }
+ var commonProperties = {
+ "categories": DEFAULT_TYPE_TEXT_MULTI,
+ "url": DEFAULT_TYPE_URI,
+ "version": DEFAULT_TYPE_TEXT,
+ "uid": DEFAULT_TYPE_TEXT
+ };
+ var commonValues = {
+ "boolean": {
+ values: ["TRUE", "FALSE"],
+ fromICAL: function fromICAL(aValue) {
+ switch (aValue) {
+ case 'TRUE':
+ return true;
+ case 'FALSE':
+ return false;
+ default:
+ //TODO: parser warning
+ return false;
+ }
+ },
+ toICAL: function toICAL(aValue) {
+ if (aValue) {
+ return 'TRUE';
+ }
+ return 'FALSE';
+ }
+ },
+ "float": {
+ matches: /^[+-]?\d+\.\d+$/,
+ fromICAL: function fromICAL(aValue) {
+ var parsed = parseFloat(aValue);
+ if (isStrictlyNaN(parsed)) {
+ // TODO: parser warning
+ return 0.0;
+ }
+ return parsed;
+ },
+ toICAL: function toICAL(aValue) {
+ return String(aValue);
+ }
+ },
+ integer: {
+ fromICAL: function fromICAL(aValue) {
+ var parsed = parseInt(aValue);
+ if (isStrictlyNaN(parsed)) {
+ return 0;
+ }
+ return parsed;
+ },
+ toICAL: function toICAL(aValue) {
+ return String(aValue);
+ }
+ },
+ "utc-offset": {
+ toICAL: function toICAL(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 fromICAL(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 decorate(aValue) {
+ return UtcOffset.fromString(aValue);
+ },
+ undecorate: function undecorate(aValue) {
+ return aValue.toString();
+ }
+ }
+ };
+ var 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!
+ var icalValues = extend(commonValues, {
+ text: createTextType(FROM_ICAL_NEWLINE, TO_ICAL_NEWLINE),
+ uri: {
+ // TODO
+ /* ... */
+ },
+ "binary": {
+ decorate: function decorate(aString) {
+ return Binary.fromString(aString);
+ },
+ undecorate: function undecorate(aBinary) {
+ return aBinary.toString();
+ }
+ },
+ "cal-address": {
+ // needs to be an uri
+ },
+ "date": {
+ decorate: function decorate(aValue, aProp) {
+ if (design.strict) {
+ return Time.fromDateString(aValue, aProp);
+ } else {
+ return Time.fromString(aValue, aProp);
+ }
+ },
+ /**
+ * undecorates a time object.
+ */
+ undecorate: function undecorate(aValue) {
+ return aValue.toString();
+ },
+ fromICAL: function fromICAL(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 toICAL(aValue) {
+ // from: 2012-09-01
+ // to: 20120901
+ var 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 fromICAL(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 {
+ var 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 toICAL(aValue) {
+ // from: 2012-09-01T13:00:00
+ // to: 20120901T130000
+ var len = aValue.length;
+ if (len == 10 && !design.strict) {
+ return icalValues.date.toICAL(aValue);
+ } else if (len >= 19) {
+ var 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 decorate(aValue, aProp) {
+ if (design.strict) {
+ return Time.fromDateTimeString(aValue, aProp);
+ } else {
+ return Time.fromString(aValue, aProp);
+ }
+ },
+ undecorate: function undecorate(aValue) {
+ return aValue.toString();
+ }
+ },
+ duration: {
+ decorate: function decorate(aValue) {
+ return Duration.fromString(aValue);
+ },
+ undecorate: function undecorate(aValue) {
+ return aValue.toString();
+ }
+ },
+ period: {
+ fromICAL: function fromICAL(string) {
+ var 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 toICAL(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 decorate(aValue, aProp) {
+ return Period.fromJSON(aValue, aProp, !design.strict);
+ },
+ undecorate: function undecorate(aValue) {
+ return aValue.toJSON();
+ }
+ },
+ recur: {
+ fromICAL: function fromICAL(string) {
+ return Recur._stringToData(string, true);
+ },
+ toICAL: function toICAL(data) {
+ var str = "";
+ for (var _i = 0, _Object$entries = Object.entries(data); _i < _Object$entries.length; _i++) {
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
+ k = _Object$entries$_i[0],
+ val = _Object$entries$_i[1];
+ 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 undecorate(aRecur) {
+ return aRecur.toJSON();
+ }
+ },
+ time: {
+ fromICAL: function fromICAL(aValue) {
+ // from: MMHHSS(Z)?
+ // to: HH:MM:SS(Z)?
+ if (aValue.length < 6) {
+ // TODO: parser exception?
+ return aValue;
+ }
+
+ // HH::MM::SSZ?
+ var result = aValue.slice(0, 2) + ':' + aValue.slice(2, 4) + ':' + aValue.slice(4, 6);
+ if (aValue[6] === 'Z') {
+ result += 'Z';
+ }
+ return result;
+ },
+ toICAL: function toICAL(aValue) {
+ // from: HH:MM:SS(Z)?
+ // to: MMHHSS(Z)?
+ if (aValue.length < 8) {
+ //TODO: error
+ return aValue;
+ }
+ var result = aValue.slice(0, 2) + aValue.slice(3, 5) + aValue.slice(6, 8);
+ if (aValue[8] === 'Z') {
+ result += 'Z';
+ }
+ return result;
+ }
+ }
+ });
+ var 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 detectType(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!
+ var vcardValues = extend(commonValues, {
+ text: createTextType(FROM_VCARD_NEWLINE, TO_VCARD_NEWLINE),
+ uri: createTextType(FROM_VCARD_NEWLINE, TO_VCARD_NEWLINE),
+ date: {
+ decorate: function decorate(aValue) {
+ return VCardTime.fromDateAndOrTimeString(aValue, "date");
+ },
+ undecorate: function undecorate(aValue) {
+ return aValue.toString();
+ },
+ fromICAL: function fromICAL(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 toICAL(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 decorate(aValue) {
+ return VCardTime.fromDateAndOrTimeString("T" + aValue, "time");
+ },
+ undecorate: function undecorate(aValue) {
+ return aValue.toString();
+ },
+ fromICAL: function fromICAL(aValue) {
+ var splitzone = vcardValues.time._splitZone(aValue, true);
+ var 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 toICAL(aValue) {
+ var splitzone = vcardValues.time._splitZone(aValue);
+ var 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 _splitZone(aValue, isFromIcal) {
+ var lastChar = aValue.length - 1;
+ var signChar = aValue.length - (isFromIcal ? 5 : 6);
+ var sign = aValue[signChar];
+ var 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 decorate(aValue) {
+ return VCardTime.fromDateAndOrTimeString(aValue, "date-time");
+ },
+ undecorate: function undecorate(aValue) {
+ return aValue.toString();
+ },
+ fromICAL: function fromICAL(aValue) {
+ return vcardValues['date-and-or-time'].fromICAL(aValue);
+ },
+ toICAL: function toICAL(aValue) {
+ return vcardValues['date-and-or-time'].toICAL(aValue);
+ }
+ },
+ "date-and-or-time": {
+ decorate: function decorate(aValue) {
+ return VCardTime.fromDateAndOrTimeString(aValue, "date-and-or-time");
+ },
+ undecorate: function undecorate(aValue) {
+ return aValue.toString();
+ },
+ fromICAL: function fromICAL(aValue) {
+ var parts = aValue.split('T');
+ return (parts[0] ? vcardValues.date.fromICAL(parts[0]) : '') + (parts[1] ? 'T' + vcardValues.time.fromICAL(parts[1]) : '');
+ },
+ toICAL: function toICAL(aValue) {
+ var 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 fromICAL(aValue) {
+ return Array.from(aValue).filter(function (c) {
+ return c === '\\' ? undefined : c;
+ }).join('');
+ },
+ toICAL: function toICAL(aValue) {
+ return Array.from(aValue).map(function (c) {
+ return c === ',' || c === ";" ? '\\' + c : c;
+ }).join('');
+ }
+ }
+ });
+ var 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
+ }
+ };
+ var 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
+ });
+ var 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 toICAL(aValue) {
+ return aValue.slice(0, 7);
+ },
+ fromICAL: function fromICAL(aValue) {
+ return aValue.slice(0, 7);
+ },
+ decorate: function decorate(aValue) {
+ return UtcOffset.fromString(aValue);
+ },
+ undecorate: function undecorate(aValue) {
+ return aValue.toString();
+ }
+ }
+ });
+ var 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
+ }
+ };
+ var 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 detectType(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 detectType(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}
+ */
+ var icalSet = {
+ name: "ical",
+ value: icalValues,
+ param: icalParams,
+ property: icalProperties,
+ propertyGroups: false
+ };
+
+ /**
+ * vCard 4.0 design set
+ * @type {designSet}
+ */
+ var vcardSet = {
+ name: "vcard4",
+ value: vcardValues,
+ param: vcardParams,
+ property: vcardProperties,
+ propertyGroups: true
+ };
+
+ /**
+ * vCard 3.0 design set
+ * @type {designSet}
+ */
+ var 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
+ */
+ var 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 getDesignSet(componentName) {
+ var isInDesign = componentName && componentName in design.components;
+ return isInDesign ? design.components[componentName] : design.defaultSet;
+ }
+ };
+
+ /**
+ * 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
+ */
+
+ var LINE_ENDING = '\r\n';
+ var DEFAULT_VALUE_TYPE = 'unknown';
+ var RFC6868_REPLACE_MAP = {
+ '"': "^'",
+ "\n": "^n",
+ "^": "^^"
+ };
+
+ /**
+ * Convert a full jCal/jCard array into a iCalendar/vCard string.
+ *
+ * @function ICAL.stringify
+ * @variation function
+ * @param {Array} jCal The jCal/jCard document
+ * @return {String} The stringified iCalendar/vCard document
+ */
+ function stringify(jCal) {
+ if (typeof jCal[0] == "string") {
+ // This is a single component
+ jCal = [jCal];
+ }
+ var i = 0;
+ var len = jCal.length;
+ var result = '';
+ for (; i < len; i++) {
+ result += stringify.component(jCal[i]) + LINE_ENDING;
+ }
+ return result;
+ }
+
+ /**
+ * Converts an jCal component array into a ICAL string.
+ * Recursive will resolve sub-components.
+ *
+ * Exact component/property order is not saved all
+ * properties will come before subcomponents.
+ *
+ * @function ICAL.stringify.component
+ * @param {Array} component
+ * jCal/jCard fragment of a component
+ * @param {designSet} designSet
+ * The design data to use for this component
+ * @return {String} The iCalendar/vCard string
+ */
+ stringify.component = function (component, designSet) {
+ var name = component[0].toUpperCase();
+ var result = 'BEGIN:' + name + LINE_ENDING;
+ var props = component[1];
+ var propIdx = 0;
+ var propLen = props.length;
+ var designSetName = component[0];
+ // rfc6350 requires that in vCard 4.0 the first component is the VERSION
+ // component with as value 4.0, note that 3.0 does not have this requirement.
+ if (designSetName === 'vcard' && component[1].length > 0 && !(component[1][0][0] === "version" && component[1][0][3] === "4.0")) {
+ designSetName = "vcard3";
+ }
+ designSet = designSet || design.getDesignSet(designSetName);
+ for (; propIdx < propLen; propIdx++) {
+ result += stringify.property(props[propIdx], designSet) + LINE_ENDING;
+ }
+
+ // Ignore subcomponents if none exist, e.g. in vCard.
+ var comps = component[2] || [];
+ var compIdx = 0;
+ var compLen = comps.length;
+ for (; compIdx < compLen; compIdx++) {
+ result += stringify.component(comps[compIdx], designSet) + LINE_ENDING;
+ }
+ result += 'END:' + name;
+ return result;
+ };
+
+ /**
+ * Converts a single jCal/jCard property to a iCalendar/vCard string.
+ *
+ * @function ICAL.stringify.property
+ * @param {Array} property
+ * jCal/jCard property array
+ * @param {designSet} designSet
+ * The design data to use for this property
+ * @param {Boolean} noFold
+ * If true, the line is not folded
+ * @return {String} The iCalendar/vCard string
+ */
+ stringify.property = function (property, designSet, noFold) {
+ var name = property[0].toUpperCase();
+ var jsName = property[0];
+ var params = property[1];
+ if (!designSet) {
+ designSet = design.defaultSet;
+ }
+ var groupName = params.group;
+ var line;
+ if (designSet.propertyGroups && groupName) {
+ line = groupName.toUpperCase() + "." + name;
+ } else {
+ line = name;
+ }
+ var _loop = function _loop() {
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
+ paramName = _Object$entries$_i[0],
+ value = _Object$entries$_i[1];
+ if (designSet.propertyGroups && paramName == 'group') {
+ return 1; // continue
+ }
+ var paramDesign = designSet.param[paramName];
+ var multiValue = paramDesign && paramDesign.multiValue;
+ if (multiValue && Array.isArray(value)) {
+ value = value.map(function (val) {
+ val = stringify._rfc6868Unescape(val);
+ val = stringify.paramPropertyValue(val, paramDesign.multiValueSeparateDQuote);
+ return val;
+ });
+ value = stringify.multiValue(value, multiValue, "unknown", null, designSet);
+ } else {
+ value = stringify._rfc6868Unescape(value);
+ value = stringify.paramPropertyValue(value);
+ }
+ line += ';' + paramName.toUpperCase() + '=' + value;
+ };
+ for (var _i = 0, _Object$entries = Object.entries(params); _i < _Object$entries.length; _i++) {
+ if (_loop()) continue;
+ }
+ if (property.length === 3) {
+ // If there are no values, we must assume a blank value
+ return line + ':';
+ }
+ var valueType = property[2];
+ var propDetails;
+ var multiValue = false;
+ var structuredValue = false;
+ var isDefault = false;
+ if (jsName in designSet.property) {
+ propDetails = designSet.property[jsName];
+ if ('multiValue' in propDetails) {
+ multiValue = propDetails.multiValue;
+ }
+ if ('structuredValue' in propDetails && Array.isArray(property[3])) {
+ structuredValue = propDetails.structuredValue;
+ }
+ if ('defaultType' in propDetails) {
+ if (valueType === propDetails.defaultType) {
+ isDefault = true;
+ }
+ } else {
+ if (valueType === DEFAULT_VALUE_TYPE) {
+ isDefault = true;
+ }
+ }
+ } else {
+ if (valueType === DEFAULT_VALUE_TYPE) {
+ isDefault = true;
+ }
+ }
+
+ // push the VALUE property if type is not the default
+ // for the current property.
+ if (!isDefault) {
+ // value will never contain ;/:/, so we don't escape it here.
+ line += ';VALUE=' + valueType.toUpperCase();
+ }
+ line += ':';
+ if (multiValue && structuredValue) {
+ line += stringify.multiValue(property[3], structuredValue, valueType, multiValue, designSet, structuredValue);
+ } else if (multiValue) {
+ line += stringify.multiValue(property.slice(3), multiValue, valueType, null, designSet, false);
+ } else if (structuredValue) {
+ line += stringify.multiValue(property[3], structuredValue, valueType, null, designSet, structuredValue);
+ } else {
+ line += stringify.value(property[3], valueType, designSet, false);
+ }
+ return noFold ? line : foldline(line);
+ };
+
+ /**
+ * Handles escaping of property values that may contain:
+ *
+ * COLON (:), SEMICOLON (;), or COMMA (,)
+ *
+ * If any of the above are present the result is wrapped
+ * in double quotes.
+ *
+ * @function ICAL.stringify.paramPropertyValue
+ * @param {String} value Raw property value
+ * @param {boolean} force If value should be escaped even when unnecessary
+ * @return {String} Given or escaped value when needed
+ */
+ stringify.paramPropertyValue = function (value, force) {
+ if (!force && value.indexOf(',') === -1 && value.indexOf(':') === -1 && value.indexOf(';') === -1) {
+ return value;
+ }
+ return '"' + value + '"';
+ };
+
+ /**
+ * Converts an array of ical values into a single
+ * string based on a type and a delimiter value (like ",").
+ *
+ * @function ICAL.stringify.multiValue
+ * @param {Array} values List of values to convert
+ * @param {String} delim Used to join the values (",", ";", ":")
+ * @param {String} type Lowecase ical value type
+ * (like boolean, date-time, etc..)
+ * @param {?String} innerMulti If set, each value will again be processed
+ * Used for structured values
+ * @param {designSet} designSet
+ * The design data to use for this property
+ *
+ * @return {String} iCalendar/vCard string for value
+ */
+ stringify.multiValue = function (values, delim, type, innerMulti, designSet, structuredValue) {
+ var result = '';
+ var len = values.length;
+ var i = 0;
+ for (; i < len; i++) {
+ if (innerMulti && Array.isArray(values[i])) {
+ result += stringify.multiValue(values[i], innerMulti, type, null, designSet, structuredValue);
+ } else {
+ result += stringify.value(values[i], type, designSet, structuredValue);
+ }
+ if (i !== len - 1) {
+ result += delim;
+ }
+ }
+ return result;
+ };
+
+ /**
+ * Processes a single ical value runs the associated "toICAL" method from the
+ * design value type if available to convert the value.
+ *
+ * @function ICAL.stringify.value
+ * @param {String|Number} value A formatted value
+ * @param {String} type Lowercase iCalendar/vCard value type
+ * (like boolean, date-time, etc..)
+ * @return {String} iCalendar/vCard value for single value
+ */
+ stringify.value = function (value, type, designSet, structuredValue) {
+ if (type in designSet.value && 'toICAL' in designSet.value[type]) {
+ return designSet.value[type].toICAL(value, structuredValue);
+ }
+ return value;
+ };
+
+ /**
+ * Internal helper for rfc6868. Exposing this on ICAL.stringify so that
+ * hackers can disable the rfc6868 parsing if the really need to.
+ *
+ * @param {String} val The value to unescape
+ * @return {String} The escaped value
+ */
+ stringify._rfc6868Unescape = function (val) {
+ return val.replace(/[\n^"]/g, function (x) {
+ return RFC6868_REPLACE_MAP[x];
+ });
+ };
+
+ /* 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 */
+
+ var NAME_INDEX$1 = 0;
+ var PROP_INDEX = 1;
+ var TYPE_INDEX = 2;
+ var VALUE_INDEX = 3;
+
+ /**
+ * 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
+ * @typedef {import("./types.js").Geo} Geo
+ * Imports the 'Geo' type from the "types.js" module
+ */
+
+ /**
+ * Provides a layer on top of the raw jCal object for manipulating a single property, with its
+ * parameters and value.
+ *
+ * @memberof ICAL
+ */
+ var Property = /*#__PURE__*/function () {
+ /**
+ * Creates a new ICAL.Property instance.
+ *
+ * It is important to note that mutations done in the wrapper directly mutate the jCal object used
+ * to initialize.
+ *
+ * Can also be used to create new properties by passing the name of the property (as a String).
+ *
+ * @param {Array|String} jCal Raw jCal representation OR the new name of the property
+ * @param {Component=} parent Parent component
+ */
+ function Property(jCal, parent) {
+ _classCallCheck(this, Property);
+ this._parent = parent || null;
+ if (typeof jCal === 'string') {
+ // We are creating the property by name and need to detect the type
+ this.jCal = [jCal, {}, design.defaultType];
+ this.jCal[TYPE_INDEX] = this.getDefaultType();
+ } else {
+ this.jCal = jCal;
+ }
+ this._updateType();
+ }
+
+ /**
+ * The value type for this property
+ * @type {String}
+ */
+ return _createClass(Property, [{
+ key: "type",
+ get: function get() {
+ return this.jCal[TYPE_INDEX];
+ }
+
+ /**
+ * The name of this property, in lowercase.
+ * @type {String}
+ */
+ }, {
+ key: "name",
+ get: function get() {
+ return this.jCal[NAME_INDEX$1];
+ }
+
+ /**
+ * The parent component for this property.
+ * @type {Component}
+ */
+ }, {
+ key: "parent",
+ get: function get() {
+ return this._parent;
+ },
+ set: function set(p) {
+ // Before setting the parent, check if the design set has changed. If it
+ // has, we later need to update the type if it was unknown before.
+ var designSetChanged = !this._parent || p && p._designSet != this._parent._designSet;
+ this._parent = p;
+ if (this.type == design.defaultType && designSetChanged) {
+ this.jCal[TYPE_INDEX] = this.getDefaultType();
+ this._updateType();
+ }
+ }
+
+ /**
+ * The design set for this property, e.g. icalendar vs vcard
+ *
+ * @type {designSet}
+ * @private
+ */
+ }, {
+ key: "_designSet",
+ get: function get() {
+ return this.parent ? this.parent._designSet : design.defaultSet;
+ }
+
+ /**
+ * Updates the type metadata from the current jCal type and design set.
+ *
+ * @private
+ */
+ }, {
+ key: "_updateType",
+ value: function _updateType() {
+ var designSet = this._designSet;
+ if (this.type in designSet.value) {
+ if ('decorate' in designSet.value[this.type]) {
+ this.isDecorated = true;
+ } else {
+ this.isDecorated = false;
+ }
+ if (this.name in designSet.property) {
+ this.isMultiValue = 'multiValue' in designSet.property[this.name];
+ this.isStructuredValue = 'structuredValue' in designSet.property[this.name];
+ }
+ }
+ }
+
+ /**
+ * Hydrate a single value. The act of hydrating means turning the raw jCal
+ * value into a potentially wrapped object, for example {@link ICAL.Time}.
+ *
+ * @private
+ * @param {Number} index The index of the value to hydrate
+ * @return {?Object} The decorated value.
+ */
+ }, {
+ key: "_hydrateValue",
+ value: function _hydrateValue(index) {
+ if (this._values && this._values[index]) {
+ return this._values[index];
+ }
+
+ // for the case where there is no value.
+ if (this.jCal.length <= VALUE_INDEX + index) {
+ return null;
+ }
+ if (this.isDecorated) {
+ if (!this._values) {
+ this._values = [];
+ }
+ return this._values[index] = this._decorate(this.jCal[VALUE_INDEX + index]);
+ } else {
+ return this.jCal[VALUE_INDEX + index];
+ }
+ }
+
+ /**
+ * Decorate a single value, returning its wrapped object. This is used by
+ * the hydrate function to actually wrap the value.
+ *
+ * @private
+ * @param {?} value The value to decorate
+ * @return {Object} The decorated value
+ */
+ }, {
+ key: "_decorate",
+ value: function _decorate(value) {
+ return this._designSet.value[this.type].decorate(value, this);
+ }
+
+ /**
+ * Undecorate a single value, returning its raw jCal data.
+ *
+ * @private
+ * @param {Object} value The value to undecorate
+ * @return {?} The undecorated value
+ */
+ }, {
+ key: "_undecorate",
+ value: function _undecorate(value) {
+ return this._designSet.value[this.type].undecorate(value, this);
+ }
+
+ /**
+ * Sets the value at the given index while also hydrating it. The passed
+ * value can either be a decorated or undecorated value.
+ *
+ * @private
+ * @param {?} value The value to set
+ * @param {Number} index The index to set it at
+ */
+ }, {
+ key: "_setDecoratedValue",
+ value: function _setDecoratedValue(value, index) {
+ if (!this._values) {
+ this._values = [];
+ }
+ if (_typeof(value) === 'object' && 'icaltype' in value) {
+ // decorated value
+ this.jCal[VALUE_INDEX + index] = this._undecorate(value);
+ this._values[index] = value;
+ } else {
+ // undecorated value
+ this.jCal[VALUE_INDEX + index] = value;
+ this._values[index] = this._decorate(value);
+ }
+ }
+
+ /**
+ * Gets a parameter on the property.
+ *
+ * @param {String} name Parameter name (lowercase)
+ * @return {Array|String} Parameter value
+ */
+ }, {
+ key: "getParameter",
+ value: function getParameter(name) {
+ if (name in this.jCal[PROP_INDEX]) {
+ return this.jCal[PROP_INDEX][name];
+ } else {
+ return undefined;
+ }
+ }
+
+ /**
+ * Gets first parameter on the property.
+ *
+ * @param {String} name Parameter name (lowercase)
+ * @return {String} Parameter value
+ */
+ }, {
+ key: "getFirstParameter",
+ value: function getFirstParameter(name) {
+ var parameters = this.getParameter(name);
+ if (Array.isArray(parameters)) {
+ return parameters[0];
+ }
+ return parameters;
+ }
+
+ /**
+ * Sets a parameter on the property.
+ *
+ * @param {String} name The parameter name
+ * @param {Array|String} value The parameter value
+ */
+ }, {
+ key: "setParameter",
+ value: function setParameter(name, value) {
+ var lcname = name.toLowerCase();
+ if (typeof value === "string" && lcname in this._designSet.param && 'multiValue' in this._designSet.param[lcname]) {
+ value = [value];
+ }
+ this.jCal[PROP_INDEX][name] = value;
+ }
+
+ /**
+ * Removes a parameter
+ *
+ * @param {String} name The parameter name
+ */
+ }, {
+ key: "removeParameter",
+ value: function removeParameter(name) {
+ delete this.jCal[PROP_INDEX][name];
+ }
+
+ /**
+ * Get the default type based on this property's name.
+ *
+ * @return {String} The default type for this property
+ */
+ }, {
+ key: "getDefaultType",
+ value: function getDefaultType() {
+ var name = this.jCal[NAME_INDEX$1];
+ var designSet = this._designSet;
+ if (name in designSet.property) {
+ var details = designSet.property[name];
+ if ('defaultType' in details) {
+ return details.defaultType;
+ }
+ }
+ return design.defaultType;
+ }
+
+ /**
+ * Sets type of property and clears out any existing values of the current
+ * type.
+ *
+ * @param {String} type New iCAL type (see design.*.values)
+ */
+ }, {
+ key: "resetType",
+ value: function resetType(type) {
+ this.removeAllValues();
+ this.jCal[TYPE_INDEX] = type;
+ this._updateType();
+ }
+
+ /**
+ * Finds the first property value.
+ *
+ * @return {Binary | Duration | Period |
+ * Recur | Time | UtcOffset | Geo | string | null} First property value
+ */
+ }, {
+ key: "getFirstValue",
+ value: function getFirstValue() {
+ return this._hydrateValue(0);
+ }
+
+ /**
+ * Gets all values on the property.
+ *
+ * NOTE: this creates an array during each call.
+ *
+ * @return {Array} List of values
+ */
+ }, {
+ key: "getValues",
+ value: function getValues() {
+ var len = this.jCal.length - VALUE_INDEX;
+ if (len < 1) {
+ // it is possible for a property to have no value.
+ return [];
+ }
+ var i = 0;
+ var result = [];
+ for (; i < len; i++) {
+ result[i] = this._hydrateValue(i);
+ }
+ return result;
+ }
+
+ /**
+ * Removes all values from this property
+ */
+ }, {
+ key: "removeAllValues",
+ value: function removeAllValues() {
+ if (this._values) {
+ this._values.length = 0;
+ }
+ this.jCal.length = 3;
+ }
+
+ /**
+ * Sets the values of the property. Will overwrite the existing values.
+ * This can only be used for multi-value properties.
+ *
+ * @param {Array} values An array of values
+ */
+ }, {
+ key: "setValues",
+ value: function setValues(values) {
+ if (!this.isMultiValue) {
+ throw new Error(this.name + ': does not not support mulitValue.\n' + 'override isMultiValue');
+ }
+ var len = values.length;
+ var i = 0;
+ this.removeAllValues();
+ if (len > 0 && _typeof(values[0]) === 'object' && 'icaltype' in values[0]) {
+ this.resetType(values[0].icaltype);
+ }
+ if (this.isDecorated) {
+ for (; i < len; i++) {
+ this._setDecoratedValue(values[i], i);
+ }
+ } else {
+ for (; i < len; i++) {
+ this.jCal[VALUE_INDEX + i] = values[i];
+ }
+ }
+ }
+
+ /**
+ * Sets the current value of the property. If this is a multi-value
+ * property, all other values will be removed.
+ *
+ * @param {String|Object} value New property value.
+ */
+ }, {
+ key: "setValue",
+ value: function setValue(value) {
+ this.removeAllValues();
+ if (_typeof(value) === 'object' && 'icaltype' in value) {
+ this.resetType(value.icaltype);
+ }
+ if (this.isDecorated) {
+ this._setDecoratedValue(value, 0);
+ } else {
+ this.jCal[VALUE_INDEX] = value;
+ }
+ }
+
+ /**
+ * Returns the Object representation of this component. The returned object
+ * is a live jCal object and should be cloned if modified.
+ * @return {Object}
+ */
+ }, {
+ key: "toJSON",
+ value: function toJSON() {
+ return this.jCal;
+ }
+
+ /**
+ * The string representation of this component.
+ * @return {String}
+ */
+ }, {
+ key: "toICALString",
+ value: function toICALString() {
+ return stringify.property(this.jCal, this._designSet, true);
+ }
+ }], [{
+ key: "fromString",
+ value:
+ /**
+ * Create an {@link ICAL.Property} by parsing the passed iCalendar string.
+ *
+ * @param {String} str The iCalendar string to parse
+ * @param {designSet=} designSet The design data to use for this property
+ * @return {Property} The created iCalendar property
+ */
+ function fromString(str, designSet) {
+ return new Property(parse.property(str, designSet));
+ }
+ }]);
+ }();
+
+ /**
+ * 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
+ * @typedef {import("./types.js").Geo} Geo
+ * Imports the 'Geo' type from the "types.js" module
+ */
+
+ var NAME_INDEX = 0;
+ var PROPERTY_INDEX = 1;
+ var COMPONENT_INDEX = 2;
+ var PROPERTY_NAME_INDEX = 0;
+ var PROPERTY_VALUE_INDEX = 3;
+
+ /**
+ * Wraps a jCal component, adding convenience methods to add, remove and update subcomponents and
+ * properties.
+ *
+ * @memberof ICAL
+ */
+ var Component = /*#__PURE__*/function () {
+ /**
+ * Creates a new Component instance.
+ *
+ * @param {Array|String} jCal Raw jCal component data OR name of new
+ * component
+ * @param {Component=} parent Parent component to associate
+ */
+ function Component(jCal, parent) {
+ _classCallCheck(this, Component);
+ /**
+ * Hydrated properties are inserted into the _properties array at the same
+ * position as in the jCal array, so it is possible that the array contains
+ * undefined values for unhydrdated properties. To avoid iterating the
+ * array when checking if all properties have been hydrated, we save the
+ * count here.
+ *
+ * @type {Number}
+ * @private
+ */
+ _defineProperty(this, "_hydratedPropertyCount", 0);
+ /**
+ * The same count as for _hydratedPropertyCount, but for subcomponents
+ *
+ * @type {Number}
+ * @private
+ */
+ _defineProperty(this, "_hydratedComponentCount", 0);
+ /**
+ * A cache of hydrated time zone objects which may be used by consumers, keyed
+ * by time zone ID.
+ *
+ * @type {Map}
+ * @private
+ */
+ _defineProperty(this, "_timezoneCache", null);
+ /**
+ * @private
+ */
+ _defineProperty(this, "_components", null);
+ /**
+ * @private
+ */
+ _defineProperty(this, "_properties", null);
+ if (typeof jCal === 'string') {
+ // jCal spec (name, properties, components)
+ jCal = [jCal, [], []];
+ }
+
+ // mostly for legacy reasons.
+ this.jCal = jCal;
+ this.parent = parent || null;
+ if (!this.parent && this.name === 'vcalendar') {
+ this._timezoneCache = new Map();
+ }
+ }
+ return _createClass(Component, [{
+ key: "name",
+ get:
+ /**
+ * The name of this component
+ *
+ * @type {String}
+ */
+ function get() {
+ return this.jCal[NAME_INDEX];
+ }
+
+ /**
+ * The design set for this component, e.g. icalendar vs vcard
+ *
+ * @type {designSet}
+ * @private
+ */
+ }, {
+ key: "_designSet",
+ get: function get() {
+ var parentDesign = this.parent && this.parent._designSet;
+ if (!parentDesign && this.name == "vcard") {
+ var _this$jCal$PROPERTY_I;
+ // We can't decide on vcard3 vs vcard4 just based on the component name, the version number is
+ // in the version property. We also can't use hydrated properties here because it would lead
+ // to recursion, but the spec says that the version property needs to be the very first one.
+ var versionProp = (_this$jCal$PROPERTY_I = this.jCal[PROPERTY_INDEX]) === null || _this$jCal$PROPERTY_I === void 0 ? void 0 : _this$jCal$PROPERTY_I[0];
+ if (versionProp && versionProp[PROPERTY_NAME_INDEX] == "version" && versionProp[PROPERTY_VALUE_INDEX] == "3.0") {
+ return design.getDesignSet("vcard3");
+ }
+ }
+ return parentDesign || design.getDesignSet(this.name);
+ }
+
+ /**
+ * @private
+ */
+ }, {
+ key: "_hydrateComponent",
+ value: function _hydrateComponent(index) {
+ if (!this._components) {
+ this._components = [];
+ this._hydratedComponentCount = 0;
+ }
+ if (this._components[index]) {
+ return this._components[index];
+ }
+ var comp = new Component(this.jCal[COMPONENT_INDEX][index], this);
+ this._hydratedComponentCount++;
+ return this._components[index] = comp;
+ }
+
+ /**
+ * @private
+ */
+ }, {
+ key: "_hydrateProperty",
+ value: function _hydrateProperty(index) {
+ if (!this._properties) {
+ this._properties = [];
+ this._hydratedPropertyCount = 0;
+ }
+ if (this._properties[index]) {
+ return this._properties[index];
+ }
+ var prop = new Property(this.jCal[PROPERTY_INDEX][index], this);
+ this._hydratedPropertyCount++;
+ return this._properties[index] = prop;
+ }
+
+ /**
+ * Finds first sub component, optionally filtered by name.
+ *
+ * @param {String=} name Optional name to filter by
+ * @return {?Component} The found subcomponent
+ */
+ }, {
+ key: "getFirstSubcomponent",
+ value: function getFirstSubcomponent(name) {
+ if (name) {
+ var i = 0;
+ var comps = this.jCal[COMPONENT_INDEX];
+ var len = comps.length;
+ for (; i < len; i++) {
+ if (comps[i][NAME_INDEX] === name) {
+ var result = this._hydrateComponent(i);
+ return result;
+ }
+ }
+ } else {
+ if (this.jCal[COMPONENT_INDEX].length) {
+ return this._hydrateComponent(0);
+ }
+ }
+
+ // ensure we return a value (strict mode)
+ return null;
+ }
+
+ /**
+ * Finds all sub components, optionally filtering by name.
+ *
+ * @param {String=} name Optional name to filter by
+ * @return {Component[]} The found sub components
+ */
+ }, {
+ key: "getAllSubcomponents",
+ value: function getAllSubcomponents(name) {
+ var jCalLen = this.jCal[COMPONENT_INDEX].length;
+ var i = 0;
+ if (name) {
+ var comps = this.jCal[COMPONENT_INDEX];
+ var result = [];
+ for (; i < jCalLen; i++) {
+ if (name === comps[i][NAME_INDEX]) {
+ result.push(this._hydrateComponent(i));
+ }
+ }
+ return result;
+ } else {
+ if (!this._components || this._hydratedComponentCount !== jCalLen) {
+ for (; i < jCalLen; i++) {
+ this._hydrateComponent(i);
+ }
+ }
+ return this._components || [];
+ }
+ }
+
+ /**
+ * Returns true when a named property exists.
+ *
+ * @param {String} name The property name
+ * @return {Boolean} True, when property is found
+ */
+ }, {
+ key: "hasProperty",
+ value: function hasProperty(name) {
+ var props = this.jCal[PROPERTY_INDEX];
+ var len = props.length;
+ var i = 0;
+ for (; i < len; i++) {
+ // 0 is property name
+ if (props[i][NAME_INDEX] === name) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Finds the first property, optionally with the given name.
+ *
+ * @param {String=} name Lowercase property name
+ * @return {?Property} The found property
+ */
+ }, {
+ key: "getFirstProperty",
+ value: function getFirstProperty(name) {
+ if (name) {
+ var i = 0;
+ var props = this.jCal[PROPERTY_INDEX];
+ var len = props.length;
+ for (; i < len; i++) {
+ if (props[i][NAME_INDEX] === name) {
+ var result = this._hydrateProperty(i);
+ return result;
+ }
+ }
+ } else {
+ if (this.jCal[PROPERTY_INDEX].length) {
+ return this._hydrateProperty(0);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns first property's value, if available.
+ *
+ * @param {String=} name Lowercase property name
+ * @return {Binary | Duration | Period |
+ * Recur | Time | UtcOffset | Geo | string | null} The found property value.
+ */
+ }, {
+ key: "getFirstPropertyValue",
+ value: function getFirstPropertyValue(name) {
+ var prop = this.getFirstProperty(name);
+ if (prop) {
+ return prop.getFirstValue();
+ }
+ return null;
+ }
+
+ /**
+ * Get all properties in the component, optionally filtered by name.
+ *
+ * @param {String=} name Lowercase property name
+ * @return {Property[]} List of properties
+ */
+ }, {
+ key: "getAllProperties",
+ value: function getAllProperties(name) {
+ var jCalLen = this.jCal[PROPERTY_INDEX].length;
+ var i = 0;
+ if (name) {
+ var props = this.jCal[PROPERTY_INDEX];
+ var result = [];
+ for (; i < jCalLen; i++) {
+ if (name === props[i][NAME_INDEX]) {
+ result.push(this._hydrateProperty(i));
+ }
+ }
+ return result;
+ } else {
+ if (!this._properties || this._hydratedPropertyCount !== jCalLen) {
+ for (; i < jCalLen; i++) {
+ this._hydrateProperty(i);
+ }
+ }
+ return this._properties || [];
+ }
+ }
+
+ /**
+ * @private
+ */
+ }, {
+ key: "_removeObjectByIndex",
+ value: function _removeObjectByIndex(jCalIndex, cache, index) {
+ cache = cache || [];
+ // remove cached version
+ if (cache[index]) {
+ var obj = cache[index];
+ if ("parent" in obj) {
+ obj.parent = null;
+ }
+ }
+ cache.splice(index, 1);
+
+ // remove it from the jCal
+ this.jCal[jCalIndex].splice(index, 1);
+ }
+
+ /**
+ * @private
+ */
+ }, {
+ key: "_removeObject",
+ value: function _removeObject(jCalIndex, cache, nameOrObject) {
+ var i = 0;
+ var objects = this.jCal[jCalIndex];
+ var len = objects.length;
+ var cached = this[cache];
+ if (typeof nameOrObject === 'string') {
+ for (; i < len; i++) {
+ if (objects[i][NAME_INDEX] === nameOrObject) {
+ this._removeObjectByIndex(jCalIndex, cached, i);
+ return true;
+ }
+ }
+ } else if (cached) {
+ for (; i < len; i++) {
+ if (cached[i] && cached[i] === nameOrObject) {
+ this._removeObjectByIndex(jCalIndex, cached, i);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @private
+ */
+ }, {
+ key: "_removeAllObjects",
+ value: function _removeAllObjects(jCalIndex, cache, name) {
+ var cached = this[cache];
+
+ // Unfortunately we have to run through all children to reset their
+ // parent property.
+ var objects = this.jCal[jCalIndex];
+ var i = objects.length - 1;
+
+ // descending search required because splice
+ // is used and will effect the indices.
+ for (; i >= 0; i--) {
+ if (!name || objects[i][NAME_INDEX] === name) {
+ this._removeObjectByIndex(jCalIndex, cached, i);
+ }
+ }
+ }
+
+ /**
+ * Adds a single sub component.
+ *
+ * @param {Component} component The component to add
+ * @return {Component} The passed in component
+ */
+ }, {
+ key: "addSubcomponent",
+ value: function addSubcomponent(component) {
+ if (!this._components) {
+ this._components = [];
+ this._hydratedComponentCount = 0;
+ }
+ if (component.parent) {
+ component.parent.removeSubcomponent(component);
+ }
+ var idx = this.jCal[COMPONENT_INDEX].push(component.jCal);
+ this._components[idx - 1] = component;
+ this._hydratedComponentCount++;
+ component.parent = this;
+ return component;
+ }
+
+ /**
+ * Removes a single component by name or the instance of a specific
+ * component.
+ *
+ * @param {Component|String} nameOrComp Name of component, or component
+ * @return {Boolean} True when comp is removed
+ */
+ }, {
+ key: "removeSubcomponent",
+ value: function removeSubcomponent(nameOrComp) {
+ var removed = this._removeObject(COMPONENT_INDEX, '_components', nameOrComp);
+ if (removed) {
+ this._hydratedComponentCount--;
+ }
+ return removed;
+ }
+
+ /**
+ * Removes all components or (if given) all components by a particular
+ * name.
+ *
+ * @param {String=} name Lowercase component name
+ */
+ }, {
+ key: "removeAllSubcomponents",
+ value: function removeAllSubcomponents(name) {
+ var removed = this._removeAllObjects(COMPONENT_INDEX, '_components', name);
+ this._hydratedComponentCount = 0;
+ return removed;
+ }
+
+ /**
+ * Adds an {@link ICAL.Property} to the component.
+ *
+ * @param {Property} property The property to add
+ * @return {Property} The passed in property
+ */
+ }, {
+ key: "addProperty",
+ value: function addProperty(property) {
+ if (!(property instanceof Property)) {
+ throw new TypeError('must be instance of ICAL.Property');
+ }
+ if (!this._properties) {
+ this._properties = [];
+ this._hydratedPropertyCount = 0;
+ }
+ if (property.parent) {
+ property.parent.removeProperty(property);
+ }
+ var idx = this.jCal[PROPERTY_INDEX].push(property.jCal);
+ this._properties[idx - 1] = property;
+ this._hydratedPropertyCount++;
+ property.parent = this;
+ return property;
+ }
+
+ /**
+ * Helper method to add a property with a value to the component.
+ *
+ * @param {String} name Property name to add
+ * @param {String|Number|Object} value Property value
+ * @return {Property} The created property
+ */
+ }, {
+ key: "addPropertyWithValue",
+ value: function addPropertyWithValue(name, value) {
+ var prop = new Property(name);
+ prop.setValue(value);
+ this.addProperty(prop);
+ return prop;
+ }
+
+ /**
+ * Helper method that will update or create a property of the given name
+ * and sets its value. If multiple properties with the given name exist,
+ * only the first is updated.
+ *
+ * @param {String} name Property name to update
+ * @param {String|Number|Object} value Property value
+ * @return {Property} The created property
+ */
+ }, {
+ key: "updatePropertyWithValue",
+ value: function updatePropertyWithValue(name, value) {
+ var prop = this.getFirstProperty(name);
+ if (prop) {
+ prop.setValue(value);
+ } else {
+ prop = this.addPropertyWithValue(name, value);
+ }
+ return prop;
+ }
+
+ /**
+ * Removes a single property by name or the instance of the specific
+ * property.
+ *
+ * @param {String|Property} nameOrProp Property name or instance to remove
+ * @return {Boolean} True, when deleted
+ */
+ }, {
+ key: "removeProperty",
+ value: function removeProperty(nameOrProp) {
+ var removed = this._removeObject(PROPERTY_INDEX, '_properties', nameOrProp);
+ if (removed) {
+ this._hydratedPropertyCount--;
+ }
+ return removed;
+ }
+
+ /**
+ * Removes all properties associated with this component, optionally
+ * filtered by name.
+ *
+ * @param {String=} name Lowercase property name
+ * @return {Boolean} True, when deleted
+ */
+ }, {
+ key: "removeAllProperties",
+ value: function removeAllProperties(name) {
+ var removed = this._removeAllObjects(PROPERTY_INDEX, '_properties', name);
+ this._hydratedPropertyCount = 0;
+ return removed;
+ }
+
+ /**
+ * Returns the Object representation of this component. The returned object
+ * is a live jCal object and should be cloned if modified.
+ * @return {Object}
+ */
+ }, {
+ key: "toJSON",
+ value: function toJSON() {
+ return this.jCal;
+ }
+
+ /**
+ * The string representation of this component.
+ * @return {String}
+ */
+ }, {
+ key: "toString",
+ value: function toString() {
+ return stringify.component(this.jCal, this._designSet);
+ }
+
+ /**
+ * Retrieve a time zone definition from the component tree, if any is present.
+ * If the tree contains no time zone definitions or the TZID cannot be
+ * matched, returns null.
+ *
+ * @param {String} tzid The ID of the time zone to retrieve
+ * @return {Timezone} The time zone corresponding to the ID, or null
+ */
+ }, {
+ key: "getTimeZoneByID",
+ value: function getTimeZoneByID(tzid) {
+ // VTIMEZONE components can only appear as a child of the VCALENDAR
+ // component; walk the tree if we're not the root.
+ if (this.parent) {
+ return this.parent.getTimeZoneByID(tzid);
+ }
+
+ // If there is no time zone cache, we are probably parsing an incomplete
+ // file and will have no time zone definitions.
+ if (!this._timezoneCache) {
+ return null;
+ }
+ if (this._timezoneCache.has(tzid)) {
+ return this._timezoneCache.get(tzid);
+ }
+
+ // If the time zone is not already cached, hydrate it from the
+ // subcomponents.
+ var zones = this.getAllSubcomponents('vtimezone');
+ var _iterator = _createForOfIteratorHelper(zones),
+ _step;
+ try {
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
+ var zone = _step.value;
+ if (zone.getFirstProperty('tzid').getFirstValue() === tzid) {
+ var hydratedZone = new Timezone({
+ component: zone,
+ tzid: tzid
+ });
+ this._timezoneCache.set(tzid, hydratedZone);
+ return hydratedZone;
+ }
+ }
+
+ // Per the standard, we should always have a time zone defined in a file
+ // for any referenced TZID, but don't blow up if the file is invalid.
+ } catch (err) {
+ _iterator.e(err);
+ } finally {
+ _iterator.f();
+ }
+ return null;
+ }
+ }], [{
+ key: "fromString",
+ value:
+ /**
+ * Create an {@link ICAL.Component} by parsing the passed iCalendar string.
+ *
+ * @param {String} str The iCalendar string to parse
+ */
+ function fromString(str) {
+ return new Component(parse.component(str));
+ }
+ }]);
+ }();
+
+ /**
+ * Primary class for expanding recurring rules. Can take multiple rrules, rdates, exdate(s) and
+ * iterate (in order) over each next occurrence.
+ *
+ * Once initialized this class can also be serialized saved and continue iteration from the last
+ * point.
+ *
+ * NOTE: it is intended that this class is to be used with {@link ICAL.Event} which handles recurrence
+ * exceptions.
+ *
+ * @example
+ * // assuming event is a parsed ical component
+ * var event;
+ *
+ * var expand = new ICAL.RecurExpansion({
+ * component: event,
+ * dtstart: event.getFirstPropertyValue('dtstart')
+ * });
+ *
+ * // remember there are infinite rules so it is a good idea to limit the scope of the iterations
+ * // then resume later on.
+ *
+ * // next is always an ICAL.Time or null
+ * var next;
+ *
+ * while (someCondition && (next = expand.next())) {
+ * // do something with next
+ * }
+ *
+ * // save instance for later
+ * var json = JSON.stringify(expand);
+ *
+ * //...
+ *
+ * // NOTE: if the component's properties have changed you will need to rebuild the class and start
+ * // over. This only works when the component's recurrence info is the same.
+ * var expand = new ICAL.RecurExpansion(JSON.parse(json));
+ *
+ * @memberof ICAL
+ */
+ var RecurExpansion = /*#__PURE__*/function () {
+ /**
+ * Creates a new ICAL.RecurExpansion instance.
+ *
+ * The options object can be filled with the specified initial values. It can also contain
+ * additional members, as a result of serializing a previous expansion state, as shown in the
+ * example.
+ *
+ * @param {Object} options
+ * Recurrence expansion options
+ * @param {Time} options.dtstart
+ * Start time of the event
+ * @param {Component=} options.component
+ * Component for expansion, required if not resuming.
+ */
+ function RecurExpansion(options) {
+ _classCallCheck(this, RecurExpansion);
+ /**
+ * True when iteration is fully completed.
+ * @type {Boolean}
+ */
+ _defineProperty(this, "complete", false);
+ /**
+ * Array of rrule iterators.
+ *
+ * @type {RecurIterator[]}
+ * @private
+ */
+ _defineProperty(this, "ruleIterators", null);
+ /**
+ * Array of rdate instances.
+ *
+ * @type {Time[]}
+ * @private
+ */
+ _defineProperty(this, "ruleDates", null);
+ /**
+ * Array of exdate instances.
+ *
+ * @type {Time[]}
+ * @private
+ */
+ _defineProperty(this, "exDates", null);
+ /**
+ * Current position in ruleDates array.
+ * @type {Number}
+ * @private
+ */
+ _defineProperty(this, "ruleDateInc", 0);
+ /**
+ * Current position in exDates array
+ * @type {Number}
+ * @private
+ */
+ _defineProperty(this, "exDateInc", 0);
+ /**
+ * Current negative date.
+ *
+ * @type {Time}
+ * @private
+ */
+ _defineProperty(this, "exDate", null);
+ /**
+ * Current additional date.
+ *
+ * @type {Time}
+ * @private
+ */
+ _defineProperty(this, "ruleDate", null);
+ /**
+ * Start date of recurring rules.
+ *
+ * @type {Time}
+ */
+ _defineProperty(this, "dtstart", null);
+ /**
+ * Last expanded time
+ *
+ * @type {Time}
+ */
+ _defineProperty(this, "last", null);
+ this.ruleDates = [];
+ this.exDates = [];
+ this.fromData(options);
+ }
+ return _createClass(RecurExpansion, [{
+ key: "fromData",
+ value:
+ /**
+ * Initialize the recurrence expansion from the data object. The options
+ * object may also contain additional members, see the
+ * {@link ICAL.RecurExpansion constructor} for more details.
+ *
+ * @param {Object} options
+ * Recurrence expansion options
+ * @param {Time} options.dtstart
+ * Start time of the event
+ * @param {Component=} options.component
+ * Component for expansion, required if not resuming.
+ */
+ function fromData(options) {
+ var start = formatClassType(options.dtstart, Time);
+ if (!start) {
+ throw new Error('.dtstart (ICAL.Time) must be given');
+ } else {
+ this.dtstart = start;
+ }
+ if (options.component) {
+ this._init(options.component);
+ } else {
+ this.last = formatClassType(options.last, Time) || start.clone();
+ if (!options.ruleIterators) {
+ throw new Error('.ruleIterators or .component must be given');
+ }
+ this.ruleIterators = options.ruleIterators.map(function (item) {
+ return formatClassType(item, RecurIterator);
+ });
+ this.ruleDateInc = options.ruleDateInc;
+ this.exDateInc = options.exDateInc;
+ if (options.ruleDates) {
+ this.ruleDates = options.ruleDates.map(function (item) {
+ return formatClassType(item, Time);
+ });
+ this.ruleDate = this.ruleDates[this.ruleDateInc];
+ }
+ if (options.exDates) {
+ this.exDates = options.exDates.map(function (item) {
+ return formatClassType(item, Time);
+ });
+ this.exDate = this.exDates[this.exDateInc];
+ }
+ if (typeof options.complete !== 'undefined') {
+ this.complete = options.complete;
+ }
+ }
+ }
+
+ /**
+ * Compare two ICAL.Time objects. When the second parameter is a DATE and the first parameter is
+ * DATE-TIME, strip the time and compare only the days.
+ *
+ * @private
+ * @param {Time} a The one object to compare
+ * @param {Time} b The other object to compare
+ */
+ }, {
+ key: "_compare_special",
+ value: function _compare_special(a, b) {
+ if (!a.isDate && b.isDate) return new Time({
+ year: a.year,
+ month: a.month,
+ day: a.day
+ }).compare(b);
+ return a.compare(b);
+ }
+
+ /**
+ * Retrieve the next occurrence in the series.
+ * @return {Time}
+ */
+ }, {
+ key: "next",
+ value: function next() {
+ var iter;
+ var next;
+ var compare;
+ var maxTries = 500;
+ var currentTry = 0;
+ while (true) {
+ if (currentTry++ > maxTries) {
+ throw new Error('max tries have occurred, rule may be impossible to fulfill.');
+ }
+ next = this.ruleDate;
+ iter = this._nextRecurrenceIter(this.last);
+
+ // no more matches
+ // because we increment the rule day or rule
+ // _after_ we choose a value this should be
+ // the only spot where we need to worry about the
+ // end of events.
+ if (!next && !iter) {
+ // there are no more iterators or rdates
+ this.complete = true;
+ break;
+ }
+
+ // no next rule day or recurrence rule is first.
+ if (!next || iter && next.compare(iter.last) > 0) {
+ // must be cloned, recur will reuse the time element.
+ next = iter.last.clone();
+ // move to next so we can continue
+ iter.next();
+ }
+
+ // if the ruleDate is still next increment it.
+ if (this.ruleDate === next) {
+ this._nextRuleDay();
+ }
+ this.last = next;
+
+ // check the negative rules
+ if (this.exDate) {
+ // EXDATE can be in DATE format, but DTSTART is in DATE-TIME format
+ compare = this._compare_special(this.last, this.exDate);
+ if (compare > 0) {
+ this._nextExDay();
+ }
+
+ // if the current rule is excluded skip it.
+ if (compare === 0) {
+ this._nextExDay();
+ continue;
+ }
+ }
+
+ //XXX: The spec states that after we resolve the final
+ // list of dates we execute exdate this seems somewhat counter
+ // intuitive to what I have seen most servers do so for now
+ // I exclude based on the original date not the one that may
+ // have been modified by the exception.
+ return this.last;
+ }
+ }
+
+ /**
+ * Converts object into a serialize-able format. This format can be passed
+ * back into the expansion to resume iteration.
+ * @return {Object}
+ */
+ }, {
+ key: "toJSON",
+ value: function toJSON() {
+ function toJSON(item) {
+ return item.toJSON();
+ }
+ var result = Object.create(null);
+ result.ruleIterators = this.ruleIterators.map(toJSON);
+ if (this.ruleDates) {
+ result.ruleDates = this.ruleDates.map(toJSON);
+ }
+ if (this.exDates) {
+ result.exDates = this.exDates.map(toJSON);
+ }
+ result.ruleDateInc = this.ruleDateInc;
+ result.exDateInc = this.exDateInc;
+ result.last = this.last.toJSON();
+ result.dtstart = this.dtstart.toJSON();
+ result.complete = this.complete;
+ return result;
+ }
+
+ /**
+ * Extract all dates from the properties in the given component. The
+ * properties will be filtered by the property name.
+ *
+ * @private
+ * @param {Component} component The component to search in
+ * @param {String} propertyName The property name to search for
+ * @return {Time[]} The extracted dates.
+ */
+ }, {
+ key: "_extractDates",
+ value: function _extractDates(component, propertyName) {
+ var result = [];
+ var props = component.getAllProperties(propertyName);
+ for (var i = 0, len = props.length; i < len; i++) {
+ var _iterator = _createForOfIteratorHelper(props[i].getValues()),
+ _step;
+ try {
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
+ var prop = _step.value;
+ var idx = binsearchInsert(result, prop, function (a, b) {
+ return a.compare(b);
+ });
+
+ // ordered insert
+ result.splice(idx, 0, prop);
+ }
+ } catch (err) {
+ _iterator.e(err);
+ } finally {
+ _iterator.f();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Initialize the recurrence expansion.
+ *
+ * @private
+ * @param {Component} component The component to initialize from.
+ */
+ }, {
+ key: "_init",
+ value: function _init(component) {
+ this.ruleIterators = [];
+ this.last = this.dtstart.clone();
+
+ // to provide api consistency non-recurring
+ // events can also use the iterator though it will
+ // only return a single time.
+ if (!component.hasProperty('rdate') && !component.hasProperty('rrule') && !component.hasProperty('recurrence-id')) {
+ this.ruleDate = this.last.clone();
+ this.complete = true;
+ return;
+ }
+ if (component.hasProperty('rdate')) {
+ this.ruleDates = this._extractDates(component, 'rdate');
+
+ // special hack for cases where first rdate is prior
+ // to the start date. We only check for the first rdate.
+ // This is mostly for google's crazy recurring date logic
+ // (contacts birthdays).
+ if (this.ruleDates[0] && this.ruleDates[0].compare(this.dtstart) < 0) {
+ this.ruleDateInc = 0;
+ this.last = this.ruleDates[0].clone();
+ } else {
+ this.ruleDateInc = binsearchInsert(this.ruleDates, this.last, function (a, b) {
+ return a.compare(b);
+ });
+ }
+ this.ruleDate = this.ruleDates[this.ruleDateInc];
+ }
+ if (component.hasProperty('rrule')) {
+ var rules = component.getAllProperties('rrule');
+ var i = 0;
+ var len = rules.length;
+ var rule;
+ var iter;
+ for (; i < len; i++) {
+ rule = rules[i].getFirstValue();
+ iter = rule.iterator(this.dtstart);
+ this.ruleIterators.push(iter);
+
+ // increment to the next occurrence so future
+ // calls to next return times beyond the initial iteration.
+ // XXX: I find this suspicious might be a bug?
+ iter.next();
+ }
+ }
+ if (component.hasProperty('exdate')) {
+ this.exDates = this._extractDates(component, 'exdate');
+ // if we have a .last day we increment the index to beyond it.
+ // When DTSTART is in DATE-TIME format, EXDATE is in DATE format and EXDATE is
+ // the date of DTSTART, _compare_special finds this out and compareTime fails.
+ this.exDateInc = binsearchInsert(this.exDates, this.last, this._compare_special);
+ this.exDate = this.exDates[this.exDateInc];
+ }
+ }
+
+ /**
+ * Advance to the next exdate
+ * @private
+ */
+ }, {
+ key: "_nextExDay",
+ value: function _nextExDay() {
+ this.exDate = this.exDates[++this.exDateInc];
+ }
+
+ /**
+ * Advance to the next rule date
+ * @private
+ */
+ }, {
+ key: "_nextRuleDay",
+ value: function _nextRuleDay() {
+ this.ruleDate = this.ruleDates[++this.ruleDateInc];
+ }
+
+ /**
+ * Find and return the recurrence rule with the most recent event and
+ * return it.
+ *
+ * @private
+ * @return {?RecurIterator} Found iterator.
+ */
+ }, {
+ key: "_nextRecurrenceIter",
+ value: function _nextRecurrenceIter() {
+ var iters = this.ruleIterators;
+ if (iters.length === 0) {
+ return null;
+ }
+ var len = iters.length;
+ var iter;
+ var iterTime;
+ var iterIdx = 0;
+ var chosenIter;
+
+ // loop through each iterator
+ for (; iterIdx < len; iterIdx++) {
+ iter = iters[iterIdx];
+ iterTime = iter.last;
+
+ // if iteration is complete
+ // then we must exclude it from
+ // the search and remove it.
+ if (iter.completed) {
+ len--;
+ if (iterIdx !== 0) {
+ iterIdx--;
+ }
+ iters.splice(iterIdx, 1);
+ continue;
+ }
+
+ // find the most recent possible choice
+ if (!chosenIter || chosenIter.last.compare(iterTime) > 0) {
+ // that iterator is saved
+ chosenIter = iter;
+ }
+ }
+
+ // the chosen iterator is returned but not mutated
+ // this iterator contains the most recent event.
+ return chosenIter;
+ }
+ }]);
+ }();
+
+ /**
+ * 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").frequencyValues} frequencyValues
+ * Imports the 'frequencyValues' type from the "types.js" module
+ * @typedef {import("./types.js").occurrenceDetails} occurrenceDetails
+ * Imports the 'occurrenceDetails' type from the "types.js" module
+ */
+
+ /**
+ * ICAL.js is organized into multiple layers. The bottom layer is a raw jCal
+ * object, followed by the component/property layer. The highest level is the
+ * event representation, which this class is part of. See the
+ * {@tutorial layers} guide for more details.
+ *
+ * @memberof ICAL
+ */
+ var Event = /*#__PURE__*/function () {
+ /**
+ * Creates a new ICAL.Event instance.
+ *
+ * @param {Component=} component The ICAL.Component to base this event on
+ * @param {Object} [options] Options for this event
+ * @param {Boolean=} options.strictExceptions When true, will verify exceptions are related by
+ * their UUID
+ * @param {Array=} options.exceptions
+ * Exceptions to this event, either as components or events. If not
+ * specified exceptions will automatically be set in relation of
+ * component's parent
+ */
+ function Event(component, options) {
+ _classCallCheck(this, Event);
+ /**
+ * List of related event exceptions.
+ *
+ * @type {Event[]}
+ */
+ _defineProperty(this, "exceptions", null);
+ /**
+ * When true, will verify exceptions are related by their UUID.
+ *
+ * @type {Boolean}
+ */
+ _defineProperty(this, "strictExceptions", false);
+ if (!(component instanceof Component)) {
+ options = component;
+ component = null;
+ }
+ if (component) {
+ this.component = component;
+ } else {
+ this.component = new Component('vevent');
+ }
+ this._rangeExceptionCache = Object.create(null);
+ this.exceptions = Object.create(null);
+ this.rangeExceptions = [];
+ if (options && options.strictExceptions) {
+ this.strictExceptions = options.strictExceptions;
+ }
+ if (options && options.exceptions) {
+ options.exceptions.forEach(this.relateException, this);
+ } else if (this.component.parent && !this.isRecurrenceException()) {
+ this.component.parent.getAllSubcomponents('vevent').forEach(function (event) {
+ if (event.hasProperty('recurrence-id')) {
+ this.relateException(event);
+ }
+ }, this);
+ }
+ }
+ return _createClass(Event, [{
+ key: "relateException",
+ value:
+ /**
+ * Relates a given event exception to this object. If the given component
+ * does not share the UID of this event it cannot be related and will throw
+ * an exception.
+ *
+ * If this component is an exception it cannot have other exceptions
+ * related to it.
+ *
+ * @param {Component|Event} obj Component or event
+ */
+ function relateException(obj) {
+ if (this.isRecurrenceException()) {
+ throw new Error('cannot relate exception to exceptions');
+ }
+ if (obj instanceof Component) {
+ obj = new Event(obj);
+ }
+ if (this.strictExceptions && obj.uid !== this.uid) {
+ throw new Error('attempted to relate unrelated exception');
+ }
+ var id = obj.recurrenceId.toString();
+
+ // we don't sort or manage exceptions directly
+ // here the recurrence expander handles that.
+ this.exceptions[id] = obj;
+
+ // index RANGE=THISANDFUTURE exceptions so we can
+ // look them up later in getOccurrenceDetails.
+ if (obj.modifiesFuture()) {
+ var item = [obj.recurrenceId.toUnixTime(), id];
+
+ // we keep them sorted so we can find the nearest
+ // value later on...
+ var idx = binsearchInsert(this.rangeExceptions, item, compareRangeException);
+ this.rangeExceptions.splice(idx, 0, item);
+ }
+ }
+
+ /**
+ * Checks if this record is an exception and has the RANGE=THISANDFUTURE
+ * value.
+ *
+ * @return {Boolean} True, when exception is within range
+ */
+ }, {
+ key: "modifiesFuture",
+ value: function modifiesFuture() {
+ if (!this.component.hasProperty('recurrence-id')) {
+ return false;
+ }
+ var range = this.component.getFirstProperty('recurrence-id').getParameter('range');
+ return range === Event.THISANDFUTURE;
+ }
+
+ /**
+ * Finds the range exception nearest to the given date.
+ *
+ * @param {Time} time usually an occurrence time of an event
+ * @return {?Event} the related event/exception or null
+ */
+ }, {
+ key: "findRangeException",
+ value: function findRangeException(time) {
+ if (!this.rangeExceptions.length) {
+ return null;
+ }
+ var utc = time.toUnixTime();
+ var idx = binsearchInsert(this.rangeExceptions, [utc], compareRangeException);
+ idx -= 1;
+
+ // occurs before
+ if (idx < 0) {
+ return null;
+ }
+ var rangeItem = this.rangeExceptions[idx];
+
+ /* c8 ignore next 4 */
+ if (utc < rangeItem[0]) {
+ // sanity check only
+ return null;
+ }
+ return rangeItem[1];
+ }
+
+ /**
+ * Returns the occurrence details based on its start time. If the
+ * occurrence has an exception will return the details for that exception.
+ *
+ * NOTE: this method is intend to be used in conjunction
+ * with the {@link ICAL.Event#iterator iterator} method.
+ *
+ * @param {Time} occurrence time occurrence
+ * @return {occurrenceDetails} Information about the occurrence
+ */
+ }, {
+ key: "getOccurrenceDetails",
+ value: function getOccurrenceDetails(occurrence) {
+ var id = occurrence.toString();
+ var utcId = occurrence.convertToZone(Timezone.utcTimezone).toString();
+ var item;
+ var result = {
+ //XXX: Clone?
+ recurrenceId: occurrence
+ };
+ if (id in this.exceptions) {
+ item = result.item = this.exceptions[id];
+ result.startDate = item.startDate;
+ result.endDate = item.endDate;
+ result.item = item;
+ } else if (utcId in this.exceptions) {
+ item = this.exceptions[utcId];
+ result.startDate = item.startDate;
+ result.endDate = item.endDate;
+ result.item = item;
+ } else {
+ // range exceptions (RANGE=THISANDFUTURE) have a
+ // lower priority then direct exceptions but
+ // must be accounted for first. Their item is
+ // always the first exception with the range prop.
+ var rangeExceptionId = this.findRangeException(occurrence);
+ var end;
+ if (rangeExceptionId) {
+ var exception = this.exceptions[rangeExceptionId];
+
+ // range exception must modify standard time
+ // by the difference (if any) in start/end times.
+ result.item = exception;
+ var startDiff = this._rangeExceptionCache[rangeExceptionId];
+ if (!startDiff) {
+ var original = exception.recurrenceId.clone();
+ var newStart = exception.startDate.clone();
+
+ // zones must be same otherwise subtract may be incorrect.
+ original.zone = newStart.zone;
+ startDiff = newStart.subtractDate(original);
+ this._rangeExceptionCache[rangeExceptionId] = startDiff;
+ }
+ var start = occurrence.clone();
+ start.zone = exception.startDate.zone;
+ start.addDuration(startDiff);
+ end = start.clone();
+ end.addDuration(exception.duration);
+ result.startDate = start;
+ result.endDate = end;
+ } else {
+ // no range exception standard expansion
+ end = occurrence.clone();
+ end.addDuration(this.duration);
+ result.endDate = end;
+ result.startDate = occurrence;
+ result.item = this;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Builds a recur expansion instance for a specific point in time (defaults
+ * to startDate).
+ *
+ * @param {Time=} startTime Starting point for expansion
+ * @return {RecurExpansion} Expansion object
+ */
+ }, {
+ key: "iterator",
+ value: function iterator(startTime) {
+ return new RecurExpansion({
+ component: this.component,
+ dtstart: startTime || this.startDate
+ });
+ }
+
+ /**
+ * Checks if the event is recurring
+ *
+ * @return {Boolean} True, if event is recurring
+ */
+ }, {
+ key: "isRecurring",
+ value: function isRecurring() {
+ var comp = this.component;
+ return comp.hasProperty('rrule') || comp.hasProperty('rdate');
+ }
+
+ /**
+ * Checks if the event describes a recurrence exception. See
+ * {@tutorial terminology} for details.
+ *
+ * @return {Boolean} True, if the event describes a recurrence exception
+ */
+ }, {
+ key: "isRecurrenceException",
+ value: function isRecurrenceException() {
+ return this.component.hasProperty('recurrence-id');
+ }
+
+ /**
+ * Returns the types of recurrences this event may have.
+ *
+ * Returned as an object with the following possible keys:
+ *
+ * - YEARLY
+ * - MONTHLY
+ * - WEEKLY
+ * - DAILY
+ * - MINUTELY
+ * - SECONDLY
+ *
+ * @return {Object.}
+ * Object of recurrence flags
+ */
+ }, {
+ key: "getRecurrenceTypes",
+ value: function getRecurrenceTypes() {
+ var rules = this.component.getAllProperties('rrule');
+ var i = 0;
+ var len = rules.length;
+ var result = Object.create(null);
+ for (; i < len; i++) {
+ var value = rules[i].getFirstValue();
+ result[value.freq] = true;
+ }
+ return result;
+ }
+
+ /**
+ * The uid of this event
+ * @type {String}
+ */
+ }, {
+ key: "uid",
+ get: function get() {
+ return this._firstProp('uid');
+ },
+ set: function set(value) {
+ this._setProp('uid', value);
+ }
+
+ /**
+ * The start date
+ * @type {Time}
+ */
+ }, {
+ key: "startDate",
+ get: function get() {
+ return this._firstProp('dtstart');
+ },
+ set: function set(value) {
+ this._setTime('dtstart', value);
+ }
+
+ /**
+ * The end date. This can be the result directly from the property, or the
+ * end date calculated from start date and duration. Setting the property
+ * will remove any duration properties.
+ * @type {Time}
+ */
+ }, {
+ key: "endDate",
+ get: function get() {
+ var endDate = this._firstProp('dtend');
+ if (!endDate) {
+ var duration = this._firstProp('duration');
+ endDate = this.startDate.clone();
+ if (duration) {
+ endDate.addDuration(duration);
+ } else if (endDate.isDate) {
+ endDate.day += 1;
+ }
+ }
+ return endDate;
+ },
+ set: function set(value) {
+ if (this.component.hasProperty('duration')) {
+ this.component.removeProperty('duration');
+ }
+ this._setTime('dtend', value);
+ }
+
+ /**
+ * The duration. This can be the result directly from the property, or the
+ * duration calculated from start date and end date. Setting the property
+ * will remove any `dtend` properties.
+ * @type {Duration}
+ */
+ }, {
+ key: "duration",
+ get: function get() {
+ var duration = this._firstProp('duration');
+ if (!duration) {
+ return this.endDate.subtractDateTz(this.startDate);
+ }
+ return duration;
+ },
+ set: function set(value) {
+ if (this.component.hasProperty('dtend')) {
+ this.component.removeProperty('dtend');
+ }
+ this._setProp('duration', value);
+ }
+
+ /**
+ * The location of the event.
+ * @type {String}
+ */
+ }, {
+ key: "location",
+ get: function get() {
+ return this._firstProp('location');
+ },
+ set: function set(value) {
+ this._setProp('location', value);
+ }
+
+ /**
+ * The attendees in the event
+ * @type {Property[]}
+ */
+ }, {
+ key: "attendees",
+ get: function get() {
+ //XXX: This is way lame we should have a better
+ // data structure for this later.
+ return this.component.getAllProperties('attendee');
+ }
+
+ /**
+ * The event summary
+ * @type {String}
+ */
+ }, {
+ key: "summary",
+ get: function get() {
+ return this._firstProp('summary');
+ },
+ set: function set(value) {
+ this._setProp('summary', value);
+ }
+
+ /**
+ * The event description.
+ * @type {String}
+ */
+ }, {
+ key: "description",
+ get: function get() {
+ return this._firstProp('description');
+ },
+ set: function set(value) {
+ this._setProp('description', value);
+ }
+
+ /**
+ * The event color from [rfc7986](https://datatracker.ietf.org/doc/html/rfc7986)
+ * @type {String}
+ */
+ }, {
+ key: "color",
+ get: function get() {
+ return this._firstProp('color');
+ },
+ set: function set(value) {
+ this._setProp('color', value);
+ }
+
+ /**
+ * The organizer value as an uri. In most cases this is a mailto: uri, but
+ * it can also be something else, like urn:uuid:...
+ * @type {String}
+ */
+ }, {
+ key: "organizer",
+ get: function get() {
+ return this._firstProp('organizer');
+ },
+ set: function set(value) {
+ this._setProp('organizer', value);
+ }
+
+ /**
+ * The sequence value for this event. Used for scheduling
+ * see {@tutorial terminology}.
+ * @type {Number}
+ */
+ }, {
+ key: "sequence",
+ get: function get() {
+ return this._firstProp('sequence');
+ },
+ set: function set(value) {
+ this._setProp('sequence', value);
+ }
+
+ /**
+ * The recurrence id for this event. See {@tutorial terminology} for details.
+ * @type {Time}
+ */
+ }, {
+ key: "recurrenceId",
+ get: function get() {
+ return this._firstProp('recurrence-id');
+ },
+ set: function set(value) {
+ this._setTime('recurrence-id', value);
+ }
+
+ /**
+ * Set/update a time property's value.
+ * This will also update the TZID of the property.
+ *
+ * TODO: this method handles the case where we are switching
+ * from a known timezone to an implied timezone (one without TZID).
+ * This does _not_ handle the case of moving between a known
+ * (by TimezoneService) timezone to an unknown timezone...
+ *
+ * We will not add/remove/update the VTIMEZONE subcomponents
+ * leading to invalid ICAL data...
+ * @private
+ * @param {String} propName The property name
+ * @param {Time} time The time to set
+ */
+ }, {
+ key: "_setTime",
+ value: function _setTime(propName, time) {
+ var prop = this.component.getFirstProperty(propName);
+ if (!prop) {
+ prop = new Property(propName);
+ this.component.addProperty(prop);
+ }
+
+ // utc and local don't get a tzid
+ if (time.zone === Timezone.localTimezone || time.zone === Timezone.utcTimezone) {
+ // remove the tzid
+ prop.removeParameter('tzid');
+ } else {
+ prop.setParameter('tzid', time.zone.tzid);
+ }
+ prop.setValue(time);
+ }
+ }, {
+ key: "_setProp",
+ value: function _setProp(name, value) {
+ this.component.updatePropertyWithValue(name, value);
+ }
+ }, {
+ key: "_firstProp",
+ value: function _firstProp(name) {
+ return this.component.getFirstPropertyValue(name);
+ }
+
+ /**
+ * The string representation of this event.
+ * @return {String}
+ */
+ }, {
+ key: "toString",
+ value: function toString() {
+ return this.component.toString();
+ }
+ }]);
+ }();
+ _defineProperty(Event, "THISANDFUTURE", 'THISANDFUTURE');
+ function compareRangeException(a, b) {
+ if (a[0] > b[0]) return 1;
+ if (b[0] > a[0]) return -1;
+ return 0;
+ }
+
+ /**
+ * The ComponentParser is used to process a String or jCal Object,
+ * firing callbacks for various found components, as well as completion.
+ *
+ * @example
+ * var options = {
+ * // when false no events will be emitted for type
+ * parseEvent: true,
+ * parseTimezone: true
+ * };
+ *
+ * var parser = new ICAL.ComponentParser(options);
+ *
+ * parser.onevent(eventComponent) {
+ * //...
+ * }
+ *
+ * // ontimezone, etc...
+ *
+ * parser.oncomplete = function() {
+ *
+ * };
+ *
+ * parser.process(stringOrComponent);
+ *
+ * @memberof ICAL
+ */
+ var ComponentParser = /*#__PURE__*/function () {
+ /**
+ * Creates a new ICAL.ComponentParser instance.
+ *
+ * @param {Object=} options Component parser options
+ * @param {Boolean} options.parseEvent Whether events should be parsed
+ * @param {Boolean} options.parseTimezeone Whether timezones should be parsed
+ */
+ function ComponentParser(options) {
+ _classCallCheck(this, ComponentParser);
+ /**
+ * When true, parse events
+ *
+ * @type {Boolean}
+ */
+ _defineProperty(this, "parseEvent", true);
+ /**
+ * When true, parse timezones
+ *
+ * @type {Boolean}
+ */
+ _defineProperty(this, "parseTimezone", true);
+ /* SAX like events here for reference */
+ /**
+ * Fired when parsing is complete
+ * @callback
+ */
+ _defineProperty(this, "oncomplete", /* c8 ignore next */function () {});
+ /**
+ * Fired if an error occurs during parsing.
+ *
+ * @callback
+ * @param {Error} err details of error
+ */
+ _defineProperty(this, "onerror", /* c8 ignore next */function (err) {});
+ /**
+ * Fired when a top level component (VTIMEZONE) is found
+ *
+ * @callback
+ * @param {Timezone} component Timezone object
+ */
+ _defineProperty(this, "ontimezone", /* c8 ignore next */function (component) {});
+ /**
+ * Fired when a top level component (VEVENT) is found.
+ *
+ * @callback
+ * @param {Event} component Top level component
+ */
+ _defineProperty(this, "onevent", /* c8 ignore next */function (component) {});
+ if (typeof options === 'undefined') {
+ options = {};
+ }
+ for (var _i = 0, _Object$entries = Object.entries(options); _i < _Object$entries.length; _i++) {
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
+ key = _Object$entries$_i[0],
+ value = _Object$entries$_i[1];
+ this[key] = value;
+ }
+ }
+ return _createClass(ComponentParser, [{
+ key: "process",
+ value:
+ /**
+ * Process a string or parse ical object. This function itself will return
+ * nothing but will start the parsing process.
+ *
+ * Events must be registered prior to calling this method.
+ *
+ * @param {Component|String|Object} ical The component to process,
+ * either in its final form, as a jCal Object, or string representation
+ */
+ function process(ical) {
+ //TODO: this is sync now in the future we will have a incremental parser.
+ if (typeof ical === 'string') {
+ ical = parse(ical);
+ }
+ if (!(ical instanceof Component)) {
+ ical = new Component(ical);
+ }
+ var components = ical.getAllSubcomponents();
+ var i = 0;
+ var len = components.length;
+ var component;
+ for (; i < len; i++) {
+ component = components[i];
+ switch (component.name) {
+ case 'vtimezone':
+ if (this.parseTimezone) {
+ var tzid = component.getFirstPropertyValue('tzid');
+ if (tzid) {
+ this.ontimezone(new Timezone({
+ tzid: tzid,
+ component: component
+ }));
+ }
+ }
+ break;
+ case 'vevent':
+ if (this.parseEvent) {
+ this.onevent(new Event(component));
+ }
+ break;
+ default:
+ continue;
+ }
+ }
+
+ //XXX: ideally we should do a "nextTick" here
+ // so in all cases this is actually async.
+ this.oncomplete();
+ }
+ }]);
+ }();
+
+ /* 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 */
+
+
+ /**
+ * The main ICAL module. Provides access to everything else.
+ *
+ * @alias ICAL
+ * @namespace ICAL
+ * @property {ICAL.design} design
+ * @property {ICAL.helpers} helpers
+ */
+ var ICALmodule = {
+ /**
+ * The number of characters before iCalendar line folding should occur
+ * @type {Number}
+ * @default 75
+ */
+ foldLength: 75,
+ debug: false,
+ /**
+ * The character(s) to be used for a newline. The default value is provided by
+ * rfc5545.
+ * @type {String}
+ * @default "\r\n"
+ */
+ newLineChar: '\r\n',
+ Binary: Binary,
+ Component: Component,
+ ComponentParser: ComponentParser,
+ Duration: Duration,
+ Event: Event,
+ Period: Period,
+ Property: Property,
+ Recur: Recur,
+ RecurExpansion: RecurExpansion,
+ RecurIterator: RecurIterator,
+ Time: Time,
+ Timezone: Timezone,
+ TimezoneService: TimezoneService,
+ UtcOffset: UtcOffset,
+ VCardTime: VCardTime,
+ parse: parse,
+ stringify: stringify,
+ design: design,
+ helpers: helpers
+ };
+
+ return ICALmodule;
+
+}));
diff --git a/cron/scripts/node_modules/ical.js/dist/ical.es5.min.cjs b/cron/scripts/node_modules/ical.js/dist/ical.es5.min.cjs
new file mode 100644
index 0000000..3f14a64
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/ical.es5.min.cjs
@@ -0,0 +1,5 @@
+/* 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 */
+!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).ICAL=e()}(this,(function(){"use strict";function t(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=Array(e);n=t.length?{done:!0}:{done:!1,value:t[r++]}},e:function(t){throw t},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,s=!0,o=!1;return{s:function(){n=n.call(t)},n:function(){var t=n.next();return s=t.done,t},e:function(t){o=!0,a=t},f:function(){try{s||null==n.return||n.return()}finally{if(o)throw a}}}}function o(t,e,n){return(e=y(e))in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function u(t){return u=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(t){return t.__proto__||Object.getPrototypeOf(t)},u(t)}function l(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),Object.defineProperty(t,"prototype",{writable:!1}),e&&c(t,e)}function h(){try{var t=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){})))}catch(t){}return(h=function(){return!!t})()}function c(t,e){return c=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(t,e){return t.__proto__=e,t},c(t,e)}function f(t,e){return function(t){if(Array.isArray(t))return t}(t)||function(t,e){var n=null==t?null:"undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(null!=n){var r,i,a,s,o=[],u=!0,l=!1;try{if(a=(n=n.call(t)).next,0===e);else for(;!(u=(r=a.call(n)).done)&&(o.push(r.value),o.length!==e);u=!0);}catch(t){l=!0,i=t}finally{try{if(!u&&null!=n.return&&(s=n.return(),Object(s)!==s))return}finally{if(l)throw i}}return o}}(t,e)||p(t,e)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function d(e){return function(e){if(Array.isArray(e))return t(e)}(e)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(e)||p(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function y(t){var e=function(t,e){if("object"!=typeof t||!t)return t;var n=t[Symbol.toPrimitive];if(void 0!==n){var r=n.call(t,e||"default");if("object"!=typeof r)return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===e?String:Number)(t)}(t,"string");return"symbol"==typeof e?e:e+""}function m(t){return m="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},m(t)}function p(e,n){if(e){if("string"==typeof e)return t(e,n);var r={}.toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?t(e,n):void 0}}function v(t){var e="function"==typeof Map?new Map:void 0;return v=function(t){if(null===t||!function(t){try{return-1!==Function.toString.call(t).indexOf("[native code]")}catch(e){return"function"==typeof t}}(t))return t;if("function"!=typeof t)throw new TypeError("Super expression must either be null or a function");if(void 0!==e){if(e.has(t))return e.get(t);e.set(t,n)}function n(){return function(t,e,n){if(h())return Reflect.construct.apply(null,arguments);var r=[null];r.push.apply(r,e);var i=new(t.bind.apply(t,r));return n&&c(i,n.prototype),i}(t,arguments,u(this).constructor)}return n.prototype=Object.create(t.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),c(n,t)},v(t)}var _=function(){function t(e){r(this,t),o(this,"icaltype","binary"),this.value=e}return a(t,[{key:"decodeValue",value:function(){return this._b64_decode(this.value)}},{key:"setEncodedValue",value:function(t){this.value=this._b64_encode(t)}},{key:"_b64_encode",value:function(t){var e,n,r,i,a,s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",o=0,u=0,l="",h=[];if(!t)return t;do{e=(a=t.charCodeAt(o++)<<16|t.charCodeAt(o++)<<8|t.charCodeAt(o++))>>18&63,n=a>>12&63,r=a>>6&63,i=63&a,h[u++]=s.charAt(e)+s.charAt(n)+s.charAt(r)+s.charAt(i)}while(o>16&255,n=s>>8&255,r=255&s,h[l++]=64==i?String.fromCharCode(e):64==a?String.fromCharCode(e,n):String.fromCharCode(e,n,r)}while(un)-(e0?-1:0}},{key:"toString",value:function(){return this.start+"/"+(this.end||this.duration)}},{key:"toJSON",value:function(){return[this.start.toString(),(this.end||this.duration).toString()]}},{key:"toICALString",value:function(){return this.start.toICALString()+"/"+(this.end||this.duration).toICALString()}}],[{key:"fromString",value:function(e,n){var r=e.split("/");if(2!==r.length)throw new Error('Invalid string value: "'+e+'" must contain a "/" char.');var i={start:b.fromDateTimeString(r[0],n)},a=r[1];return T.isValueString(a)?i.duration=T.fromString(a):i.end=b.fromDateTimeString(a,n),new t(i)}},{key:"fromData",value:function(e){return new t(e)}},{key:"fromJSON",value:function(e,n,r){function i(t,e){return r?b.fromString(t,e):b.fromDateTimeString(t,e)}return T.isValueString(e[1])?t.fromData({start:i(e[0],n),duration:T.fromString(e[1])}):t.fromData({start:i(e[0],n),end:i(e[1],n)})}}])}(),b=function(){function t(e,n){r(this,t),o(this,"icalclass","icaltime"),o(this,"_cachedUnixTime",null),o(this,"zone",null),o(this,"_pendingNormalization",!1),this.wrappedJSObject=this,this._time=Object.create(null),this._time.year=0,this._time.month=1,this._time.day=1,this._time.hour=0,this._time.minute=0,this._time.second=0,this._time.isDate=!1,this.fromData(e,n)}return a(t,[{key:"icaltype",get:function(){return this.isDate?"date":"date-time"}},{key:"year",get:function(){return this._getTimeAttr("year")},set:function(t){this._setTimeAttr("year",t)}},{key:"month",get:function(){return this._getTimeAttr("month")},set:function(t){this._setTimeAttr("month",t)}},{key:"day",get:function(){return this._getTimeAttr("day")},set:function(t){this._setTimeAttr("day",t)}},{key:"hour",get:function(){return this._getTimeAttr("hour")},set:function(t){this._setTimeAttr("hour",t)}},{key:"minute",get:function(){return this._getTimeAttr("minute")},set:function(t){this._setTimeAttr("minute",t)}},{key:"second",get:function(){return this._getTimeAttr("second")},set:function(t){this._setTimeAttr("second",t)}},{key:"isDate",get:function(){return this._getTimeAttr("isDate")},set:function(t){this._setTimeAttr("isDate",t)}},{key:"_getTimeAttr",value:function(t){return this._pendingNormalization&&(this._normalize(),this._pendingNormalization=!1),this._time[t]}},{key:"_setTimeAttr",value:function(t,e){"isDate"===t&&e&&!this._time.isDate&&this.adjust(0,0,0,0),this._cachedUnixTime=null,this._pendingNormalization=!0,this._time[t]=e}},{key:"clone",value:function(){return new t(this._time,this.zone)}},{key:"reset",value:function(){this.fromData(t.epochTime),this.zone=I.utcTimezone}},{key:"resetTo",value:function(t,e,n,r,i,a,s){this.fromData({year:t,month:e,day:n,hour:r,minute:i,second:a,zone:s})}},{key:"fromJSDate",value:function(t,e){return t?e?(this.zone=I.utcTimezone,this.year=t.getUTCFullYear(),this.month=t.getUTCMonth()+1,this.day=t.getUTCDate(),this.hour=t.getUTCHours(),this.minute=t.getUTCMinutes(),this.second=t.getUTCSeconds()):(this.zone=I.localTimezone,this.year=t.getFullYear(),this.month=t.getMonth()+1,this.day=t.getDate(),this.hour=t.getHours(),this.minute=t.getMinutes(),this.second=t.getSeconds()):this.reset(),this._cachedUnixTime=null,this}},{key:"fromData",value:function(t,e){if(t)for(var n=0,r=Object.entries(t);n=0){o.day=1,0!=a&&a--,s=o.day;var u=e-o.dayOfWeek();u<0&&(u+=7),s+=u,s-=e,r=e}else{o.day=i,a++,(r=o.dayOfWeek()-e)<0&&(r+=7),r=i-r}return s+(r+=7*a)}},{key:"isNthWeekDay",value:function(t,e){var n=this.dayOfWeek();return 0===e&&n===t||this.nthWeekDay(t,e)===this.day}},{key:"weekNumber",value:function(e){var n,r=(this.year<<12)+(this.month<<8)+(this.day<<3)+e;if(r in t._wnCache)return t._wnCache[r];var i=this.clone();i.isDate=!0;var a=this.year;12==i.month&&i.day>25?(n=t.weekOneStarts(a+1,e),i.compare(n)<0?n=t.weekOneStarts(a,e):a++):(n=t.weekOneStarts(a,e),i.compare(n)<0&&(n=t.weekOneStarts(--a,e)));var s=K(i.subtractDate(n).toSeconds()/86400/7)+1;return t._wnCache[r]=s,s}},{key:"addDuration",value:function(t){var e=t.isNegative?-1:1,n=this.second,r=this.minute,i=this.hour,a=this.day;n+=e*t.seconds,r+=e*t.minutes,i+=e*t.hours,a+=e*t.days,a+=7*e*t.weeks,this.second=n,this.minute=r,this.hour=i,this.day=a,this._cachedUnixTime=null}},{key:"subtractDate",value:function(t){var e=this.toUnixTime()+this.utcOffset(),n=t.toUnixTime()+t.utcOffset();return T.fromSeconds(e-n)}},{key:"subtractDateTz",value:function(t){var e=this.toUnixTime(),n=t.toUnixTime();return T.fromSeconds(e-n)}},{key:"compare",value:function(t){if(t instanceof A)return-1*t.compare(this);var e=this.toUnixTime(),n=t.toUnixTime();return e>n?1:n>e?-1:0}},{key:"compareDateOnlyTz",value:function(e,n){var r=this.convertToZone(n),i=e.convertToZone(n),a=0;return 0!=(a=t._cmp_attr(r,i,"year"))||0!=(a=t._cmp_attr(r,i,"month"))||(a=t._cmp_attr(r,i,"day")),a}},{key:"convertToZone",value:function(t){var e=this.clone(),n=this.zone.tzid==t.tzid;return this.isDate||n||I.convert_time(e,this.zone,t),e.zone=t,e}},{key:"utcOffset",value:function(){return this.zone==I.localTimezone||this.zone==I.utcTimezone?0:this.zone.utcOffset(this)}},{key:"toICALString",value:function(){var t=this.toString();return t.length>10?It.icalendar.value["date-time"].toICAL(t):It.icalendar.value.date.toICAL(t)}},{key:"toString",value:function(){var t=this.year+"-"+F(this.month)+"-"+F(this.day);return this.isDate||(t+="T"+F(this.hour)+":"+F(this.minute)+":"+F(this.second),this.zone===I.utcTimezone&&(t+="Z")),t}},{key:"toJSDate",value:function(){return this.zone==I.localTimezone?this.isDate?new Date(this.year,this.month-1,this.day):new Date(this.year,this.month-1,this.day,this.hour,this.minute,this.second,0):new Date(1e3*this.toUnixTime())}},{key:"_normalize",value:function(){return this._time.isDate&&(this._time.hour=0,this._time.minute=0,this._time.second=0),this.adjust(0,0,0,0),this}},{key:"adjust",value:function(e,n,r,i,a){var s,o,u,l,h,c,f,d=0,y=0,m=a||this._time;if(m.isDate||(u=m.second+i,m.second=u%60,s=K(u/60),m.second<0&&(m.second+=60,s--),l=m.minute+r+s,m.minute=l%60,o=K(l/60),m.minute<0&&(m.minute+=60,o--),h=m.hour+n+o,m.hour=h%24,d=K(h/24),m.hour<0&&(m.hour+=24,d--)),m.month>12?y=K((m.month-1)/12):m.month<1&&(y=K(m.month/12)-1),m.year+=y,m.month-=12*y,(c=m.day+e+d)>0)for(;!(c<=(f=t.daysInMonth(m.month,m.year)));)m.month++,m.month>12&&(m.year++,m.month=1),c-=f;else for(;c<=0;)1==m.month?(m.year--,m.month=12):m.month--,c+=t.daysInMonth(m.month,m.year);return m.day=c,this._cachedUnixTime=null,this}},{key:"fromUnixTime",value:function(t){this.zone=I.utcTimezone;var e=new Date(1e3*t);this.year=e.getUTCFullYear(),this.month=e.getUTCMonth()+1,this.day=e.getUTCDate(),this._time.isDate?(this.hour=0,this.minute=0,this.second=0):(this.hour=e.getUTCHours(),this.minute=e.getUTCMinutes(),this.second=e.getUTCSeconds()),this._cachedUnixTime=null}},{key:"toUnixTime",value:function(){if(null!==this._cachedUnixTime)return this._cachedUnixTime;var t=this.utcOffset(),e=Date.UTC(this.year,this.month-1,this.day,this.hour,this.minute,this.second-t);return this._cachedUnixTime=e/1e3,this._cachedUnixTime}},{key:"toJSON",value:function(){for(var t,e=["year","month","day","hour","minute","second","isDate"],n=Object.create(null),r=0,i=e.length;r12||(r=[0,31,28,31,30,31,30,31,31,30,31,30,31][e],2==e&&(r+=t.isLeapYear(n))),r}},{key:"isLeapYear",value:function(t){return t<=1752?t%4==0:t%4==0&&t%100!=0||t%400==0}},{key:"fromDayOfYear",value:function(e,n){var r=n,i=e,a=new t;a.auto_normalize=!1;var s=t.isLeapYear(r)?1:0;if(i<1)return r--,s=t.isLeapYear(r)?1:0,i+=t.daysInYearPassedMonth[s][12],t.fromDayOfYear(i,r);if(i>t.daysInYearPassedMonth[s][12])return s=t.isLeapYear(r)?1:0,i-=t.daysInYearPassedMonth[s][12],r++,t.fromDayOfYear(i,r);a.year=r,a.isDate=!0;for(var o=11;o>=0;o--)if(i>t.daysInYearPassedMonth[s][o]){a.month=o+1,a.day=i-t.daysInYearPassedMonth[s][o];break}return a.auto_normalize=!0,a}},{key:"fromStringv2",value:function(e){return new t({year:parseInt(e.slice(0,4),10),month:parseInt(e.slice(5,7),10),day:parseInt(e.slice(8,10),10),isDate:!0})}},{key:"fromDateString",value:function(e){return new t({year:U(e.slice(0,4)),month:U(e.slice(5,7)),day:U(e.slice(8,10)),isDate:!0})}},{key:"fromDateTimeString",value:function(e,n){if(e.length<19)throw new Error('invalid date-time value: "'+e+'"');var r,i;"Z"===e.slice(-1)?r=I.utcTimezone:n&&(i=n.getParameter("tzid"),n.parent&&("standard"===n.parent.name||"daylight"===n.parent.name?r=I.localTimezone:i&&(r=n.parent.getTimeZoneByID(i))));var a={year:U(e.slice(0,4)),month:U(e.slice(5,7)),day:U(e.slice(8,10)),hour:U(e.slice(11,13)),minute:U(e.slice(14,16)),second:U(e.slice(17,19))};return i&&!r&&(a.timezone=i),new t(a,r)}},{key:"fromString",value:function(e,n){return e.length>10?t.fromDateTimeString(e,n):t.fromDateString(e)}},{key:"fromJSDate",value:function(e,n){return(new t).fromJSDate(e,n)}},{key:"now",value:function(){return t.fromJSDate(new Date,!1)}},{key:"weekOneStarts",value:function(e,n){var r=t.fromData({year:e,month:1,day:1,isDate:!0}),i=r.dayOfWeek(),a=n||t.DEFAULT_WEEK_START;return i>t.THURSDAY&&(r.day+=7),a>t.THURSDAY&&(r.day-=7),r.day-=i-a,r}},{key:"getDominicalLetter",value:function(e){var n="GFEDCBA",r=(e+(e/4|0)+(e/400|0)-(e/100|0)-1)%7;return t.isLeapYear(e)?n[(r+6)%7]+n[r]:n[r]}},{key:"epochTime",get:function(){return e(t,this,O)._||(O._=e(t,this,t.fromData({year:1970,month:1,day:1,hour:0,minute:0,second:0,isDate:!1,timezone:"Z"}))),e(t,this,O)._}},{key:"_cmp_attr",value:function(t,e,n){return t[n]>e[n]?1:t[n]1)throw new x("invalid ical body. component began but did not end");return e=null,1==n.length?n[0]:n}C.property=function(t,e){var n={component:[[],[]],designSet:e||It.defaultSet};return C._handleContentLine(t,n),n.component[1][0]},C.component=function(t){return C(t)};var x=function(t){function e(){var t;r(this,e);for(var i=arguments.length,a=new Array(i),s=0;su&&(l=-1),-1!==l){if(i=t.slice(0,Math.max(0,l)).toLowerCase(),-1==(s=C._parseParameters(t.slice(Math.max(0,l)),0,e.designSet))[2])throw new x("Invalid parameters in '"+t+"'");if(h=s[0],n=("string"==typeof s[1]?s[1].length:s[1].reduce((function(t,e){return t+e.length}),0))+s[2]+l,-1===(r=t.slice(Math.max(0,n)).indexOf(w)))throw new x("Missing parameter value in '"+t+"'");a=t.slice(Math.max(0,n+r+1))}else{if(-1===u)throw new x('invalid line (no token ";" or ":") "'+t+'"');if(i=t.slice(0,Math.max(0,u)).toLowerCase(),a=t.slice(Math.max(0,u+1)),"begin"===i){var c=[a.toLowerCase(),[],[]];return 1===e.stack.length?e.component.push(c):e.component[2].push(c),e.stack.push(e.component),e.component=c,void(e.designSet||(e.designSet=It.getDesignSet(e.component[0])))}if("end"===i)return void(e.component=e.stack.pop())}var f,d,y,m,p=!1,v=!1;e.designSet.propertyGroups&&-1!==i.indexOf(".")?(d=i.split("."),h.group=d[0],y=d[1]):y=i,y in e.designSet.property&&("multiValue"in(f=e.designSet.property[y])&&(p=f.multiValue),"structuredValue"in f&&(v=f.structuredValue),a&&"detectType"in f&&(o=f.detectType(a))),o||(o="value"in h?h.value.toLowerCase():f?f.defaultType:"unknown"),delete h.value,p&&v?m=[y,h,o,a=C._parseMultiValue(a,v,o,[],p,e.designSet,v)]:p?(m=[y,h,o],C._parseMultiValue(a,p,o,m,null,e.designSet,!1)):m=v?[y,h,o,a=C._parseMultiValue(a,v,o,[],null,e.designSet,v)]:[y,h,o,a=C._parseValue(a,o,e.designSet,!1)],"vcard"!==e.component[0]||0!==e.component[1].length||"version"===i&&"4.0"===a||(e.designSet=It.getDesignSet("vcard3")),e.component[1].push(m)},C._parseValue=function(t,e,n,r){return e in n.value&&"fromICAL"in n.value[e]?n.value[e].fromICAL(t,r):t},C._parseParameters=function(t,e,n){for(var r,i,a,s,o,u,l=e,h=0,c={},f=-1;!1!==h&&-1!==(h=t.indexOf("=",h+1));){if(0==(r=t.slice(l+1,h)).length)throw new x("Empty parameter name in '"+t+"'");if(u=!1,o=!1,s=(i=r.toLowerCase())in n.param&&n.param[i].valueType?n.param[i].valueType:"text",i in n.param&&(o=n.param[i].multiValue,n.param[i].multiValueSeparateDQuote&&(u=C._rfc6868Escape('"'+o+'"'))),'"'===t[h+1]){if(f=h+2,h=t.indexOf('"',f),o&&-1!=h)for(var d=!0;d;)t[h+1]==o&&'"'==t[h+2]?h=t.indexOf('"',h+3):d=!1;if(-1===h)throw new x('invalid line (no matching double quote) "'+t+'"');a=t.slice(f,h),l=t.indexOf(";",h);var y=t.indexOf(w,h);(-1===l||-1!==y&&l>y)&&(h=!1)}else{f=h+1;var m=t.indexOf(";",f),p=t.indexOf(w,f);-1!==p&&m>p?(m=p,h=!1):-1===m?(m=-1===p?t.length:p,h=!1):(l=m,h=m),a=t.slice(f,m)}if(f+=a.length-(a=C._rfc6868Escape(a)).length,o){var v=u||o;a=C._parseMultiValue(a,v,s,[],null,n)}else a=C._parseValue(a,s,n);o&&i in c?Array.isArray(c[i])?c[i].push(a):c[i]=[c[i],a]:c[i]=a}return[c,a,f]},C._rfc6868Escape=function(t){return t.replace(/\^['n^]/g,(function(t){return E[t]}))},C._parseMultiValue=function(t,e,n,r,i,a,s){var o,u=0,l=0;if(0===e.length)return t;for(;-1!==(u=V(t,e,l));)o=t.slice(l,u),o=i?C._parseMultiValue(o,i,n,[],null,a,s):C._parseValue(o,n,a,s),r.push(o),l=u+e.length;return o=t.slice(l),o=i?C._parseMultiValue(o,i,n,[],null,a,s):C._parseValue(o,n,a,s),r.push(o),1==r.length?r[0]:r},C._eachLine=function(t,e){var n,r,i,a=t.length,s=t.search(S),o=s;do{i=(o=t.indexOf("\n",s)+1)>1&&"\r"===t[o-2]?2:1,0===o&&(o=a,i=0)," "===(r=t[s])||"\t"===r?n+=t.slice(s+1,o-i):(n&&e(null,n),n=t.slice(s,o-i)),s=o}while(o!==a);(n=n.trim()).length&&e(null,n)};var N=["tzid","location","tznames","latitude","longitude"],I=function(){function t(e){r(this,t),o(this,"tzid",""),o(this,"location",""),o(this,"tznames",""),o(this,"latitude",0),o(this,"longitude",0),o(this,"component",null),o(this,"expandedUntilYear",0),o(this,"icalclass","icaltimezone"),this.wrappedJSObject=this,this.fromData(e)}return a(t,[{key:"fromData",value:function(t){if(this.expandedUntilYear=0,this.changes=[],t instanceof Ut)this.component=t;else{if(t&&"component"in t)if("string"==typeof t.component){var e=C(t.component);this.component=new Ut(e)}else t.component instanceof Ut?this.component=t.component:this.component=null;for(var n=0,r=N;n=0?i=r:a=-1,-1==a&&-1!=i)break;if((r+=a)<0)return 0;if(r>=this.changes.length)break}var o=this.changes[i];if(o.utcOffset-o.prevUtcOffset<0&&i>0){var u=R(o,!0);if(t.adjust_change(u,0,0,0,u.prevUtcOffset),t._compare_change_fn(n,u)<0){var l=this.changes[i-1],h=!1;o.is_daylight!=h&&l.is_daylight==h&&(o=l)}}return o.utcOffset}},{key:"_findNearbyChange",value:function(e){var n=H(this.changes,e,t._compare_change_fn);return n>=this.changes.length?this.changes.length-1:n}},{key:"_ensureCoverage",value:function(e){if(-1==t._minimumExpansionYear){var n=b.now();t._minimumExpansionYear=n.year}var r=e;if(rn)&&d);)i.year=d.year,i.month=d.month,i.day=d.day,i.hour=d.hour,i.minute=d.minute,i.second=d.second,i.isDate=d.isDate,t.adjust_change(i,0,0,0,-i.prevUtcOffset),r.push(i)}}else(i=u()).year=a.year,i.month=a.month,i.day=a.day,i.hour=a.hour,i.minute=a.minute,i.second=a.second,t.adjust_change(i,0,0,0,-i.prevUtcOffset),r.push(i);return r}},{key:"toString",value:function(){return this.tznames?this.tznames:this.tzid}}],[{key:"_compare_change_fn",value:function(t,e){return t.yeare.year?1:t.monthe.month?1:t.daye.day?1:t.houre.hour?1:t.minutee.minute?1:t.seconde.second?1:0}},{key:"convert_time",value:function(e,n,r){if(e.isDate||n.tzid==r.tzid||n==t.localTimezone||r==t.localTimezone)return e.zone=r,e;var i=n.utcOffset(e);return e.adjust(0,0,0,-i),i=r.utcOffset(e),e.adjust(0,0,0,i),null}},{key:"fromData",value:function(e){return(new t).fromData(e)}},{key:"utcTimezone",get:function(){return e(t,this,B)._||(B._=e(t,this,t.fromData({tzid:"UTC"}))),e(t,this,B)._}},{key:"localTimezone",get:function(){return e(t,this,M)._||(M._=e(t,this,t.fromData({tzid:"floating"}))),e(t,this,M)._}},{key:"adjust_change",value:function(t,e,n,r,i){return b.prototype.adjust.call(t,e,n,r,i,t)}}])}(),B={_:null},M={_:null};o(I,"_minimumExpansionYear",-1),o(I,"EXTRA_COVERAGE",5);var z=null,L={get count(){return null===z?0:Object.keys(z).length},reset:function(){z=Object.create(null);var t=I.utcTimezone;z.Z=t,z.UTC=t,z.GMT=t},_hard_reset:function(){z=null},has:function(t){return null!==z&&!!z[t]},get:function(t){return null===z&&this.reset(),z[t]},register:function(t,e){if(null===z&&this.reset(),"string"==typeof t&&e instanceof I){var n=[e,t];t=n[0],e=n[1]}if(e||(t instanceof I?e=t.tzid:"vtimezone"===t.name&&(e=(t=new I(t)).tzid)),!e)throw new TypeError("Neither a timezone nor a name was passed");if(!(t instanceof I))throw new TypeError("timezone must be ICAL.Timezone or ICAL.Component");z[e]=t},remove:function(t){return null===z?null:delete z[t]}};function P(t){return"number"==typeof t&&isNaN(t)}function U(t){var e=parseInt(t,10);if(P(e))throw new Error('Could not extract integer from "'+t+'"');return e}function j(t,e){if(void 0!==t)return t instanceof e?t:new e(t)}function V(t,e,n){for(;-1!==(n=t.indexOf(e,n));){if(!(n>0&&"\\"===t[n-1]))return n;n+=1}return-1}function H(t,e,n){if(!t.length)return 0;for(var r,i,a=0,s=t.length-1;a<=s;)if((i=n(e,t[r=a+Math.floor((s-a)/2)]))<0)s=r-1;else{if(!(i>0))break;a=r+1}return i<0?r:i>0?r+1:r}function R(t,e){if(t&&"object"==m(t)){if(t instanceof Date)return new Date(t.getTime());if("clone"in t)return t.clone();if(Array.isArray(t)){for(var n=[],r=0;r65535?2:1:(e+=Wt.newLineChar+" "+n.slice(0,Math.max(0,r)),n=n.slice(Math.max(0,r)),r=i=0)}return e.slice(Wt.newLineChar.length+1)}function F(t){switch("string"!=typeof t&&("number"==typeof t&&(t=parseInt(t)),t=String(t)),t.length){case 0:return"00";case 1:return"0"+t;default:return t}}function K(t){return t<0?Math.ceil(t):Math.floor(t)}function q(t,e){for(var n in t){var r=Object.getOwnPropertyDescriptor(t,n);r&&!Object.getOwnPropertyDescriptor(e,n)&&Object.defineProperty(e,n,r)}return e}var J=Object.freeze({__proto__:null,binsearchInsert:H,clone:R,extend:q,foldline:W,formatClassType:j,isStrictlyNaN:P,pad2:F,strictParseInt:U,trunc:K,unescapedIndexOf:V,updateTimezones:function(t){var e,n,r,i,a;if(!t||"vcalendar"!==t.name)return t;for(e=t.getAllSubcomponents(),n=[],r={},a=0;an)-(n>e)}},{key:"_normalize",value:function(){for(var t=this.toSeconds(),e=this.factor;t<-43200;)t+=97200;for(;t>50400;)t-=97200;this.fromSeconds(t),0==t&&(this.factor=e)}},{key:"toICALString",value:function(){return It.icalendar.value["utc-offset"].toICAL(this.toString())}},{key:"toString",value:function(){return(1==this.factor?"+":"-")+F(this.hours)+":"+F(this.minutes)}}],[{key:"fromString",value:function(e){var n={};return n.factor="+"===e[0]?1:-1,n.hours=U(e.slice(1,3)),n.minutes=U(e.slice(4,6)),new t(n)}},{key:"fromSeconds",value:function(e){var n=new t;return n.fromSeconds(e),n}}])}(),G=function(t){function e(t,i,a){var s;return r(this,e),o(s=n(this,e,[t,i]),"icalclass","vcardtime"),o(s,"icaltype","date-and-or-time"),s.icaltype=a||"date-and-or-time",s}return l(e,t),a(e,[{key:"clone",value:function(){return new e(this._time,this.zone,this.icaltype)}},{key:"_normalize",value:function(){return this}},{key:"utcOffset",value:function(){return this.zone instanceof Z?this.zone.toSeconds():b.prototype.utcOffset.apply(this,arguments)}},{key:"toICALString",value:function(){return It.vcard.value[this.icaltype].toICAL(this.toString())}},{key:"toString",value:function(){var t,e=this.year,n=this.month,r=this.day,i=this.hour,a=this.minute,s=this.second,o=null!==n,u=null!==r,l=null!==i,h=null!==a,c=null!==s,f=(null!==e?F(e)+(o||u?"-":""):o||u?"--":"")+(o?F(n):"")+(u?"-"+F(r):""),d=(l?F(i):"-")+(l&&h?":":"")+(h?F(a):"")+(l||h?"":"-")+(h&&c?":":"")+(c?F(s):"");if(this.zone===I.utcTimezone)t="Z";else if(this.zone instanceof Z)t=this.zone.toString();else if(this.zone===I.localTimezone)t="";else if(this.zone instanceof I){t=Z.fromSeconds(this.zone.utcOffset(this)).toString()}else t="";switch(this.icaltype){case"time":return d+t;case"date-and-or-time":case"date-time":return f+("--"==d?"":"T"+d+t);case"date":return f}return null}}],[{key:"fromDateAndOrTimeString",value:function(t,n){function r(t,e,n){return t?U(t.slice(e,e+n)):null}var i=t.split("T"),a=i[0],s=i[1],o=s?It.vcard.value.time._splitZone(s):[],u=o[0],l=o[1],h=a?a.length:0,c=l?l.length:0,f=a&&"-"==a[0]&&"-"==a[1],d=l&&"-"==l[0];return new e({year:f?null:r(a,0,4),month:!f||4!=h&&7!=h?7==h||10==h?r(a,5,2):null:r(a,2,2),day:5==h?r(a,3,2):7==h&&f?r(a,5,2):10==h?r(a,8,2):null,hour:d?null:r(l,0,2),minute:d&&3==c?r(l,1,2):c>4?r(l,d?1:3,2):null,second:4==c?r(l,2,2):6==c?r(l,4,2):8==c?r(l,6,2):null},u="Z"==u?I.utcTimezone:u&&":"==u[3]?Z.fromString(u):null,n)}}])}(b),X=function(){function t(e){r(this,t),o(this,"completed",!1),o(this,"rule",null),o(this,"dtstart",null),o(this,"last",null),o(this,"occurrence_number",0),o(this,"by_indices",null),o(this,"initialized",!1),o(this,"by_data",null),o(this,"days",null),o(this,"days_index",0),this.fromData(e)}return a(t,[{key:"fromData",value:function(t){if(this.rule=j(t.rule,it),!this.rule)throw new Error("iterator requires a (ICAL.Recur) rule");if(this.dtstart=j(t.dtstart,b),!this.dtstart)throw new Error("iterator requires a (ICAL.Time) dtstart");if(t.by_data?this.by_data=t.by_data:this.by_data=R(this.rule.parts,!0),t.occurrence_number&&(this.occurrence_number=t.occurrence_number),this.days=t.days||[],t.last&&(this.last=j(t.last,b)),this.by_indices=t.by_indices,this.by_indices||(this.by_indices={BYSECOND:0,BYMINUTE:0,BYHOUR:0,BYDAY:0,BYMONTH:0,BYWEEKNO:0,BYMONTHDAY:0}),this.initialized=t.initialized||!1,!this.initialized)try{this.init()}catch(t){if(!(t instanceof Q))throw t;this.completed=!0}}},{key:"init",value:function(){var t=this;this.initialized=!0,this.last=this.dtstart.clone();var e=this.by_data;if("BYDAY"in e&&this.sort_byday_rules(e.BYDAY),"BYYEARDAY"in e&&("BYMONTH"in e||"BYWEEKNO"in e||"BYMONTHDAY"in e))throw new Error("Invalid BYYEARDAY rule");if("BYWEEKNO"in e&&"BYMONTHDAY"in e)throw new Error("BYWEEKNO does not fit to BYMONTHDAY");if("MONTHLY"==this.rule.freq&&("BYYEARDAY"in e||"BYWEEKNO"in e))throw new Error("For MONTHLY recurrences neither BYYEARDAY nor BYWEEKNO may appear");if("WEEKLY"==this.rule.freq&&("BYYEARDAY"in e||"BYMONTHDAY"in e))throw new Error("For WEEKLY recurrences neither BYMONTHDAY nor BYYEARDAY may appear");if("YEARLY"!=this.rule.freq&&"BYYEARDAY"in e)throw new Error("BYYEARDAY may only appear in YEARLY rules");if(this.last.second=this.setup_defaults("BYSECOND","SECONDLY",this.dtstart.second),this.last.minute=this.setup_defaults("BYMINUTE","MINUTELY",this.dtstart.minute),this.last.hour=this.setup_defaults("BYHOUR","HOURLY",this.dtstart.hour),this.last.day=this.setup_defaults("BYMONTHDAY","DAILY",this.dtstart.day),this.last.month=this.setup_defaults("BYMONTH","MONTHLY",this.dtstart.month),"WEEKLY"==this.rule.freq)if("BYDAY"in e){var n=f(this.ruleDayOfWeek(e.BYDAY[0],this.rule.wkst),2)[1],r=n-this.last.dayOfWeek(this.rule.wkst);(this.last.dayOfWeek(this.rule.wkst)=0||r<0)&&(this.last.day+=r)}else{var i=it.numericDayToIcalDay(this.dtstart.dayOfWeek());e.BYDAY=[i]}if("YEARLY"==this.rule.freq){for(var a=this.rule.until?this.rule.until.year:2e4;this.last.year<=a&&(this.expand_year_days(this.last.year),!(this.days.length>0));)this.increment_year(this.rule.interval);if(0==this.days.length)throw new Q;if(!(this._nextByYearDay()||this.next_year()||this.next_year()||this.next_year()))throw new Q}if("MONTHLY"==this.rule.freq)if(this.has_by_data("BYDAY")){var o,u=null,l=this.last.clone(),h=b.daysInMonth(this.last.month,this.last.year),c=s(this.by_data.BYDAY);try{for(c.s();!(o=c.n()).done;){var d=o.value;this.last=l.clone();var y=f(this.ruleDayOfWeek(d),2),m=y[0],p=y[1],v=this.last.nthWeekDay(p,m);if(m>=6||m<=-6)throw new Error("Malformed values in BYDAY part");if(v>h||v<=0){if(u&&u.month==l.month)continue;for(;v>h||v<=0;)this.increment_month(),h=b.daysInMonth(this.last.month,this.last.year),v=this.last.nthWeekDay(p,m)}this.last.day=v,(!u||this.last.compare(u)<0)&&(u=this.last.clone())}}catch(t){c.e(t)}finally{c.f()}if(this.last=u.clone(),this.has_by_data("BYMONTHDAY")&&this._byDayAndMonthDay(!0),this.last.day>h||0==this.last.day)throw new Error("Malformed values in BYDAY part")}else if(this.has_by_data("BYMONTHDAY")){this.last.day=1;var _=this.normalizeByMonthDayRules(this.last.year,this.last.month,this.rule.parts.BYMONTHDAY).filter((function(e){return e>=t.last.day}));if(_.length)this.last.day=_[0],this.by_data.BYMONTHDAY=_;else if(!this.next_month()&&!this.next_month()&&!this.next_month())throw new Q}}},{key:"next",value:function(){var t,e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],n=this.last?this.last.clone():null;if((this.rule.count&&this.occurrence_number>=this.rule.count||this.rule.until&&this.last.compare(this.rule.until)>0)&&(this.completed=!0),this.completed)return null;if(0==this.occurrence_number&&this.last.compare(this.dtstart)>=0)return this.occurrence_number++,this.last;var r=0;do{switch(t=1,this.rule.freq){case"SECONDLY":this.next_second();break;case"MINUTELY":this.next_minute();break;case"HOURLY":this.next_hour();break;case"DAILY":this.next_day();break;case"WEEKLY":this.next_week();break;case"MONTHLY":if(t=this.next_month())r=0;else if(336==++r)return this.completed=!0,null;break;case"YEARLY":if(t=this.next_year())r=0;else if(28==++r)return this.completed=!0,null;break;default:return null}}while(!this.check_contracting_rules()||this.last.compare(this.dtstart)<0||!t);if(0==this.last.compare(n)){if(e)throw new Error("Same occurrence found twice, protecting you from death by recursion");this.next(!0)}return this.rule.until&&this.last.compare(this.rule.until)>0?(this.completed=!0,null):(this.occurrence_number++,this.last)}},{key:"next_second",value:function(){return this.next_generic("BYSECOND","SECONDLY","second","minute")}},{key:"increment_second",value:function(t){return this.increment_generic(t,"second",60,"minute")}},{key:"next_minute",value:function(){return this.next_generic("BYMINUTE","MINUTELY","minute","hour","next_second")}},{key:"increment_minute",value:function(t){return this.increment_generic(t,"minute",60,"hour")}},{key:"next_hour",value:function(){return this.next_generic("BYHOUR","HOURLY","hour","monthday","next_minute")}},{key:"increment_hour",value:function(t){this.increment_generic(t,"hour",24,"monthday")}},{key:"next_day",value:function(){var t="DAILY"==this.rule.freq;return 0==this.next_hour()||(t?this.increment_monthday(this.rule.interval):this.increment_monthday(1)),0}},{key:"next_week",value:function(){var t=0;if(0==this.next_weekday_by_week())return t;if(this.has_by_data("BYWEEKNO")){this.by_indices.BYWEEKNO++,this.by_indices.BYWEEKNO==this.by_data.BYWEEKNO.length&&(this.by_indices.BYWEEKNO=0,t=1),this.last.month=1,this.last.day=1;var e=this.by_data.BYWEEKNO[this.by_indices.BYWEEKNO];this.last.day+=7*e,t&&this.increment_year(1)}else this.increment_monthday(7*this.rule.interval);return t}},{key:"normalizeByMonthDayRules",value:function(t,e,n){for(var r,i=b.daysInMonth(e,t),a=[],s=0,o=n.length;si)){if(r<0)r=i+(r+1);else if(0===r)continue;-1===a.indexOf(r)&&a.push(r)}}return a.sort((function(t,e){return t-e}))}},{key:"_byDayAndMonthDay",value:function(t){var e,n,r,i,a=this.by_data.BYDAY,s=0,o=a.length,u=0,l=this,h=this.last.day;function c(){for(i=b.daysInMonth(l.last.month,l.last.year),e=l.normalizeByMonthDayRules(l.last.year,l.last.month,l.by_data.BYMONTHDAY),r=e.length;e[s]<=h&&(!t||e[s]!=h)&&si)f();else{var y=e[s++];if(y>=n){h=y;for(var m=0;mn&&(this.last.day=1,this.increment_month(),this.is_day_in_byday(this.last)?this.has_by_data("BYSETPOS")&&!this.check_set_position(1)||(t=1):t=0)}else if(this.has_by_data("BYMONTHDAY")){if(this.by_indices.BYMONTHDAY++,this.by_indices.BYMONTHDAY>=this.by_data.BYMONTHDAY.length&&(this.by_indices.BYMONTHDAY=0,this.increment_month(),this.by_indices.BYMONTHDAY>=this.by_data.BYMONTHDAY.length))return 0;var o=b.daysInMonth(this.last.month,this.last.year),u=this.by_data.BYMONTHDAY[this.by_indices.BYMONTHDAY];u<0&&(u=o+u+1),u>o?(this.last.day=1,t=this.is_day_in_byday(this.last)):this.last.day=u}else{this.increment_month();var l=b.daysInMonth(this.last.month,this.last.year);this.by_data.BYMONTHDAY[0]>l?t=0:this.last.day=this.by_data.BYMONTHDAY[0]}return t}},{key:"next_weekday_by_week",value:function(){var t=0;if(0==this.next_hour())return t;if(!this.has_by_data("BYDAY"))return 1;for(;;){var e=new b;this.by_indices.BYDAY++,this.by_indices.BYDAY==Object.keys(this.by_data.BYDAY).length&&(this.by_indices.BYDAY=0,t=1);var n=this.by_data.BYDAY[this.by_indices.BYDAY],r=this.ruleDayOfWeek(n)[1];(r-=this.rule.wkst)<0&&(r+=7),e.year=this.last.year,e.month=this.last.month,e.day=this.last.day;var i=e.startDoyWeek(this.rule.wkst);if(!(r+i<1)||t){var a=b.fromDayOfYear(i+r,this.last.year);return this.last.year=a.year,this.last.month=a.month,this.last.day=a.day,t}}}},{key:"next_year",value:function(){return 0==this.next_hour()?0:0!=this.days.length&&++this.days_index!=this.days.length||(this.days_index=0,this.increment_year(this.rule.interval),this.has_by_data("BYMONTHDAY")&&(this.by_data.BYMONTHDAY=this.normalizeByMonthDayRules(this.last.year,this.last.month,this.rule.parts.BYMONTHDAY)),this.expand_year_days(this.last.year),0!=this.days.length)?this._nextByYearDay():0}},{key:"_nextByYearDay",value:function(){var t=this.days[this.days_index],e=this.last.year;if(366==Math.abs(t)&&!b.isLeapYear(this.last.year))return 0;t<1&&(t+=1,e+=1);var n=b.fromDayOfYear(t,e);return this.last.day=n.day,this.last.month=n.month,1}},{key:"ruleDayOfWeek",value:function(t,e){var n=t.match(/([+-]?[0-9])?(MO|TU|WE|TH|FR|SA|SU)/);return n?[parseInt(n[1]||0,10),t=it.icalDayToNumericDay(n[2],e)]:[0,0]}},{key:"next_generic",value:function(t,e,n,r,i){var a=t in this.by_data,s=this.rule.freq==e,o=0;if(i&&0==this[i]())return o;if(a){this.by_indices[t]++;var u=this.by_data[t];this.by_indices[t]==u.length&&(this.by_indices[t]=0,o=1),this.last[n]=u[this.by_indices[t]]}else s&&this["increment_"+n](this.rule.interval);return a&&o&&s&&this["increment_"+r](1),o}},{key:"increment_monthday",value:function(t){for(var e=0;en&&(this.last.day-=n,this.increment_month())}}},{key:"increment_month",value:function(){if(this.last.day=1,this.has_by_data("BYMONTH"))this.by_indices.BYMONTH++,this.by_indices.BYMONTH==this.by_data.BYMONTH.length&&(this.by_indices.BYMONTH=0,this.increment_year(1)),this.last.month=this.by_data.BYMONTH[this.by_indices.BYMONTH];else{"MONTHLY"==this.rule.freq?this.last.month+=this.rule.interval:this.last.month++,this.last.month--;var t=K(this.last.month/12);this.last.month%=12,this.last.month++,0!=t&&this.increment_year(t)}this.has_by_data("BYMONTHDAY")&&(this.by_data.BYMONTHDAY=this.normalizeByMonthDayRules(this.last.year,this.last.month,this.rule.parts.BYMONTHDAY))}},{key:"increment_year",value:function(t){this.last.day=1,this.last.year+=t}},{key:"increment_generic",value:function(t,e,n,r){this.last[e]+=t;var i=K(this.last[e]/n);this.last[e]%=n,0!=i&&this["increment_"+r](i)}},{key:"has_by_data",value:function(t){return t in this.rule.parts}},{key:"expand_year_days",value:function(t){var e=new b;this.days=[];for(var n={},r=0,i=["BYDAY","BYWEEKNO","BYMONTHDAY","BYMONTH","BYYEARDAY"];r0?(J=Z+7*(K-1))<=M&&this.days.push(L+J):(J=G+7*(K+1))>0&&this.days.push(L+J)}}catch(t){R.e(t)}finally{R.f()}}}}catch(t){I.e(t)}finally{I.f()}this.days.sort((function(t,e){return t-e}))}else if(2==y&&"BYDAY"in n&&"BYMONTHDAY"in n){var Q,$=s(this.expand_by_day(t));try{for($.s();!(Q=$.n()).done;){var tt=Q.value,et=b.fromDayOfYear(tt,t);this.by_data.BYMONTHDAY.indexOf(et.day)>=0&&this.days.push(tt)}}catch(t){$.e(t)}finally{$.f()}}else if(3==y&&"BYDAY"in n&&"BYMONTHDAY"in n&&"BYMONTH"in n){var nt,rt=s(this.expand_by_day(t));try{for(rt.s();!(nt=rt.n()).done;){var it=nt.value,at=b.fromDayOfYear(it,t);this.by_data.BYMONTH.indexOf(at.month)>=0&&this.by_data.BYMONTHDAY.indexOf(at.day)>=0&&this.days.push(it)}}catch(t){rt.e(t)}finally{rt.f()}}else if(2==y&&"BYDAY"in n&&"BYWEEKNO"in n){var st,ot=s(this.expand_by_day(t));try{for(ot.s();!(st=ot.n()).done;){var ut=st.value,lt=b.fromDayOfYear(ut,t).weekNumber(this.rule.wkst);this.by_data.BYWEEKNO.indexOf(lt)&&this.days.push(ut)}}catch(t){ot.e(t)}finally{ot.f()}}else if(3==y&&"BYDAY"in n&&"BYWEEKNO"in n&&"BYMONTHDAY"in n);else if(1==y&&"BYYEARDAY"in n)this.days=this.days.concat(this.by_data.BYYEARDAY);else if(2==y&&"BYYEARDAY"in n&&"BYDAY"in n){var ht,ct=b.isLeapYear(t)?366:365,ft=new Set(this.expand_by_day(t)),dt=s(this.by_data.BYYEARDAY);try{for(dt.s();!(ht=dt.n()).done;){var yt=ht.value;yt<0&&(yt+=ct+1),ft.has(yt)&&this.days.push(yt)}}catch(t){dt.e(t)}finally{dt.f()}}else this.days=[];var mt=b.isLeapYear(t)?366:365;return this.days.sort((function(t,e){return t<0&&(t+=mt+1),e<0&&(e+=mt+1),t-e})),0}},{key:"expand_by_day",value:function(t){var e=[],n=this.last.clone();n.year=t,n.month=1,n.day=1,n.isDate=!0;var r=n.dayOfWeek();n.month=12,n.day=31,n.isDate=!0;var i,a=n.dayOfWeek(),o=n.dayOfYear(),u=s(this.by_data.BYDAY);try{for(u.s();!(i=u.n()).done;){var l=i.value,h=this.ruleDayOfWeek(l),c=h[0],f=h[1];if(0==c)for(var d=(f+7-r)%7+1;d<=o;d+=7)e.push(d);else if(c>0){var y=void 0;y=f>=r?f-r+1:f-r+8,e.push(y+7*(c-1))}else{var m=void 0;c=-c,m=f<=a?o-a+f:o-a+f-7,e.push(m-7*(c-1))}}}catch(t){u.e(t)}finally{u.f()}return e}},{key:"is_day_in_byday",value:function(t){if(this.by_data.BYDAY){var e,n=s(this.by_data.BYDAY);try{for(n.s();!(e=n.n()).done;){var r=e.value,i=this.ruleDayOfWeek(r),a=i[0],o=i[1],u=t.dayOfWeek();if(0==a&&o==u||t.nthWeekDay(o,a)==t.day)return 1}}catch(t){n.e(t)}finally{n.f()}}return 0}},{key:"check_set_position",value:function(t){return!!this.has_by_data("BYSETPOS")&&-1!==this.by_data.BYSETPOS.indexOf(t)}},{key:"sort_byday_rules",value:function(t){for(var e=0;ethis.ruleDayOfWeek(t[e],this.rule.wkst)[1]){var r=t[e];t[e]=t[n],t[n]=r}}}},{key:"check_contract_restriction",value:function(e,n){var r=t._indexMap[e],i=t._expandMap[this.rule.freq][r],a=!1;if(e in this.by_data&&i==t.CONTRACT){var o,u=s(this.by_data[e]);try{for(u.s();!(o=u.n()).done;){if(o.value==n){a=!0;break}}}catch(t){u.e(t)}finally{u.f()}}else a=!0;return a}},{key:"check_contracting_rules",value:function(){var t=this.last.dayOfWeek(),e=this.last.weekNumber(this.rule.wkst),n=this.last.dayOfYear();return this.check_contract_restriction("BYSECOND",this.last.second)&&this.check_contract_restriction("BYMINUTE",this.last.minute)&&this.check_contract_restriction("BYHOUR",this.last.hour)&&this.check_contract_restriction("BYDAY",it.numericDayToIcalDay(t))&&this.check_contract_restriction("BYWEEKNO",e)&&this.check_contract_restriction("BYMONTHDAY",this.last.day)&&this.check_contract_restriction("BYMONTH",this.last.month)&&this.check_contract_restriction("BYYEARDAY",n)}},{key:"setup_defaults",value:function(e,n,r){var i=t._indexMap[e];return t._expandMap[this.rule.freq][i]!=t.CONTRACT&&(e in this.by_data||(this.by_data[e]=[r]),this.rule.freq!=n)?this.by_data[e][0]:r}},{key:"toJSON",value:function(){var t=Object.create(null);return t.initialized=this.initialized,t.rule=this.rule.toJSON(),t.dtstart=this.dtstart.toJSON(),t.by_data=this.by_data,t.days=this.days,t.last=this.last.toJSON(),t.by_indices=this.by_indices,t.occurrence_number=this.occurrence_number,t}}])}();o(X,"_indexMap",{BYSECOND:0,BYMINUTE:1,BYHOUR:2,BYDAY:3,BYMONTHDAY:4,BYYEARDAY:5,BYWEEKNO:6,BYMONTH:7,BYSETPOS:8}),o(X,"_expandMap",{SECONDLY:[1,1,1,1,1,1,1,1],MINUTELY:[2,1,1,1,1,1,1,1],HOURLY:[2,2,1,1,1,1,1,1],DAILY:[2,2,2,1,1,1,1,1],WEEKLY:[2,2,2,2,3,3,1,1],MONTHLY:[2,2,2,2,2,3,3,1],YEARLY:[2,2,2,2,2,2,2,2]}),o(X,"UNKNOWN",0),o(X,"CONTRACT",1),o(X,"EXPAND",2),o(X,"ILLEGAL",3);var Q=function(t){function e(){return r(this,e),n(this,e,["Recurrence rule has no valid occurrences"])}return l(e,t),a(e)}(v(Error)),$=/^(SU|MO|TU|WE|TH|FR|SA)$/,tt=/^([+-])?(5[0-3]|[1-4][0-9]|[1-9])?(SU|MO|TU|WE|TH|FR|SA)$/,et={SU:b.SUNDAY,MO:b.MONDAY,TU:b.TUESDAY,WE:b.WEDNESDAY,TH:b.THURSDAY,FR:b.FRIDAY,SA:b.SATURDAY},nt=Object.fromEntries(Object.entries(et).map((function(t){return t.reverse()}))),rt=["SECONDLY","MINUTELY","HOURLY","DAILY","WEEKLY","MONTHLY","YEARLY"],it=function(){function t(e){r(this,t),o(this,"parts",null),o(this,"interval",1),o(this,"wkst",b.MONDAY),o(this,"until",null),o(this,"count",null),o(this,"freq",null),o(this,"icalclass","icalrecur"),o(this,"icaltype","recur"),this.wrappedJSObject=this,this.parts={},e&&"object"===m(e)&&this.fromData(e)}return a(t,[{key:"iterator",value:function(t){return new X({rule:this,dtstart:t})}},{key:"clone",value:function(){return new t(this.toJSON())}},{key:"isFinite",value:function(){return!(!this.count&&!this.until)}},{key:"isByCount",value:function(){return!(!this.count||this.until)}},{key:"addComponent",value:function(t,e){var n=t.toUpperCase();n in this.parts?this.parts[n].push(e):this.parts[n]=[e]}},{key:"setComponent",value:function(t,e){this.parts[t.toUpperCase()]=e.slice()}},{key:"getComponent",value:function(t){var e=t.toUpperCase();return e in this.parts?this.parts[e].slice():[]}},{key:"getNextOccurrence",value:function(t,e){var n,r=this.iterator(t);do{n=r.next()}while(n&&n.compare(e)<=0);return n&&e.zone&&(n.zone=e.zone),n}},{key:"fromData",value:function(e){for(var n in e){var r=n.toUpperCase();r in ot?Array.isArray(e[n])?this.parts[r]=e[n]:this.parts[r]=[e[n]]:this[n]=e[n]}this.interval&&"number"!=typeof this.interval&&st.INTERVAL(this.interval,this),this.wkst&&"number"!=typeof this.wkst&&(this.wkst=t.icalDayToNumericDay(this.wkst)),!this.until||this.until instanceof b||(this.until=b.fromString(this.until))}},{key:"toJSON",value:function(){var e=Object.create(null);e.freq=this.freq,this.count&&(e.count=this.count),this.interval>1&&(e.interval=this.interval);for(var n=0,r=Object.entries(this.parts);n1&&(e+=";INTERVAL="+this.interval);for(var n=0,r=Object.entries(this.parts);n7&&(n-=7),nt[n]}}])}();function at(t,e,n,r){var i=r;if("+"===r[0]&&(i=r.slice(1)),i=U(i),void 0!==e&&r '+e);if(void 0!==n&&r>n)throw new Error(t+': invalid value "'+r+'" must be < '+e);return i}var st={FREQ:function(t,e,n){if(-1===rt.indexOf(t))throw new Error('invalid frequency "'+t+'" expected: "'+rt.join(", ")+'"');e.freq=t},COUNT:function(t,e,n){e.count=U(t)},INTERVAL:function(t,e,n){e.interval=U(t),e.interval<1&&(e.interval=1)},UNTIL:function(t,e,n){t.length>10?e.until=It.icalendar.value["date-time"].fromICAL(t):e.until=It.icalendar.value.date.fromICAL(t),n||(e.until=b.fromString(e.until))},WKST:function(t,e,n){if(!$.test(t))throw new Error('invalid WKST value "'+t+'"');e.wkst=it.icalDayToNumericDay(t)}},ot={BYSECOND:at.bind(void 0,"BYSECOND",0,60),BYMINUTE:at.bind(void 0,"BYMINUTE",0,59),BYHOUR:at.bind(void 0,"BYHOUR",0,23),BYDAY:function(t){if(tt.test(t))return t;throw new Error('invalid BYDAY value "'+t+'"')},BYMONTHDAY:at.bind(void 0,"BYMONTHDAY",-31,31),BYYEARDAY:at.bind(void 0,"BYYEARDAY",-366,366),BYWEEKNO:at.bind(void 0,"BYWEEKNO",-53,53),BYMONTH:at.bind(void 0,"BYMONTH",1,12),BYSETPOS:at.bind(void 0,"BYSETPOS",-366,366)},ut=/\\\\|\\,|\\[Nn]/g,lt=/\\|,|\n/g;function ht(t,e){return{matches:/.*/,fromICAL:function(e,n){return function(t,e,n){if(-1===t.indexOf("\\"))return t;n&&(e=new RegExp(e.source+"|\\\\"+n,e.flags));return t.replace(e,Tt)}(e,t,n)},toICAL:function(t,n){var r=e;return n&&(r=new RegExp(r.source+"|"+n,r.flags)),t.replace(r,(function(t){switch(t){case"\\":return"\\\\";case";":return"\\;";case",":return"\\,";case"\n":return"\\n";default:return t}}))}}}var ct={defaultType:"text"},ft={defaultType:"text",multiValue:","},dt={defaultType:"text",structuredValue:";"},yt={defaultType:"integer"},mt={defaultType:"date-time",allowedTypes:["date-time","date"]},pt={defaultType:"date-time"},vt={defaultType:"uri"},_t={defaultType:"utc-offset"},gt={defaultType:"recur"},Dt={defaultType:"date-and-or-time",allowedTypes:["date-time","date","text"]};function Tt(t){switch(t){case"\\\\":return"\\";case"\\;":return";";case"\\,":return",";case"\\n":case"\\N":return"\n";default:return t}}var Yt={categories:ft,url:vt,version:ct,uid:ct},kt={boolean:{values:["TRUE","FALSE"],fromICAL:function(t){return"TRUE"===t},toICAL:function(t){return t?"TRUE":"FALSE"}},float:{matches:/^[+-]?\d+\.\d+$/,fromICAL:function(t){var e=parseFloat(t);return P(e)?0:e},toICAL:function(t){return String(t)}},integer:{fromICAL:function(t){var e=parseInt(t);return P(e)?0:e},toICAL:function(t){return String(t)}},"utc-offset":{toICAL:function(t){return t.length<7?t.slice(0,3)+t.slice(4,6):t.slice(0,3)+t.slice(4,6)+t.slice(7,9)},fromICAL:function(t){return t.length<6?t.slice(0,3)+":"+t.slice(3,5):t.slice(0,3)+":"+t.slice(3,5)+":"+t.slice(5,7)},decorate:function(t){return Z.fromString(t)},undecorate:function(t){return t.toString()}}},At=q(kt,{text:ht(/\\\\|\\;|\\,|\\[Nn]/g,/\\|;|,|\n/g),uri:{},binary:{decorate:function(t){return _.fromString(t)},undecorate:function(t){return t.toString()}},"cal-address":{},date:{decorate:function(t,e){return It.strict?b.fromDateString(t,e):b.fromString(t,e)},undecorate:function(t){return t.toString()},fromICAL:function(t){return!It.strict&&t.length>=15?At["date-time"].fromICAL(t):t.slice(0,4)+"-"+t.slice(4,6)+"-"+t.slice(6,8)},toICAL:function(t){var e=t.length;return 10==e?t.slice(0,4)+t.slice(5,7)+t.slice(8,10):e>=19?At["date-time"].toICAL(t):t}},"date-time":{fromICAL:function(t){if(It.strict||8!=t.length){var e=t.slice(0,4)+"-"+t.slice(4,6)+"-"+t.slice(6,8)+"T"+t.slice(9,11)+":"+t.slice(11,13)+":"+t.slice(13,15);return t[15]&&"Z"===t[15]&&(e+="Z"),e}return At.date.fromICAL(t)},toICAL:function(t){var e=t.length;if(10!=e||It.strict){if(e>=19){var n=t.slice(0,4)+t.slice(5,7)+t.slice(8,13)+t.slice(14,16)+t.slice(17,19);return t[19]&&"Z"===t[19]&&(n+="Z"),n}return t}return At.date.toICAL(t)},decorate:function(t,e){return It.strict?b.fromDateTimeString(t,e):b.fromString(t,e)},undecorate:function(t){return t.toString()}},duration:{decorate:function(t){return T.fromString(t)},undecorate:function(t){return t.toString()}},period:{fromICAL:function(t){var e=t.split("/");return e[0]=At["date-time"].fromICAL(e[0]),T.isValueString(e[1])||(e[1]=At["date-time"].fromICAL(e[1])),e},toICAL:function(t){return t=t.slice(),It.strict||10!=t[0].length?t[0]=At["date-time"].toICAL(t[0]):t[0]=At.date.toICAL(t[0]),T.isValueString(t[1])||(It.strict||10!=t[1].length?t[1]=At["date-time"].toICAL(t[1]):t[1]=At.date.toICAL(t[1])),t.join("/")},decorate:function(t,e){return A.fromJSON(t,e,!It.strict)},undecorate:function(t){return t.toJSON()}},recur:{fromICAL:function(t){return it._stringToData(t,!0)},toICAL:function(t){for(var e="",n=0,r=Object.entries(t);n10?At["date-time"].toICAL(s):At.date.toICAL(s):"wkst"==a?"number"==typeof s&&(s=it.numericDayToIcalDay(s)):Array.isArray(s)&&(s=s.join(",")),e+=a.toUpperCase()+"="+s+";"}return e.slice(0,Math.max(0,e.length-1))},decorate:function(t){return it.fromData(t)},undecorate:function(t){return t.toJSON()}},time:{fromICAL:function(t){if(t.length<6)return t;var e=t.slice(0,2)+":"+t.slice(2,4)+":"+t.slice(4,6);return"Z"===t[6]&&(e+="Z"),e},toICAL:function(t){if(t.length<8)return t;var e=t.slice(0,2)+t.slice(3,5)+t.slice(6,8);return"Z"===t[8]&&(e+="Z"),e}}}),bt=q(Yt,{action:ct,attach:{defaultType:"uri"},attendee:{defaultType:"cal-address"},calscale:ct,class:ct,comment:ct,completed:pt,contact:ct,created:pt,description:ct,dtend:mt,dtstamp:pt,dtstart:mt,due:mt,duration:{defaultType:"duration"},exdate:{defaultType:"date-time",allowedTypes:["date-time","date"],multiValue:","},exrule:gt,freebusy:{defaultType:"period",multiValue:","},geo:{defaultType:"float",structuredValue:";"},"last-modified":pt,location:ct,method:ct,organizer:{defaultType:"cal-address"},"percent-complete":yt,priority:yt,prodid:ct,"related-to":ct,repeat:yt,rdate:{defaultType:"date-time",allowedTypes:["date-time","date","period"],multiValue:",",detectType:function(t){return-1!==t.indexOf("/")?"period":-1===t.indexOf("T")?"date":"date-time"}},"recurrence-id":mt,resources:ft,"request-status":dt,rrule:gt,sequence:yt,status:ct,summary:ct,transp:ct,trigger:{defaultType:"duration",allowedTypes:["duration","date-time"]},tzoffsetfrom:_t,tzoffsetto:_t,tzurl:vt,tzid:ct,tzname:ct}),Ot=q(kt,{text:ht(ut,lt),uri:ht(ut,lt),date:{decorate:function(t){return G.fromDateAndOrTimeString(t,"date")},undecorate:function(t){return t.toString()},fromICAL:function(t){return 8==t.length?At.date.fromICAL(t):"-"==t[0]&&6==t.length?t.slice(0,4)+"-"+t.slice(4):t},toICAL:function(t){return 10==t.length?At.date.toICAL(t):"-"==t[0]&&7==t.length?t.slice(0,4)+t.slice(5):t}},time:{decorate:function(t){return G.fromDateAndOrTimeString("T"+t,"time")},undecorate:function(t){return t.toString()},fromICAL:function(t){var e=Ot.time._splitZone(t,!0),n=e[0],r=e[1];return 6==r.length?r=r.slice(0,2)+":"+r.slice(2,4)+":"+r.slice(4,6):4==r.length&&"-"!=r[0]?r=r.slice(0,2)+":"+r.slice(2,4):5==r.length&&(r=r.slice(0,3)+":"+r.slice(3,5)),5!=n.length||"-"!=n[0]&&"+"!=n[0]||(n=n.slice(0,3)+":"+n.slice(3)),r+n},toICAL:function(t){var e=Ot.time._splitZone(t),n=e[0],r=e[1];return 8==r.length?r=r.slice(0,2)+r.slice(3,5)+r.slice(6,8):5==r.length&&"-"!=r[0]?r=r.slice(0,2)+r.slice(3,5):6==r.length&&(r=r.slice(0,3)+r.slice(4,6)),6!=n.length||"-"!=n[0]&&"+"!=n[0]||(n=n.slice(0,3)+n.slice(4)),r+n},_splitZone:function(t,e){var n,r,i=t.length-1,a=t.length-(e?5:6),s=t[a];return"Z"==t[i]?(n=t[i],r=t.slice(0,Math.max(0,i))):t.length>6&&("-"==s||"+"==s)?(n=t.slice(a),r=t.slice(0,Math.max(0,a))):(n="",r=t),[n,r]}},"date-time":{decorate:function(t){return G.fromDateAndOrTimeString(t,"date-time")},undecorate:function(t){return t.toString()},fromICAL:function(t){return Ot["date-and-or-time"].fromICAL(t)},toICAL:function(t){return Ot["date-and-or-time"].toICAL(t)}},"date-and-or-time":{decorate:function(t){return G.fromDateAndOrTimeString(t,"date-and-or-time")},undecorate:function(t){return t.toString()},fromICAL:function(t){var e=t.split("T");return(e[0]?Ot.date.fromICAL(e[0]):"")+(e[1]?"T"+Ot.time.fromICAL(e[1]):"")},toICAL:function(t){var e=t.split("T");return Ot.date.toICAL(e[0])+(e[1]?"T"+Ot.time.toICAL(e[1]):"")}},timestamp:At["date-time"],"language-tag":{matches:/^[a-zA-Z0-9-]+$/},"phone-number":{fromICAL:function(t){return Array.from(t).filter((function(t){return"\\"===t?void 0:t})).join("")},toICAL:function(t){return Array.from(t).map((function(t){return","===t||";"===t?"\\"+t:t})).join("")}}}),St=q(Yt,{adr:{defaultType:"text",structuredValue:";",multiValue:","},anniversary:Dt,bday:Dt,caladruri:vt,caluri:vt,clientpidmap:dt,email:ct,fburl:vt,fn:ct,gender:dt,geo:vt,impp:vt,key:vt,kind:ct,lang:{defaultType:"language-tag"},logo:vt,member:vt,n:{defaultType:"text",structuredValue:";",multiValue:","},nickname:ft,note:ct,org:{defaultType:"text",structuredValue:";"},photo:vt,related:vt,rev:{defaultType:"timestamp"},role:ct,sound:vt,source:vt,tel:{defaultType:"uri",allowedTypes:["uri","text"]},title:ct,tz:{defaultType:"text",allowedTypes:["text","utc-offset","uri"]},xml:ct}),wt=q(kt,{binary:At.binary,date:Ot.date,"date-time":Ot["date-time"],"phone-number":Ot["phone-number"],uri:At.uri,text:Ot.text,time:At.time,vcard:At.text,"utc-offset":{toICAL:function(t){return t.slice(0,7)},fromICAL:function(t){return t.slice(0,7)},decorate:function(t){return Z.fromString(t)},undecorate:function(t){return t.toString()}}}),Et=q(Yt,{fn:ct,n:{defaultType:"text",structuredValue:";",multiValue:","},nickname:ft,photo:{defaultType:"binary",allowedTypes:["binary","uri"]},bday:{defaultType:"date-time",allowedTypes:["date-time","date"],detectType:function(t){return-1===t.indexOf("T")?"date":"date-time"}},adr:{defaultType:"text",structuredValue:";",multiValue:","},label:ct,tel:{defaultType:"phone-number"},email:ct,mailer:ct,tz:{defaultType:"utc-offset",allowedTypes:["utc-offset","text"]},geo:{defaultType:"float",structuredValue:";"},title:ct,role:ct,logo:{defaultType:"binary",allowedTypes:["binary","uri"]},agent:{defaultType:"vcard",allowedTypes:["vcard","text","uri"]},org:dt,note:ft,prodid:ct,rev:{defaultType:"date-time",allowedTypes:["date-time","date"],detectType:function(t){return-1===t.indexOf("T")?"date":"date-time"}},"sort-string":ct,sound:{defaultType:"binary",allowedTypes:["binary","uri"]},class:ct,key:{defaultType:"binary",allowedTypes:["binary","text"]}}),Ct={name:"ical",value:At,param:{cutype:{values:["INDIVIDUAL","GROUP","RESOURCE","ROOM","UNKNOWN"],allowXName:!0,allowIanaToken:!0},"delegated-from":{valueType:"cal-address",multiValue:",",multiValueSeparateDQuote:!0},"delegated-to":{valueType:"cal-address",multiValue:",",multiValueSeparateDQuote:!0},encoding:{values:["8BIT","BASE64"]},fbtype:{values:["FREE","BUSY","BUSY-UNAVAILABLE","BUSY-TENTATIVE"],allowXName:!0,allowIanaToken:!0},member:{valueType:"cal-address",multiValue:",",multiValueSeparateDQuote:!0},partstat:{values:["NEEDS-ACTION","ACCEPTED","DECLINED","TENTATIVE","DELEGATED","COMPLETED","IN-PROCESS"],allowXName:!0,allowIanaToken:!0},range:{values:["THISANDFUTURE"]},related:{values:["START","END"]},reltype:{values:["PARENT","CHILD","SIBLING"],allowXName:!0,allowIanaToken:!0},role:{values:["REQ-PARTICIPANT","CHAIR","OPT-PARTICIPANT","NON-PARTICIPANT"],allowXName:!0,allowIanaToken:!0},rsvp:{values:["TRUE","FALSE"]},"sent-by":{valueType:"cal-address"},tzid:{matches:/^\//},value:{values:["binary","boolean","cal-address","date","date-time","duration","float","integer","period","recur","text","time","uri","utc-offset"],allowXName:!0,allowIanaToken:!0}},property:bt,propertyGroups:!1},xt={name:"vcard4",value:Ot,param:{type:{valueType:"text",multiValue:","},value:{values:["text","uri","date","time","date-time","date-and-or-time","timestamp","boolean","integer","float","utc-offset","language-tag"],allowXName:!0,allowIanaToken:!0}},property:St,propertyGroups:!0},Nt={name:"vcard3",value:wt,param:{type:{valueType:"text",multiValue:","},value:{values:["text","uri","date","date-time","phone-number","time","boolean","integer","float","utc-offset","vcard","binary"],allowXName:!0,allowIanaToken:!0}},property:Et,propertyGroups:!0},It={strict:!0,defaultSet:Ct,defaultType:"unknown",components:{vcard:xt,vcard3:Nt,vevent:Ct,vtodo:Ct,vjournal:Ct,valarm:Ct,vtimezone:Ct,daylight:Ct,standard:Ct},icalendar:Ct,vcard:xt,vcard3:Nt,getDesignSet:function(t){return t&&t in It.components?It.components[t]:It.defaultSet}},Bt="\r\n",Mt="unknown",zt={'"':"^'","\n":"^n","^":"^^"};function Lt(t){"string"==typeof t[0]&&(t=[t]);for(var e=0,n=t.length,r="";e0&&("version"!==t[1][0][0]||"4.0"!==t[1][0][3])&&(o="vcard3"),e=e||It.getDesignSet(o);a0&&"object"===m(t[0])&&"icaltype"in t[0]&&this.resetType(t[0].icaltype),this.isDecorated)for(;n=0;a--)n&&i[a][0]!==n||this._removeObjectByIndex(t,r,a)}},{key:"addSubcomponent",value:function(t){this._components||(this._components=[],this._hydratedComponentCount=0),t.parent&&t.parent.removeSubcomponent(t);var e=this.jCal[2].push(t.jCal);return this._components[e-1]=t,this._hydratedComponentCount++,t.parent=this,t}},{key:"removeSubcomponent",value:function(t){var e=this._removeObject(2,"_components",t);return e&&this._hydratedComponentCount--,e}},{key:"removeAllSubcomponents",value:function(t){var e=this._removeAllObjects(2,"_components",t);return this._hydratedComponentCount=0,e}},{key:"addProperty",value:function(t){if(!(t instanceof Pt))throw new TypeError("must be instance of ICAL.Property");this._properties||(this._properties=[],this._hydratedPropertyCount=0),t.parent&&t.parent.removeProperty(t);var e=this.jCal[1].push(t.jCal);return this._properties[e-1]=t,this._hydratedPropertyCount++,t.parent=this,t}},{key:"addPropertyWithValue",value:function(t,e){var n=new Pt(t);return n.setValue(e),this.addProperty(n),n}},{key:"updatePropertyWithValue",value:function(t,e){var n=this.getFirstProperty(t);return n?n.setValue(e):n=this.addPropertyWithValue(t,e),n}},{key:"removeProperty",value:function(t){var e=this._removeObject(1,"_properties",t);return e&&this._hydratedPropertyCount--,e}},{key:"removeAllProperties",value:function(t){var e=this._removeAllObjects(1,"_properties",t);return this._hydratedPropertyCount=0,e}},{key:"toJSON",value:function(){return this.jCal}},{key:"toString",value:function(){return Lt.component(this.jCal,this._designSet)}},{key:"getTimeZoneByID",value:function(t){if(this.parent)return this.parent.getTimeZoneByID(t);if(!this._timezoneCache)return null;if(this._timezoneCache.has(t))return this._timezoneCache.get(t);var e,n=s(this.getAllSubcomponents("vtimezone"));try{for(n.s();!(e=n.n()).done;){var r=e.value;if(r.getFirstProperty("tzid").getFirstValue()===t){var i=new I({component:r,tzid:t});return this._timezoneCache.set(t,i),i}}}catch(t){n.e(t)}finally{n.f()}return null}}],[{key:"fromString",value:function(e){return new t(C.component(e))}}])}(),jt=function(){return a((function t(e){r(this,t),o(this,"complete",!1),o(this,"ruleIterators",null),o(this,"ruleDates",null),o(this,"exDates",null),o(this,"ruleDateInc",0),o(this,"exDateInc",0),o(this,"exDate",null),o(this,"ruleDate",null),o(this,"dtstart",null),o(this,"last",null),this.ruleDates=[],this.exDates=[],this.fromData(e)}),[{key:"fromData",value:function(t){var e=j(t.dtstart,b);if(!e)throw new Error(".dtstart (ICAL.Time) must be given");if(this.dtstart=e,t.component)this._init(t.component);else{if(this.last=j(t.last,b)||e.clone(),!t.ruleIterators)throw new Error(".ruleIterators or .component must be given");this.ruleIterators=t.ruleIterators.map((function(t){return j(t,X)})),this.ruleDateInc=t.ruleDateInc,this.exDateInc=t.exDateInc,t.ruleDates&&(this.ruleDates=t.ruleDates.map((function(t){return j(t,b)})),this.ruleDate=this.ruleDates[this.ruleDateInc]),t.exDates&&(this.exDates=t.exDates.map((function(t){return j(t,b)})),this.exDate=this.exDates[this.exDateInc]),void 0!==t.complete&&(this.complete=t.complete)}}},{key:"_compare_special",value:function(t,e){return!t.isDate&&e.isDate?new b({year:t.year,month:t.month,day:t.day}).compare(e):t.compare(e)}},{key:"next",value:function(){for(var t,e,n,r=0;;){if(r++>500)throw new Error("max tries have occurred, rule may be impossible to fulfill.");if(e=this.ruleDate,t=this._nextRecurrenceIter(this.last),!e&&!t){this.complete=!0;break}if((!e||t&&e.compare(t.last)>0)&&(e=t.last.clone(),t.next()),this.ruleDate===e&&this._nextRuleDay(),this.last=e,!this.exDate||((n=this._compare_special(this.last,this.exDate))>0&&this._nextExDay(),0!==n))return this.last;this._nextExDay()}}},{key:"toJSON",value:function(){function t(t){return t.toJSON()}var e=Object.create(null);return e.ruleIterators=this.ruleIterators.map(t),this.ruleDates&&(e.ruleDates=this.ruleDates.map(t)),this.exDates&&(e.exDates=this.exDates.map(t)),e.ruleDateInc=this.ruleDateInc,e.exDateInc=this.exDateInc,e.last=this.last.toJSON(),e.dtstart=this.dtstart.toJSON(),e.complete=this.complete,e}},{key:"_extractDates",value:function(t,e){for(var n=[],r=t.getAllProperties(e),i=0,a=r.length;i0)&&(r=e);return r}}])}(),Vt=function(){function t(e,n){r(this,t),o(this,"exceptions",null),o(this,"strictExceptions",!1),e instanceof Ut||(n=e,e=null),this.component=e||new Ut("vevent"),this._rangeExceptionCache=Object.create(null),this.exceptions=Object.create(null),this.rangeExceptions=[],n&&n.strictExceptions&&(this.strictExceptions=n.strictExceptions),n&&n.exceptions?n.exceptions.forEach(this.relateException,this):this.component.parent&&!this.isRecurrenceException()&&this.component.parent.getAllSubcomponents("vevent").forEach((function(t){t.hasProperty("recurrence-id")&&this.relateException(t)}),this)}return a(t,[{key:"relateException",value:function(e){if(this.isRecurrenceException())throw new Error("cannot relate exception to exceptions");if(e instanceof Ut&&(e=new t(e)),this.strictExceptions&&e.uid!==this.uid)throw new Error("attempted to relate unrelated exception");var n=e.recurrenceId.toString();if(this.exceptions[n]=e,e.modifiesFuture()){var r=[e.recurrenceId.toUnixTime(),n],i=H(this.rangeExceptions,r,Ht);this.rangeExceptions.splice(i,0,r)}}},{key:"modifiesFuture",value:function(){return!!this.component.hasProperty("recurrence-id")&&this.component.getFirstProperty("recurrence-id").getParameter("range")===t.THISANDFUTURE}},{key:"findRangeException",value:function(t){if(!this.rangeExceptions.length)return null;var e=t.toUnixTime(),n=H(this.rangeExceptions,[e],Ht);if((n-=1)<0)return null;var r=this.rangeExceptions[n];return ee[0]?1:e[0]>t[0]?-1:0}o(Vt,"THISANDFUTURE","THISANDFUTURE");var Rt=function(){return a((function t(e){r(this,t),o(this,"parseEvent",!0),o(this,"parseTimezone",!0),o(this,"oncomplete",(function(){})),o(this,"onerror",(function(t){})),o(this,"ontimezone",(function(t){})),o(this,"onevent",(function(t){})),void 0===e&&(e={});for(var n=0,i=Object.entries(e);n> 18 & 0x3f;
+ h2 = bits >> 12 & 0x3f;
+ h3 = bits >> 6 & 0x3f;
+ h4 = bits & 0x3f;
+
+ // use hexets to index into b64, and append result to encoded string
+ tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
+ } while (i < data.length);
+
+ enc = tmp_arr.join('');
+
+ let r = data.length % 3;
+
+ return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3);
+
+ }
+
+ _b64_decode(data) {
+ // http://kevin.vanzonneveld.net
+ // + original by: Tyler Akins (http://rumkin.com)
+ // + improved by: Thunder.m
+ // + input by: Aman Gupta
+ // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ // + bugfixed by: Onno Marsman
+ // + bugfixed by: Pellentesque Malesuada
+ // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ // + input by: Brett Zamir (http://brett-zamir.me)
+ // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ // * example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA==');
+ // * returns 1: 'Kevin van Zonneveld'
+ // mozilla has this native
+ // - but breaks in 2.0.0.12!
+ //if (typeof this.window['btoa'] == 'function') {
+ // return btoa(data);
+ //}
+ let b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
+ "abcdefghijklmnopqrstuvwxyz0123456789+/=";
+ let o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
+ ac = 0,
+ dec = "",
+ tmp_arr = [];
+
+ if (!data) {
+ return data;
+ }
+
+ data += '';
+
+ do { // unpack four hexets into three octets using index points in b64
+ h1 = b64.indexOf(data.charAt(i++));
+ h2 = b64.indexOf(data.charAt(i++));
+ h3 = b64.indexOf(data.charAt(i++));
+ h4 = b64.indexOf(data.charAt(i++));
+
+ bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
+
+ o1 = bits >> 16 & 0xff;
+ o2 = bits >> 8 & 0xff;
+ o3 = bits & 0xff;
+
+ if (h3 == 64) {
+ tmp_arr[ac++] = String.fromCharCode(o1);
+ } else if (h4 == 64) {
+ tmp_arr[ac++] = String.fromCharCode(o1, o2);
+ } else {
+ tmp_arr[ac++] = String.fromCharCode(o1, o2, o3);
+ }
+ } while (i < data.length);
+
+ dec = tmp_arr.join('');
+
+ return dec;
+ }
+
+ /**
+ * The string representation of this value
+ * @return {String}
+ */
+ toString() {
+ return this.value;
+ }
+}
+
+/* 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 */
+
+
+const DURATION_LETTERS = /([PDWHMTS]{1,1})/;
+const DATA_PROPS_TO_COPY = ["weeks", "days", "hours", "minutes", "seconds", "isNegative"];
+
+/**
+ * This class represents the "duration" value type, with various calculation
+ * and manipulation methods.
+ *
+ * @memberof ICAL
+ */
+class Duration {
+ /**
+ * Returns a new ICAL.Duration instance from the passed seconds value.
+ *
+ * @param {Number} aSeconds The seconds to create the instance from
+ * @return {Duration} The newly created duration instance
+ */
+ static fromSeconds(aSeconds) {
+ return (new Duration()).fromSeconds(aSeconds);
+ }
+
+ /**
+ * Checks if the given string is an iCalendar duration value.
+ *
+ * @param {String} value The raw ical value
+ * @return {Boolean} True, if the given value is of the
+ * duration ical type
+ */
+ static isValueString(string) {
+ return (string[0] === 'P' || string[1] === 'P');
+ }
+
+ /**
+ * Creates a new {@link ICAL.Duration} instance from the passed string.
+ *
+ * @param {String} aStr The string to parse
+ * @return {Duration} The created duration instance
+ */
+ static fromString(aStr) {
+ let pos = 0;
+ let dict = Object.create(null);
+ let chunks = 0;
+
+ while ((pos = aStr.search(DURATION_LETTERS)) !== -1) {
+ let type = aStr[pos];
+ let numeric = aStr.slice(0, Math.max(0, pos));
+ aStr = aStr.slice(pos + 1);
+
+ chunks += parseDurationChunk(type, numeric, dict);
+ }
+
+ if (chunks < 2) {
+ // There must be at least a chunk with "P" and some unit chunk
+ throw new Error(
+ 'invalid duration value: Not enough duration components in "' + aStr + '"'
+ );
+ }
+
+ return new Duration(dict);
+ }
+
+ /**
+ * Creates a new ICAL.Duration instance from the given data object.
+ *
+ * @param {Object} aData An object with members of the duration
+ * @param {Number=} aData.weeks Duration in weeks
+ * @param {Number=} aData.days Duration in days
+ * @param {Number=} aData.hours Duration in hours
+ * @param {Number=} aData.minutes Duration in minutes
+ * @param {Number=} aData.seconds Duration in seconds
+ * @param {Boolean=} aData.isNegative If true, the duration is negative
+ * @return {Duration} The createad duration instance
+ */
+ static fromData(aData) {
+ return new Duration(aData);
+ }
+
+ /**
+ * Creates a new ICAL.Duration instance.
+ *
+ * @param {Object} data An object with members of the duration
+ * @param {Number=} data.weeks Duration in weeks
+ * @param {Number=} data.days Duration in days
+ * @param {Number=} data.hours Duration in hours
+ * @param {Number=} data.minutes Duration in minutes
+ * @param {Number=} data.seconds Duration in seconds
+ * @param {Boolean=} data.isNegative If true, the duration is negative
+ */
+ constructor(data) {
+ this.wrappedJSObject = this;
+ this.fromData(data);
+ }
+
+ /**
+ * The weeks in this duration
+ * @type {Number}
+ * @default 0
+ */
+ weeks = 0;
+
+ /**
+ * The days in this duration
+ * @type {Number}
+ * @default 0
+ */
+ days = 0;
+
+ /**
+ * The days in this duration
+ * @type {Number}
+ * @default 0
+ */
+ hours = 0;
+
+ /**
+ * The minutes in this duration
+ * @type {Number}
+ * @default 0
+ */
+ minutes = 0;
+
+ /**
+ * The seconds in this duration
+ * @type {Number}
+ * @default 0
+ */
+ seconds = 0;
+
+ /**
+ * The seconds in this duration
+ * @type {Boolean}
+ * @default false
+ */
+ isNegative = false;
+
+ /**
+ * The class identifier.
+ * @constant
+ * @type {String}
+ * @default "icalduration"
+ */
+ icalclass = "icalduration";
+
+ /**
+ * The type name, to be used in the jCal object.
+ * @constant
+ * @type {String}
+ * @default "duration"
+ */
+ icaltype = "duration";
+
+ /**
+ * Returns a clone of the duration object.
+ *
+ * @return {Duration} The cloned object
+ */
+ clone() {
+ return Duration.fromData(this);
+ }
+
+ /**
+ * The duration value expressed as a number of seconds.
+ *
+ * @return {Number} The duration value in seconds
+ */
+ toSeconds() {
+ let seconds = this.seconds + 60 * this.minutes + 3600 * this.hours +
+ 86400 * this.days + 7 * 86400 * this.weeks;
+ return (this.isNegative ? -seconds : seconds);
+ }
+
+ /**
+ * Reads the passed seconds value into this duration object. Afterwards,
+ * members like {@link ICAL.Duration#days days} and {@link ICAL.Duration#weeks weeks} will be set up
+ * accordingly.
+ *
+ * @param {Number} aSeconds The duration value in seconds
+ * @return {Duration} Returns this instance
+ */
+ fromSeconds(aSeconds) {
+ let secs = Math.abs(aSeconds);
+
+ this.isNegative = (aSeconds < 0);
+ this.days = trunc(secs / 86400);
+
+ // If we have a flat number of weeks, use them.
+ if (this.days % 7 == 0) {
+ this.weeks = this.days / 7;
+ this.days = 0;
+ } else {
+ this.weeks = 0;
+ }
+
+ secs -= (this.days + 7 * this.weeks) * 86400;
+
+ this.hours = trunc(secs / 3600);
+ secs -= this.hours * 3600;
+
+ this.minutes = trunc(secs / 60);
+ secs -= this.minutes * 60;
+
+ this.seconds = secs;
+ return this;
+ }
+
+ /**
+ * Sets up the current instance using members from the passed data object.
+ *
+ * @param {Object} aData An object with members of the duration
+ * @param {Number=} aData.weeks Duration in weeks
+ * @param {Number=} aData.days Duration in days
+ * @param {Number=} aData.hours Duration in hours
+ * @param {Number=} aData.minutes Duration in minutes
+ * @param {Number=} aData.seconds Duration in seconds
+ * @param {Boolean=} aData.isNegative If true, the duration is negative
+ */
+ fromData(aData) {
+ for (let prop of DATA_PROPS_TO_COPY) {
+ if (aData && prop in aData) {
+ this[prop] = aData[prop];
+ } else {
+ this[prop] = 0;
+ }
+ }
+ }
+
+ /**
+ * Resets the duration instance to the default values, i.e. PT0S
+ */
+ reset() {
+ this.isNegative = false;
+ this.weeks = 0;
+ this.days = 0;
+ this.hours = 0;
+ this.minutes = 0;
+ this.seconds = 0;
+ }
+
+ /**
+ * Compares the duration instance with another one.
+ *
+ * @param {Duration} aOther The instance to compare with
+ * @return {Number} -1, 0 or 1 for less/equal/greater
+ */
+ compare(aOther) {
+ let thisSeconds = this.toSeconds();
+ let otherSeconds = aOther.toSeconds();
+ return (thisSeconds > otherSeconds) - (thisSeconds < otherSeconds);
+ }
+
+ /**
+ * Normalizes the duration instance. For example, a duration with a value
+ * of 61 seconds will be normalized to 1 minute and 1 second.
+ */
+ normalize() {
+ this.fromSeconds(this.toSeconds());
+ }
+
+ /**
+ * The string representation of this duration.
+ * @return {String}
+ */
+ toString() {
+ if (this.toSeconds() == 0) {
+ return "PT0S";
+ } else {
+ let str = "";
+ if (this.isNegative) str += "-";
+ str += "P";
+ let hasWeeks = false;
+ if (this.weeks) {
+ if (this.days || this.hours || this.minutes || this.seconds) {
+ str += (this.weeks * 7 + this.days) + "D";
+ } else {
+ str += (this.weeks + "W");
+ hasWeeks = true;
+ }
+ } else if (this.days) {
+ str += (this.days + "D");
+ }
+
+ if (!hasWeeks) {
+ if (this.hours || this.minutes || this.seconds) {
+ str += "T";
+ if (this.hours) {
+ str += this.hours + "H";
+ }
+
+ if (this.minutes) {
+ str += this.minutes + "M";
+ }
+
+ if (this.seconds) {
+ str += this.seconds + "S";
+ }
+ }
+ }
+
+ return str;
+ }
+ }
+
+ /**
+ * The iCalendar string representation of this duration.
+ * @return {String}
+ */
+ toICALString() {
+ return this.toString();
+ }
+}
+
+/**
+ * Internal helper function to handle a chunk of a duration.
+ *
+ * @private
+ * @param {String} letter type of duration chunk
+ * @param {String} number numeric value or -/+
+ * @param {Object} dict target to assign values to
+ */
+function parseDurationChunk(letter, number, object) {
+ let type;
+ switch (letter) {
+ case 'P':
+ if (number && number === '-') {
+ object.isNegative = true;
+ } else {
+ object.isNegative = false;
+ }
+ // period
+ break;
+ case 'D':
+ type = 'days';
+ break;
+ case 'W':
+ type = 'weeks';
+ break;
+ case 'H':
+ type = 'hours';
+ break;
+ case 'M':
+ type = 'minutes';
+ break;
+ case 'S':
+ type = 'seconds';
+ break;
+ default:
+ // Not a valid chunk
+ return 0;
+ }
+
+ if (type) {
+ if (!number && number !== 0) {
+ throw new Error(
+ 'invalid duration value: Missing number before "' + letter + '"'
+ );
+ }
+ let num = parseInt(number, 10);
+ if (isStrictlyNaN(num)) {
+ throw new Error(
+ 'invalid duration value: Invalid number "' + number + '" before "' + letter + '"'
+ );
+ }
+ object[type] = num;
+ }
+
+ return 1;
+}
+
+/* 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 */
+
+
+/**
+ * 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").jCalComponent} jCalComponent
+ * Imports the 'occurrenceDetails' type from the "types.js" module
+ */
+
+/**
+ * This class represents the "period" value type, with various calculation and manipulation methods.
+ *
+ * @memberof ICAL
+ */
+class Period {
+ /**
+ * Creates a new {@link ICAL.Period} instance from the passed string.
+ *
+ * @param {String} str The string to parse
+ * @param {Property} prop The property this period will be on
+ * @return {Period} The created period instance
+ */
+ static fromString(str, prop) {
+ let parts = str.split('/');
+
+ if (parts.length !== 2) {
+ throw new Error(
+ 'Invalid string value: "' + str + '" must contain a "/" char.'
+ );
+ }
+
+ let options = {
+ start: Time.fromDateTimeString(parts[0], prop)
+ };
+
+ let end = parts[1];
+
+ if (Duration.isValueString(end)) {
+ options.duration = Duration.fromString(end);
+ } else {
+ options.end = Time.fromDateTimeString(end, prop);
+ }
+
+ return new Period(options);
+ }
+
+ /**
+ * Creates a new {@link ICAL.Period} instance from the given data object.
+ * The passed data object cannot contain both and end date and a duration.
+ *
+ * @param {Object} aData An object with members of the period
+ * @param {Time=} aData.start The start of the period
+ * @param {Time=} aData.end The end of the period
+ * @param {Duration=} aData.duration The duration of the period
+ * @return {Period} The period instance
+ */
+ static fromData(aData) {
+ return new Period(aData);
+ }
+
+ /**
+ * Returns a new period instance from the given jCal data array. The first
+ * member is always the start date string, the second member is either a
+ * duration or end date string.
+ *
+ * @param {jCalComponent} aData The jCal data array
+ * @param {Property} aProp The property this jCal data is on
+ * @param {Boolean} aLenient If true, data value can be both date and date-time
+ * @return {Period} The period instance
+ */
+ static fromJSON(aData, aProp, aLenient) {
+ function fromDateOrDateTimeString(aValue, dateProp) {
+ if (aLenient) {
+ return Time.fromString(aValue, dateProp);
+ } else {
+ return Time.fromDateTimeString(aValue, dateProp);
+ }
+ }
+
+ if (Duration.isValueString(aData[1])) {
+ return Period.fromData({
+ start: fromDateOrDateTimeString(aData[0], aProp),
+ duration: Duration.fromString(aData[1])
+ });
+ } else {
+ return Period.fromData({
+ start: fromDateOrDateTimeString(aData[0], aProp),
+ end: fromDateOrDateTimeString(aData[1], aProp)
+ });
+ }
+ }
+
+ /**
+ * Creates a new ICAL.Period instance. The passed data object cannot contain both and end date and
+ * a duration.
+ *
+ * @param {Object} aData An object with members of the period
+ * @param {Time=} aData.start The start of the period
+ * @param {Time=} aData.end The end of the period
+ * @param {Duration=} aData.duration The duration of the period
+ */
+ constructor(aData) {
+ this.wrappedJSObject = this;
+
+ if (aData && 'start' in aData) {
+ if (aData.start && !(aData.start instanceof Time)) {
+ throw new TypeError('.start must be an instance of ICAL.Time');
+ }
+ this.start = aData.start;
+ }
+
+ if (aData && aData.end && aData.duration) {
+ throw new Error('cannot accept both end and duration');
+ }
+
+ if (aData && 'end' in aData) {
+ if (aData.end && !(aData.end instanceof Time)) {
+ throw new TypeError('.end must be an instance of ICAL.Time');
+ }
+ this.end = aData.end;
+ }
+
+ if (aData && 'duration' in aData) {
+ if (aData.duration && !(aData.duration instanceof Duration)) {
+ throw new TypeError('.duration must be an instance of ICAL.Duration');
+ }
+ this.duration = aData.duration;
+ }
+ }
+
+
+ /**
+ * The start of the period
+ * @type {Time}
+ */
+ start = null;
+
+ /**
+ * The end of the period
+ * @type {Time}
+ */
+ end = null;
+
+ /**
+ * The duration of the period
+ * @type {Duration}
+ */
+ duration = null;
+
+ /**
+ * The class identifier.
+ * @constant
+ * @type {String}
+ * @default "icalperiod"
+ */
+ icalclass = "icalperiod";
+
+ /**
+ * The type name, to be used in the jCal object.
+ * @constant
+ * @type {String}
+ * @default "period"
+ */
+ icaltype = "period";
+
+ /**
+ * Returns a clone of the duration object.
+ *
+ * @return {Period} The cloned object
+ */
+ clone() {
+ return Period.fromData({
+ start: this.start ? this.start.clone() : null,
+ end: this.end ? this.end.clone() : null,
+ duration: this.duration ? this.duration.clone() : null
+ });
+ }
+
+ /**
+ * Calculates the duration of the period, either directly or by subtracting
+ * start from end date.
+ *
+ * @return {Duration} The calculated duration
+ */
+ getDuration() {
+ if (this.duration) {
+ return this.duration;
+ } else {
+ return this.end.subtractDate(this.start);
+ }
+ }
+
+ /**
+ * Calculates the end date of the period, either directly or by adding
+ * duration to start date.
+ *
+ * @return {Time} The calculated end date
+ */
+ getEnd() {
+ if (this.end) {
+ return this.end;
+ } else {
+ let end = this.start.clone();
+ end.addDuration(this.duration);
+ return end;
+ }
+ }
+
+ /**
+ * Compare this period with a date or other period. To maintain the logic where a.compare(b)
+ * returns 1 when a > b, this function will return 1 when the period is after the date, 0 when the
+ * date is within the period, and -1 when the period is before the date. When comparing two
+ * periods, as soon as they overlap in any way this will return 0.
+ *
+ * @param {Time|Period} dt The date or other period to compare with
+ */
+ compare(dt) {
+ if (dt.compare(this.start) < 0) {
+ return 1;
+ } else if (dt.compare(this.getEnd()) > 0) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * The string representation of this period.
+ * @return {String}
+ */
+ toString() {
+ return this.start + "/" + (this.end || this.duration);
+ }
+
+ /**
+ * The jCal representation of this period type.
+ * @return {Object}
+ */
+ toJSON() {
+ return [this.start.toString(), (this.end || this.duration).toString()];
+ }
+
+ /**
+ * The iCalendar string representation of this period.
+ * @return {String}
+ */
+ toICALString() {
+ return this.start.toICALString() + "/" +
+ (this.end || this.duration).toICALString();
+ }
+}
+
+/* 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 */
+
+
+/**
+ * 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").weekDay} weekDay
+ * Imports the 'weekDay' type from the "types.js" module
+ *
+ * @ignore
+ * @typedef {import("./types.js").timeInit} timeInit
+ * Imports the 'timeInit' type from the "types.js" module
+ */
+
+/**
+ * @classdesc
+ * iCalendar Time representation (similar to JS Date object). Fully
+ * independent of system (OS) timezone / time. Unlike JS Date, the month
+ * January is 1, not zero.
+ *
+ * @example
+ * var time = new ICAL.Time({
+ * year: 2012,
+ * month: 10,
+ * day: 11
+ * minute: 0,
+ * second: 0,
+ * isDate: false
+ * });
+ *
+ *
+ * @memberof ICAL
+*/
+class Time {
+ static _dowCache = {};
+ static _wnCache = {};
+
+ /**
+ * Returns the days in the given month
+ *
+ * @param {Number} month The month to check
+ * @param {Number} year The year to check
+ * @return {Number} The number of days in the month
+ */
+ static daysInMonth(month, year) {
+ let _daysInMonth = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
+ let days = 30;
+
+ if (month < 1 || month > 12) return days;
+
+ days = _daysInMonth[month];
+
+ if (month == 2) {
+ days += Time.isLeapYear(year);
+ }
+
+ return days;
+ }
+
+ /**
+ * Checks if the year is a leap year
+ *
+ * @param {Number} year The year to check
+ * @return {Boolean} True, if the year is a leap year
+ */
+ static isLeapYear(year) {
+ if (year <= 1752) {
+ return ((year % 4) == 0);
+ } else {
+ return (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0));
+ }
+ }
+
+ /**
+ * Create a new ICAL.Time from the day of year and year. The date is returned
+ * in floating timezone.
+ *
+ * @param {Number} aDayOfYear The day of year
+ * @param {Number} aYear The year to create the instance in
+ * @return {Time} The created instance with the calculated date
+ */
+ static fromDayOfYear(aDayOfYear, aYear) {
+ let year = aYear;
+ let doy = aDayOfYear;
+ let tt = new Time();
+ tt.auto_normalize = false;
+ let is_leap = (Time.isLeapYear(year) ? 1 : 0);
+
+ if (doy < 1) {
+ year--;
+ is_leap = (Time.isLeapYear(year) ? 1 : 0);
+ doy += Time.daysInYearPassedMonth[is_leap][12];
+ return Time.fromDayOfYear(doy, year);
+ } else if (doy > Time.daysInYearPassedMonth[is_leap][12]) {
+ is_leap = (Time.isLeapYear(year) ? 1 : 0);
+ doy -= Time.daysInYearPassedMonth[is_leap][12];
+ year++;
+ return Time.fromDayOfYear(doy, year);
+ }
+
+ tt.year = year;
+ tt.isDate = true;
+
+ for (let month = 11; month >= 0; month--) {
+ if (doy > Time.daysInYearPassedMonth[is_leap][month]) {
+ tt.month = month + 1;
+ tt.day = doy - Time.daysInYearPassedMonth[is_leap][month];
+ break;
+ }
+ }
+
+ tt.auto_normalize = true;
+ return tt;
+ }
+
+ /**
+ * Returns a new ICAL.Time instance from a date string, e.g 2015-01-02.
+ *
+ * @deprecated Use {@link ICAL.Time.fromDateString} instead
+ * @param {String} str The string to create from
+ * @return {Time} The date/time instance
+ */
+ static fromStringv2(str) {
+ return new Time({
+ year: parseInt(str.slice(0, 4), 10),
+ month: parseInt(str.slice(5, 7), 10),
+ day: parseInt(str.slice(8, 10), 10),
+ isDate: true
+ });
+ }
+
+ /**
+ * Returns a new ICAL.Time instance from a date string, e.g 2015-01-02.
+ *
+ * @param {String} aValue The string to create from
+ * @return {Time} The date/time instance
+ */
+ static fromDateString(aValue) {
+ // Dates should have no timezone.
+ // Google likes to sometimes specify Z on dates
+ // we specifically ignore that to avoid issues.
+
+ // YYYY-MM-DD
+ // 2012-10-10
+ return new Time({
+ year: strictParseInt(aValue.slice(0, 4)),
+ month: strictParseInt(aValue.slice(5, 7)),
+ day: strictParseInt(aValue.slice(8, 10)),
+ isDate: true
+ });
+ }
+
+ /**
+ * Returns a new ICAL.Time instance from a date-time string, e.g
+ * 2015-01-02T03:04:05. If a property is specified, the timezone is set up
+ * from the property's TZID parameter.
+ *
+ * @param {String} aValue The string to create from
+ * @param {Property=} prop The property the date belongs to
+ * @return {Time} The date/time instance
+ */
+ static fromDateTimeString(aValue, prop) {
+ if (aValue.length < 19) {
+ throw new Error(
+ 'invalid date-time value: "' + aValue + '"'
+ );
+ }
+
+ let zone;
+ let zoneId;
+
+ if (aValue.slice(-1) === 'Z') {
+ zone = Timezone.utcTimezone;
+ } else if (prop) {
+ zoneId = prop.getParameter('tzid');
+
+ if (prop.parent) {
+ if (prop.parent.name === 'standard' || prop.parent.name === 'daylight') {
+ // Per RFC 5545 3.8.2.4 and 3.8.2.2, start/end date-times within
+ // these components MUST be specified in local time.
+ zone = Timezone.localTimezone;
+ } else if (zoneId) {
+ // If the desired time zone is defined within the component tree,
+ // fetch its definition and prefer that.
+ zone = prop.parent.getTimeZoneByID(zoneId);
+ }
+ }
+ }
+
+ const timeData = {
+ year: strictParseInt(aValue.slice(0, 4)),
+ month: strictParseInt(aValue.slice(5, 7)),
+ day: strictParseInt(aValue.slice(8, 10)),
+ hour: strictParseInt(aValue.slice(11, 13)),
+ minute: strictParseInt(aValue.slice(14, 16)),
+ second: strictParseInt(aValue.slice(17, 19)),
+ };
+
+ // Although RFC 5545 requires that all TZIDs used within a file have a
+ // corresponding time zone definition, we may not be parsing the full file
+ // or we may be dealing with a non-compliant file; in either case, we can
+ // check our own time zone service for the TZID in a last-ditch effort.
+ if (zoneId && !zone) {
+ timeData.timezone = zoneId;
+ }
+
+ // 2012-10-10T10:10:10(Z)?
+ return new Time(timeData, zone);
+ }
+
+ /**
+ * Returns a new ICAL.Time instance from a date or date-time string,
+ *
+ * @param {String} aValue The string to create from
+ * @param {Property=} prop The property the date belongs to
+ * @return {Time} The date/time instance
+ */
+ static fromString(aValue, aProperty) {
+ if (aValue.length > 10) {
+ return Time.fromDateTimeString(aValue, aProperty);
+ } else {
+ return Time.fromDateString(aValue);
+ }
+ }
+
+ /**
+ * Creates a new ICAL.Time instance from the given Javascript Date.
+ *
+ * @param {?Date} aDate The Javascript Date to read, or null to reset
+ * @param {Boolean} [useUTC=false] If true, the UTC values of the date will be used
+ */
+ static fromJSDate(aDate, useUTC) {
+ let tt = new Time();
+ return tt.fromJSDate(aDate, useUTC);
+ }
+
+ /**
+ * Creates a new ICAL.Time instance from the the passed data object.
+ *
+ * @param {timeInit} aData Time initialization
+ * @param {Timezone=} aZone Timezone this position occurs in
+ */
+ static fromData = function fromData(aData, aZone) {
+ let t = new Time();
+ return t.fromData(aData, aZone);
+ };
+
+ /**
+ * Creates a new ICAL.Time instance from the current moment.
+ * The instance is “floating” - has no timezone relation.
+ * To create an instance considering the time zone, call
+ * ICAL.Time.fromJSDate(new Date(), true)
+ * @return {Time}
+ */
+ static now() {
+ return Time.fromJSDate(new Date(), false);
+ }
+
+ /**
+ * Returns the date on which ISO week number 1 starts.
+ *
+ * @see Time#weekNumber
+ * @param {Number} aYear The year to search in
+ * @param {weekDay=} aWeekStart The week start weekday, used for calculation.
+ * @return {Time} The date on which week number 1 starts
+ */
+ static weekOneStarts(aYear, aWeekStart) {
+ let t = Time.fromData({
+ year: aYear,
+ month: 1,
+ day: 1,
+ isDate: true
+ });
+
+ let dow = t.dayOfWeek();
+ let wkst = aWeekStart || Time.DEFAULT_WEEK_START;
+ if (dow > Time.THURSDAY) {
+ t.day += 7;
+ }
+ if (wkst > Time.THURSDAY) {
+ t.day -= 7;
+ }
+
+ t.day -= dow - wkst;
+
+ return t;
+ }
+
+ /**
+ * Get the dominical letter for the given year. Letters range from A - G for
+ * common years, and AG to GF for leap years.
+ *
+ * @param {Number} yr The year to retrieve the letter for
+ * @return {String} The dominical letter.
+ */
+ static getDominicalLetter(yr) {
+ let LTRS = "GFEDCBA";
+ let dom = (yr + (yr / 4 | 0) + (yr / 400 | 0) - (yr / 100 | 0) - 1) % 7;
+ let isLeap = Time.isLeapYear(yr);
+ if (isLeap) {
+ return LTRS[(dom + 6) % 7] + LTRS[dom];
+ } else {
+ return LTRS[dom];
+ }
+ }
+
+ static #epochTime = null;
+ /**
+ * January 1st, 1970 as an ICAL.Time.
+ * @type {Time}
+ * @constant
+ * @instance
+ */
+ static get epochTime() {
+ if (!this.#epochTime) {
+ this.#epochTime = Time.fromData({
+ year: 1970,
+ month: 1,
+ day: 1,
+ hour: 0,
+ minute: 0,
+ second: 0,
+ isDate: false,
+ timezone: "Z"
+ });
+ }
+ return this.#epochTime;
+ }
+
+ static _cmp_attr(a, b, attr) {
+ if (a[attr] > b[attr]) return 1;
+ if (a[attr] < b[attr]) return -1;
+ return 0;
+ }
+
+ /**
+ * The days that have passed in the year after a given month. The array has
+ * two members, one being an array of passed days for non-leap years, the
+ * other analog for leap years.
+ * @example
+ * var isLeapYear = ICAL.Time.isLeapYear(year);
+ * var passedDays = ICAL.Time.daysInYearPassedMonth[isLeapYear][month];
+ * @type {Array.>}
+ */
+ static daysInYearPassedMonth = [
+ [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365],
+ [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]
+ ];
+
+ static SUNDAY = 1;
+ static MONDAY = 2;
+ static TUESDAY = 3;
+ static WEDNESDAY = 4;
+ static THURSDAY = 5;
+ static FRIDAY = 6;
+ static SATURDAY = 7;
+
+ /**
+ * The default weekday for the WKST part.
+ * @constant
+ * @default ICAL.Time.MONDAY
+ */
+ static DEFAULT_WEEK_START = 2; // MONDAY
+
+ /**
+ * Creates a new ICAL.Time instance.
+ *
+ * @param {timeInit} data Time initialization
+ * @param {Timezone} zone timezone this position occurs in
+ */
+ constructor(data, zone) {
+ this.wrappedJSObject = this;
+
+ /**
+ * @type {timeInit}
+ * @private
+ */
+ this._time = Object.create(null);
+
+ /* time defaults */
+ this._time.year = 0;
+ this._time.month = 1;
+ this._time.day = 1;
+ this._time.hour = 0;
+ this._time.minute = 0;
+ this._time.second = 0;
+ this._time.isDate = false;
+
+ this.fromData(data, zone);
+ }
+
+ /**
+ * The class identifier.
+ * @constant
+ * @type {String}
+ * @default "icaltime"
+ */
+ icalclass = "icaltime";
+ _cachedUnixTime = null;
+
+ /**
+ * The type name, to be used in the jCal object. This value may change and
+ * is strictly defined by the {@link ICAL.Time#isDate isDate} member.
+ * @type {String}
+ * @default "date-time"
+ */
+ get icaltype() {
+ return this.isDate ? 'date' : 'date-time';
+ }
+
+ /**
+ * The timezone for this time.
+ * @type {Timezone}
+ */
+ zone = null;
+
+ /**
+ * Internal uses to indicate that a change has been made and the next read
+ * operation must attempt to normalize the value (for example changing the
+ * day to 33).
+ *
+ * @type {Boolean}
+ * @private
+ */
+ _pendingNormalization = false;
+
+ /**
+ * The year of this date.
+ * @type {Number}
+ */
+ get year() {
+ return this._getTimeAttr('year');
+ }
+
+ set year(val) {
+ this._setTimeAttr('year', val);
+ }
+
+ /**
+ * The month of this date.
+ * @type {Number}
+ */
+ get month() {
+ return this._getTimeAttr('month');
+ }
+
+ set month(val) {
+ this._setTimeAttr('month', val);
+ }
+
+ /**
+ * The day of this date.
+ * @type {Number}
+ */
+ get day() {
+ return this._getTimeAttr('day');
+ }
+
+ set day(val) {
+ this._setTimeAttr('day', val);
+ }
+
+ /**
+ * The hour of this date-time.
+ * @type {Number}
+ */
+ get hour() {
+ return this._getTimeAttr('hour');
+ }
+
+ set hour(val) {
+ this._setTimeAttr('hour', val);
+ }
+
+ /**
+ * The minute of this date-time.
+ * @type {Number}
+ */
+ get minute() {
+ return this._getTimeAttr('minute');
+ }
+
+ set minute(val) {
+ this._setTimeAttr('minute', val);
+ }
+
+ /**
+ * The second of this date-time.
+ * @type {Number}
+ */
+ get second() {
+ return this._getTimeAttr('second');
+ }
+
+ set second(val) {
+ this._setTimeAttr('second', val);
+ }
+
+ /**
+ * If true, the instance represents a date (as opposed to a date-time)
+ * @type {Boolean}
+ */
+ get isDate() {
+ return this._getTimeAttr('isDate');
+ }
+
+ set isDate(val) {
+ this._setTimeAttr('isDate', val);
+ }
+
+ /**
+ * @private
+ * @param {String} attr Attribute to get (one of: year, month,
+ * day, hour, minute, second, isDate)
+ * @return {Number|Boolean} Current value for the attribute
+ */
+ _getTimeAttr(attr) {
+ if (this._pendingNormalization) {
+ this._normalize();
+ this._pendingNormalization = false;
+ }
+
+ return this._time[attr];
+ }
+
+ /**
+ * @private
+ * @param {String} attr Attribute to set (one of: year, month,
+ * day, hour, minute, second, isDate)
+ * @param {Number|Boolean} val New value for the attribute
+ */
+ _setTimeAttr(attr, val) {
+ // Check if isDate will be set and if was not set to normalize date.
+ // This avoids losing days when seconds, minutes and hours are zeroed
+ // what normalize will do when time is a date.
+ if (attr === "isDate" && val && !this._time.isDate) {
+ this.adjust(0, 0, 0, 0);
+ }
+ this._cachedUnixTime = null;
+ this._pendingNormalization = true;
+ this._time[attr] = val;
+ }
+
+ /**
+ * Returns a clone of the time object.
+ *
+ * @return {Time} The cloned object
+ */
+ clone() {
+ return new Time(this._time, this.zone);
+ }
+
+ /**
+ * Reset the time instance to epoch time
+ */
+ reset() {
+ this.fromData(Time.epochTime);
+ this.zone = Timezone.utcTimezone;
+ }
+
+ /**
+ * Reset the time instance to the given date/time values.
+ *
+ * @param {Number} year The year to set
+ * @param {Number} month The month to set
+ * @param {Number} day The day to set
+ * @param {Number} hour The hour to set
+ * @param {Number} minute The minute to set
+ * @param {Number} second The second to set
+ * @param {Timezone} timezone The timezone to set
+ */
+ resetTo(year, month, day, hour, minute, second, timezone) {
+ this.fromData({
+ year: year,
+ month: month,
+ day: day,
+ hour: hour,
+ minute: minute,
+ second: second,
+ zone: timezone
+ });
+ }
+
+ /**
+ * Set up the current instance from the Javascript date value.
+ *
+ * @param {?Date} aDate The Javascript Date to read, or null to reset
+ * @param {Boolean} [useUTC=false] If true, the UTC values of the date will be used
+ */
+ fromJSDate(aDate, useUTC) {
+ if (!aDate) {
+ this.reset();
+ } else {
+ if (useUTC) {
+ this.zone = Timezone.utcTimezone;
+ this.year = aDate.getUTCFullYear();
+ this.month = aDate.getUTCMonth() + 1;
+ this.day = aDate.getUTCDate();
+ this.hour = aDate.getUTCHours();
+ this.minute = aDate.getUTCMinutes();
+ this.second = aDate.getUTCSeconds();
+ } else {
+ this.zone = Timezone.localTimezone;
+ this.year = aDate.getFullYear();
+ this.month = aDate.getMonth() + 1;
+ this.day = aDate.getDate();
+ this.hour = aDate.getHours();
+ this.minute = aDate.getMinutes();
+ this.second = aDate.getSeconds();
+ }
+ }
+ this._cachedUnixTime = null;
+ return this;
+ }
+
+ /**
+ * Sets up the current instance using members from the passed data object.
+ *
+ * @param {timeInit} aData Time initialization
+ * @param {Timezone=} aZone Timezone this position occurs in
+ */
+ fromData(aData, aZone) {
+ if (aData) {
+ for (let [key, value] of Object.entries(aData)) {
+ // ical type cannot be set
+ if (key === 'icaltype') continue;
+ this[key] = value;
+ }
+ }
+
+ if (aZone) {
+ this.zone = aZone;
+ }
+
+ if (aData && !("isDate" in aData)) {
+ this.isDate = !("hour" in aData);
+ } else if (aData && ("isDate" in aData)) {
+ this.isDate = aData.isDate;
+ }
+
+ if (aData && "timezone" in aData) {
+ let zone = TimezoneService.get(
+ aData.timezone
+ );
+
+ this.zone = zone || Timezone.localTimezone;
+ }
+
+ if (aData && "zone" in aData) {
+ this.zone = aData.zone;
+ }
+
+ if (!this.zone) {
+ this.zone = Timezone.localTimezone;
+ }
+
+ this._cachedUnixTime = null;
+ return this;
+ }
+
+ /**
+ * Calculate the day of week.
+ * @param {weekDay=} aWeekStart
+ * The week start weekday, defaults to SUNDAY
+ * @return {weekDay}
+ */
+ dayOfWeek(aWeekStart) {
+ let firstDow = aWeekStart || Time.SUNDAY;
+ let dowCacheKey = (this.year << 12) + (this.month << 8) + (this.day << 3) + firstDow;
+ if (dowCacheKey in Time._dowCache) {
+ return Time._dowCache[dowCacheKey];
+ }
+
+ // Using Zeller's algorithm
+ let q = this.day;
+ let m = this.month + (this.month < 3 ? 12 : 0);
+ let Y = this.year - (this.month < 3 ? 1 : 0);
+
+ let h = (q + Y + trunc(((m + 1) * 26) / 10) + trunc(Y / 4));
+ { // eslint-disable-line no-constant-condition
+ h += trunc(Y / 100) * 6 + trunc(Y / 400);
+ }
+
+ // Normalize to 1 = wkst
+ h = ((h + 7 - firstDow) % 7) + 1;
+ Time._dowCache[dowCacheKey] = h;
+ return h;
+ }
+
+ /**
+ * Calculate the day of year.
+ * @return {Number}
+ */
+ dayOfYear() {
+ let is_leap = (Time.isLeapYear(this.year) ? 1 : 0);
+ let diypm = Time.daysInYearPassedMonth;
+ return diypm[is_leap][this.month - 1] + this.day;
+ }
+
+ /**
+ * Returns a copy of the current date/time, rewound to the start of the
+ * week. The resulting ICAL.Time instance is of icaltype date, even if this
+ * is a date-time.
+ *
+ * @param {weekDay=} aWeekStart
+ * The week start weekday, defaults to SUNDAY
+ * @return {Time} The start of the week (cloned)
+ */
+ startOfWeek(aWeekStart) {
+ let firstDow = aWeekStart || Time.SUNDAY;
+ let result = this.clone();
+ result.day -= ((this.dayOfWeek() + 7 - firstDow) % 7);
+ result.isDate = true;
+ result.hour = 0;
+ result.minute = 0;
+ result.second = 0;
+ return result;
+ }
+
+ /**
+ * Returns a copy of the current date/time, shifted to the end of the week.
+ * The resulting ICAL.Time instance is of icaltype date, even if this is a
+ * date-time.
+ *
+ * @param {weekDay=} aWeekStart
+ * The week start weekday, defaults to SUNDAY
+ * @return {Time} The end of the week (cloned)
+ */
+ endOfWeek(aWeekStart) {
+ let firstDow = aWeekStart || Time.SUNDAY;
+ let result = this.clone();
+ result.day += (7 - this.dayOfWeek() + firstDow - Time.SUNDAY) % 7;
+ result.isDate = true;
+ result.hour = 0;
+ result.minute = 0;
+ result.second = 0;
+ return result;
+ }
+
+ /**
+ * Returns a copy of the current date/time, rewound to the start of the
+ * month. The resulting ICAL.Time instance is of icaltype date, even if
+ * this is a date-time.
+ *
+ * @return {Time} The start of the month (cloned)
+ */
+ startOfMonth() {
+ let result = this.clone();
+ result.day = 1;
+ result.isDate = true;
+ result.hour = 0;
+ result.minute = 0;
+ result.second = 0;
+ return result;
+ }
+
+ /**
+ * Returns a copy of the current date/time, shifted to the end of the
+ * month. The resulting ICAL.Time instance is of icaltype date, even if
+ * this is a date-time.
+ *
+ * @return {Time} The end of the month (cloned)
+ */
+ endOfMonth() {
+ let result = this.clone();
+ result.day = Time.daysInMonth(result.month, result.year);
+ result.isDate = true;
+ result.hour = 0;
+ result.minute = 0;
+ result.second = 0;
+ return result;
+ }
+
+ /**
+ * Returns a copy of the current date/time, rewound to the start of the
+ * year. The resulting ICAL.Time instance is of icaltype date, even if
+ * this is a date-time.
+ *
+ * @return {Time} The start of the year (cloned)
+ */
+ startOfYear() {
+ let result = this.clone();
+ result.day = 1;
+ result.month = 1;
+ result.isDate = true;
+ result.hour = 0;
+ result.minute = 0;
+ result.second = 0;
+ return result;
+ }
+
+ /**
+ * Returns a copy of the current date/time, shifted to the end of the
+ * year. The resulting ICAL.Time instance is of icaltype date, even if
+ * this is a date-time.
+ *
+ * @return {Time} The end of the year (cloned)
+ */
+ endOfYear() {
+ let result = this.clone();
+ result.day = 31;
+ result.month = 12;
+ result.isDate = true;
+ result.hour = 0;
+ result.minute = 0;
+ result.second = 0;
+ return result;
+ }
+
+ /**
+ * First calculates the start of the week, then returns the day of year for
+ * this date. If the day falls into the previous year, the day is zero or negative.
+ *
+ * @param {weekDay=} aFirstDayOfWeek
+ * The week start weekday, defaults to SUNDAY
+ * @return {Number} The calculated day of year
+ */
+ startDoyWeek(aFirstDayOfWeek) {
+ let firstDow = aFirstDayOfWeek || Time.SUNDAY;
+ let delta = this.dayOfWeek() - firstDow;
+ if (delta < 0) delta += 7;
+ return this.dayOfYear() - delta;
+ }
+
+ /**
+ * Get the dominical letter for the current year. Letters range from A - G
+ * for common years, and AG to GF for leap years.
+ *
+ * @param {Number} yr The year to retrieve the letter for
+ * @return {String} The dominical letter.
+ */
+ getDominicalLetter() {
+ return Time.getDominicalLetter(this.year);
+ }
+
+ /**
+ * Finds the nthWeekDay relative to the current month (not day). The
+ * returned value is a day relative the month that this month belongs to so
+ * 1 would indicate the first of the month and 40 would indicate a day in
+ * the following month.
+ *
+ * @param {Number} aDayOfWeek Day of the week see the day name constants
+ * @param {Number} aPos Nth occurrence of a given week day values
+ * of 1 and 0 both indicate the first weekday of that type. aPos may
+ * be either positive or negative
+ *
+ * @return {Number} numeric value indicating a day relative
+ * to the current month of this time object
+ */
+ nthWeekDay(aDayOfWeek, aPos) {
+ let daysInMonth = Time.daysInMonth(this.month, this.year);
+ let weekday;
+ let pos = aPos;
+
+ let start = 0;
+
+ let otherDay = this.clone();
+
+ if (pos >= 0) {
+ otherDay.day = 1;
+
+ // because 0 means no position has been given
+ // 1 and 0 indicate the same day.
+ if (pos != 0) {
+ // remove the extra numeric value
+ pos--;
+ }
+
+ // set current start offset to current day.
+ start = otherDay.day;
+
+ // find the current day of week
+ let startDow = otherDay.dayOfWeek();
+
+ // calculate the difference between current
+ // day of the week and desired day of the week
+ let offset = aDayOfWeek - startDow;
+
+
+ // if the offset goes into the past
+ // week we add 7 so it goes into the next
+ // week. We only want to go forward in time here.
+ if (offset < 0)
+ // this is really important otherwise we would
+ // end up with dates from in the past.
+ offset += 7;
+
+ // add offset to start so start is the same
+ // day of the week as the desired day of week.
+ start += offset;
+
+ // because we are going to add (and multiply)
+ // the numeric value of the day we subtract it
+ // from the start position so not to add it twice.
+ start -= aDayOfWeek;
+
+ // set week day
+ weekday = aDayOfWeek;
+ } else {
+
+ // then we set it to the last day in the current month
+ otherDay.day = daysInMonth;
+
+ // find the ends weekday
+ let endDow = otherDay.dayOfWeek();
+
+ pos++;
+
+ weekday = (endDow - aDayOfWeek);
+
+ if (weekday < 0) {
+ weekday += 7;
+ }
+
+ weekday = daysInMonth - weekday;
+ }
+
+ weekday += pos * 7;
+
+ return start + weekday;
+ }
+
+ /**
+ * Checks if current time is the nth weekday, relative to the current
+ * month. Will always return false when rule resolves outside of current
+ * month.
+ *
+ * @param {weekDay} aDayOfWeek Day of week to check
+ * @param {Number} aPos Relative position
+ * @return {Boolean} True, if it is the nth weekday
+ */
+ isNthWeekDay(aDayOfWeek, aPos) {
+ let dow = this.dayOfWeek();
+
+ if (aPos === 0 && dow === aDayOfWeek) {
+ return true;
+ }
+
+ // get pos
+ let day = this.nthWeekDay(aDayOfWeek, aPos);
+
+ if (day === this.day) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Calculates the ISO 8601 week number. The first week of a year is the
+ * week that contains the first Thursday. The year can have 53 weeks, if
+ * January 1st is a Friday.
+ *
+ * Note there are regions where the first week of the year is the one that
+ * starts on January 1st, which may offset the week number. Also, if a
+ * different week start is specified, this will also affect the week
+ * number.
+ *
+ * @see Time.weekOneStarts
+ * @param {weekDay} aWeekStart The weekday the week starts with
+ * @return {Number} The ISO week number
+ */
+ weekNumber(aWeekStart) {
+ let wnCacheKey = (this.year << 12) + (this.month << 8) + (this.day << 3) + aWeekStart;
+ if (wnCacheKey in Time._wnCache) {
+ return Time._wnCache[wnCacheKey];
+ }
+ // This function courtesty of Julian Bucknall, published under the MIT license
+ // http://www.boyet.com/articles/publishedarticles/calculatingtheisoweeknumb.html
+ // plus some fixes to be able to use different week starts.
+ let week1;
+
+ let dt = this.clone();
+ dt.isDate = true;
+ let isoyear = this.year;
+
+ if (dt.month == 12 && dt.day > 25) {
+ week1 = Time.weekOneStarts(isoyear + 1, aWeekStart);
+ if (dt.compare(week1) < 0) {
+ week1 = Time.weekOneStarts(isoyear, aWeekStart);
+ } else {
+ isoyear++;
+ }
+ } else {
+ week1 = Time.weekOneStarts(isoyear, aWeekStart);
+ if (dt.compare(week1) < 0) {
+ week1 = Time.weekOneStarts(--isoyear, aWeekStart);
+ }
+ }
+
+ let daysBetween = (dt.subtractDate(week1).toSeconds() / 86400);
+ let answer = trunc(daysBetween / 7) + 1;
+ Time._wnCache[wnCacheKey] = answer;
+ return answer;
+ }
+
+ /**
+ * Adds the duration to the current time. The instance is modified in
+ * place.
+ *
+ * @param {Duration} aDuration The duration to add
+ */
+ addDuration(aDuration) {
+ let mult = (aDuration.isNegative ? -1 : 1);
+
+ // because of the duration optimizations it is much
+ // more efficient to grab all the values up front
+ // then set them directly (which will avoid a normalization call).
+ // So we don't actually normalize until we need it.
+ let second = this.second;
+ let minute = this.minute;
+ let hour = this.hour;
+ let day = this.day;
+
+ second += mult * aDuration.seconds;
+ minute += mult * aDuration.minutes;
+ hour += mult * aDuration.hours;
+ day += mult * aDuration.days;
+ day += mult * 7 * aDuration.weeks;
+
+ this.second = second;
+ this.minute = minute;
+ this.hour = hour;
+ this.day = day;
+
+ this._cachedUnixTime = null;
+ }
+
+ /**
+ * Subtract the date details (_excluding_ timezone). Useful for finding
+ * the relative difference between two time objects excluding their
+ * timezone differences.
+ *
+ * @param {Time} aDate The date to subtract
+ * @return {Duration} The difference as a duration
+ */
+ subtractDate(aDate) {
+ let unixTime = this.toUnixTime() + this.utcOffset();
+ let other = aDate.toUnixTime() + aDate.utcOffset();
+ return Duration.fromSeconds(unixTime - other);
+ }
+
+ /**
+ * Subtract the date details, taking timezones into account.
+ *
+ * @param {Time} aDate The date to subtract
+ * @return {Duration} The difference in duration
+ */
+ subtractDateTz(aDate) {
+ let unixTime = this.toUnixTime();
+ let other = aDate.toUnixTime();
+ return Duration.fromSeconds(unixTime - other);
+ }
+
+ /**
+ * Compares the ICAL.Time instance with another one, or a period.
+ *
+ * @param {Time|Period} aOther The instance to compare with
+ * @return {Number} -1, 0 or 1 for less/equal/greater
+ */
+ compare(other) {
+ if (other instanceof Period) {
+ return -1 * other.compare(this);
+ } else {
+ let a = this.toUnixTime();
+ let b = other.toUnixTime();
+
+ if (a > b) return 1;
+ if (b > a) return -1;
+ return 0;
+ }
+ }
+
+ /**
+ * Compares only the date part of this instance with another one.
+ *
+ * @param {Time} other The instance to compare with
+ * @param {Timezone} tz The timezone to compare in
+ * @return {Number} -1, 0 or 1 for less/equal/greater
+ */
+ compareDateOnlyTz(other, tz) {
+ let a = this.convertToZone(tz);
+ let b = other.convertToZone(tz);
+ let rc = 0;
+
+ if ((rc = Time._cmp_attr(a, b, "year")) != 0) return rc;
+ if ((rc = Time._cmp_attr(a, b, "month")) != 0) return rc;
+ if ((rc = Time._cmp_attr(a, b, "day")) != 0) return rc;
+
+ return rc;
+ }
+
+ /**
+ * Convert the instance into another timezone. The returned ICAL.Time
+ * instance is always a copy.
+ *
+ * @param {Timezone} zone The zone to convert to
+ * @return {Time} The copy, converted to the zone
+ */
+ convertToZone(zone) {
+ let copy = this.clone();
+ let zone_equals = (this.zone.tzid == zone.tzid);
+
+ if (!this.isDate && !zone_equals) {
+ Timezone.convert_time(copy, this.zone, zone);
+ }
+
+ copy.zone = zone;
+ return copy;
+ }
+
+ /**
+ * Calculates the UTC offset of the current date/time in the timezone it is
+ * in.
+ *
+ * @return {Number} UTC offset in seconds
+ */
+ utcOffset() {
+ if (this.zone == Timezone.localTimezone ||
+ this.zone == Timezone.utcTimezone) {
+ return 0;
+ } else {
+ return this.zone.utcOffset(this);
+ }
+ }
+
+ /**
+ * Returns an RFC 5545 compliant ical representation of this object.
+ *
+ * @return {String} ical date/date-time
+ */
+ toICALString() {
+ let string = this.toString();
+
+ if (string.length > 10) {
+ return design.icalendar.value['date-time'].toICAL(string);
+ } else {
+ return design.icalendar.value.date.toICAL(string);
+ }
+ }
+
+ /**
+ * The string representation of this date/time, in jCal form
+ * (including : and - separators).
+ * @return {String}
+ */
+ toString() {
+ let result = this.year + '-' +
+ pad2(this.month) + '-' +
+ pad2(this.day);
+
+ if (!this.isDate) {
+ result += 'T' + pad2(this.hour) + ':' +
+ pad2(this.minute) + ':' +
+ pad2(this.second);
+
+ if (this.zone === Timezone.utcTimezone) {
+ result += 'Z';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Converts the current instance to a Javascript date
+ * @return {Date}
+ */
+ toJSDate() {
+ if (this.zone == Timezone.localTimezone) {
+ if (this.isDate) {
+ return new Date(this.year, this.month - 1, this.day);
+ } else {
+ return new Date(this.year, this.month - 1, this.day,
+ this.hour, this.minute, this.second, 0);
+ }
+ } else {
+ return new Date(this.toUnixTime() * 1000);
+ }
+ }
+
+ _normalize() {
+ if (this._time.isDate) {
+ this._time.hour = 0;
+ this._time.minute = 0;
+ this._time.second = 0;
+ }
+ this.adjust(0, 0, 0, 0);
+
+ return this;
+ }
+
+ /**
+ * Adjust the date/time by the given offset
+ *
+ * @param {Number} aExtraDays The extra amount of days
+ * @param {Number} aExtraHours The extra amount of hours
+ * @param {Number} aExtraMinutes The extra amount of minutes
+ * @param {Number} aExtraSeconds The extra amount of seconds
+ * @param {Number=} aTime The time to adjust, defaults to the
+ * current instance.
+ */
+ adjust(aExtraDays, aExtraHours, aExtraMinutes, aExtraSeconds, aTime) {
+
+ let minutesOverflow, hoursOverflow,
+ daysOverflow = 0, yearsOverflow = 0;
+
+ let second, minute, hour, day;
+ let daysInMonth;
+
+ let time = aTime || this._time;
+
+ if (!time.isDate) {
+ second = time.second + aExtraSeconds;
+ time.second = second % 60;
+ minutesOverflow = trunc(second / 60);
+ if (time.second < 0) {
+ time.second += 60;
+ minutesOverflow--;
+ }
+
+ minute = time.minute + aExtraMinutes + minutesOverflow;
+ time.minute = minute % 60;
+ hoursOverflow = trunc(minute / 60);
+ if (time.minute < 0) {
+ time.minute += 60;
+ hoursOverflow--;
+ }
+
+ hour = time.hour + aExtraHours + hoursOverflow;
+
+ time.hour = hour % 24;
+ daysOverflow = trunc(hour / 24);
+ if (time.hour < 0) {
+ time.hour += 24;
+ daysOverflow--;
+ }
+ }
+
+
+ // Adjust month and year first, because we need to know what month the day
+ // is in before adjusting it.
+ if (time.month > 12) {
+ yearsOverflow = trunc((time.month - 1) / 12);
+ } else if (time.month < 1) {
+ yearsOverflow = trunc(time.month / 12) - 1;
+ }
+
+ time.year += yearsOverflow;
+ time.month -= 12 * yearsOverflow;
+
+ // Now take care of the days (and adjust month if needed)
+ day = time.day + aExtraDays + daysOverflow;
+
+ if (day > 0) {
+ for (;;) {
+ daysInMonth = Time.daysInMonth(time.month, time.year);
+ if (day <= daysInMonth) {
+ break;
+ }
+
+ time.month++;
+ if (time.month > 12) {
+ time.year++;
+ time.month = 1;
+ }
+
+ day -= daysInMonth;
+ }
+ } else {
+ while (day <= 0) {
+ if (time.month == 1) {
+ time.year--;
+ time.month = 12;
+ } else {
+ time.month--;
+ }
+
+ day += Time.daysInMonth(time.month, time.year);
+ }
+ }
+
+ time.day = day;
+
+ this._cachedUnixTime = null;
+ return this;
+ }
+
+ /**
+ * Sets up the current instance from unix time, the number of seconds since
+ * January 1st, 1970.
+ *
+ * @param {Number} seconds The seconds to set up with
+ */
+ fromUnixTime(seconds) {
+ this.zone = Timezone.utcTimezone;
+ // We could use `fromJSDate` here, but this is about twice as fast.
+ // We could also clone `epochTime` and use `adjust` for a more
+ // ical.js-centric approach, but this is about 100 times as fast.
+ let date = new Date(seconds * 1000);
+ this.year = date.getUTCFullYear();
+ this.month = date.getUTCMonth() + 1;
+ this.day = date.getUTCDate();
+ if (this._time.isDate) {
+ this.hour = 0;
+ this.minute = 0;
+ this.second = 0;
+ } else {
+ this.hour = date.getUTCHours();
+ this.minute = date.getUTCMinutes();
+ this.second = date.getUTCSeconds();
+ }
+
+ this._cachedUnixTime = null;
+ }
+
+ /**
+ * Converts the current instance to seconds since January 1st 1970.
+ *
+ * @return {Number} Seconds since 1970
+ */
+ toUnixTime() {
+ if (this._cachedUnixTime !== null) {
+ return this._cachedUnixTime;
+ }
+ let offset = this.utcOffset();
+
+ // we use the offset trick to ensure
+ // that we are getting the actual UTC time
+ let ms = Date.UTC(
+ this.year,
+ this.month - 1,
+ this.day,
+ this.hour,
+ this.minute,
+ this.second - offset
+ );
+
+ // seconds
+ this._cachedUnixTime = ms / 1000;
+ return this._cachedUnixTime;
+ }
+
+ /**
+ * Converts time to into Object which can be serialized then re-created
+ * using the constructor.
+ *
+ * @example
+ * // toJSON will automatically be called
+ * var json = JSON.stringify(mytime);
+ *
+ * var deserialized = JSON.parse(json);
+ *
+ * var time = new ICAL.Time(deserialized);
+ *
+ * @return {Object}
+ */
+ toJSON() {
+ let copy = [
+ 'year',
+ 'month',
+ 'day',
+ 'hour',
+ 'minute',
+ 'second',
+ 'isDate'
+ ];
+
+ let result = Object.create(null);
+
+ let i = 0;
+ let len = copy.length;
+ let prop;
+
+ for (; i < len; i++) {
+ prop = copy[i];
+ result[prop] = this[prop];
+ }
+
+ if (this.zone) {
+ result.timezone = this.zone.tzid;
+ }
+
+ return result;
+ }
+}
+
+/* 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 */
+
+
+/**
+ * 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").parserState} parserState
+ * Imports the 'parserState' type from the "types.js" module
+ * @typedef {import("./types.js").designSet} designSet
+ * Imports the 'designSet' type from the "types.js" module
+ */
+
+const CHAR = /[^ \t]/;
+const VALUE_DELIMITER = ':';
+const PARAM_DELIMITER = ';';
+const PARAM_NAME_DELIMITER = '=';
+const DEFAULT_VALUE_TYPE$1 = 'unknown';
+const DEFAULT_PARAM_TYPE = 'text';
+const RFC6868_REPLACE_MAP$1 = { "^'": '"', "^n": "\n", "^^": "^" };
+
+/**
+ * Parses iCalendar or vCard data into a raw jCal object. Consult
+ * documentation on the {@tutorial layers|layers of parsing} for more
+ * details.
+ *
+ * @function ICAL.parse
+ * @memberof ICAL
+ * @variation function
+ * @todo Fix the API to be more clear on the return type
+ * @param {String} input The string data to parse
+ * @return {Object|Object[]} A single jCal object, or an array thereof
+ */
+function parse(input) {
+ let state = {};
+ let root = state.component = [];
+
+ state.stack = [root];
+
+ parse._eachLine(input, function(err, line) {
+ parse._handleContentLine(line, state);
+ });
+
+
+ // when there are still items on the stack
+ // throw a fatal error, a component was not closed
+ // correctly in that case.
+ if (state.stack.length > 1) {
+ throw new ParserError(
+ 'invalid ical body. component began but did not end'
+ );
+ }
+
+ state = null;
+
+ return (root.length == 1 ? root[0] : root);
+}
+
+/**
+ * Parse an iCalendar property value into the jCal for a single property
+ *
+ * @function ICAL.parse.property
+ * @param {String} str
+ * The iCalendar property string to parse
+ * @param {designSet=} designSet
+ * The design data to use for this property
+ * @return {Object}
+ * The jCal Object containing the property
+ */
+parse.property = function(str, designSet) {
+ let state = {
+ component: [[], []],
+ designSet: designSet || design.defaultSet
+ };
+ parse._handleContentLine(str, state);
+ return state.component[1][0];
+};
+
+/**
+ * Convenience method to parse a component. You can use ICAL.parse() directly
+ * instead.
+ *
+ * @function ICAL.parse.component
+ * @see ICAL.parse(function)
+ * @param {String} str The iCalendar component string to parse
+ * @return {Object} The jCal Object containing the component
+ */
+parse.component = function(str) {
+ return parse(str);
+};
+
+
+/**
+ * An error that occurred during parsing.
+ *
+ * @param {String} message The error message
+ * @memberof ICAL.parse
+ * @extends {Error}
+ */
+class ParserError extends Error {
+ name = this.constructor.name;
+}
+
+// classes & constants
+parse.ParserError = ParserError;
+
+
+/**
+ * Handles a single line of iCalendar/vCard, updating the state.
+ *
+ * @private
+ * @function ICAL.parse._handleContentLine
+ * @param {String} line The content line to process
+ * @param {parserState} state The current state of the line parsing
+ */
+parse._handleContentLine = function(line, state) {
+ // break up the parts of the line
+ let valuePos = line.indexOf(VALUE_DELIMITER);
+ let paramPos = line.indexOf(PARAM_DELIMITER);
+
+ let lastParamIndex;
+ let lastValuePos;
+
+ // name of property or begin/end
+ let name;
+ let value;
+ // params is only overridden if paramPos !== -1.
+ // we can't do params = params || {} later on
+ // because it sacrifices ops.
+ let params = {};
+
+ /**
+ * Different property cases
+ *
+ *
+ * 1. RRULE:FREQ=foo
+ * // FREQ= is not a param but the value
+ *
+ * 2. ATTENDEE;ROLE=REQ-PARTICIPANT;
+ * // ROLE= is a param because : has not happened yet
+ */
+ // when the parameter delimiter is after the
+ // value delimiter then it is not a parameter.
+
+ if ((paramPos !== -1 && valuePos !== -1)) {
+ // when the parameter delimiter is after the
+ // value delimiter then it is not a parameter.
+ if (paramPos > valuePos) {
+ paramPos = -1;
+ }
+ }
+
+ let parsedParams;
+ if (paramPos !== -1) {
+ name = line.slice(0, Math.max(0, paramPos)).toLowerCase();
+ parsedParams = parse._parseParameters(line.slice(Math.max(0, paramPos)), 0, state.designSet);
+ if (parsedParams[2] == -1) {
+ throw new ParserError("Invalid parameters in '" + line + "'");
+ }
+ params = parsedParams[0];
+ // Handle parameter values with multiple entries
+ let parsedParamLength;
+ if (typeof parsedParams[1] === 'string') {
+ parsedParamLength = parsedParams[1].length;
+ } else {
+ parsedParamLength = parsedParams[1].reduce((accumulator, currentValue) => {
+ return accumulator + currentValue.length;
+ }, 0);
+ }
+ lastParamIndex = parsedParamLength + parsedParams[2] + paramPos;
+ if ((lastValuePos =
+ line.slice(Math.max(0, lastParamIndex)).indexOf(VALUE_DELIMITER)) !== -1) {
+ value = line.slice(Math.max(0, lastParamIndex + lastValuePos + 1));
+ } else {
+ throw new ParserError("Missing parameter value in '" + line + "'");
+ }
+ } else if (valuePos !== -1) {
+ // without parmeters (BEGIN:VCAENDAR, CLASS:PUBLIC)
+ name = line.slice(0, Math.max(0, valuePos)).toLowerCase();
+ value = line.slice(Math.max(0, valuePos + 1));
+
+ if (name === 'begin') {
+ let newComponent = [value.toLowerCase(), [], []];
+ if (state.stack.length === 1) {
+ state.component.push(newComponent);
+ } else {
+ state.component[2].push(newComponent);
+ }
+ state.stack.push(state.component);
+ state.component = newComponent;
+ if (!state.designSet) {
+ state.designSet = design.getDesignSet(state.component[0]);
+ }
+ return;
+ } else if (name === 'end') {
+ state.component = state.stack.pop();
+ return;
+ }
+ // If it is not begin/end, then this is a property with an empty value,
+ // which should be considered valid.
+ } else {
+ /**
+ * Invalid line.
+ * The rational to throw an error is we will
+ * never be certain that the rest of the file
+ * is sane and it is unlikely that we can serialize
+ * the result correctly either.
+ */
+ throw new ParserError(
+ 'invalid line (no token ";" or ":") "' + line + '"'
+ );
+ }
+
+ let valueType;
+ let multiValue = false;
+ let structuredValue = false;
+ let propertyDetails;
+ let splitName;
+ let ungroupedName;
+
+ // fetch the ungrouped part of the name
+ if (state.designSet.propertyGroups && name.indexOf('.') !== -1) {
+ splitName = name.split('.');
+ params.group = splitName[0];
+ ungroupedName = splitName[1];
+ } else {
+ ungroupedName = name;
+ }
+
+ if (ungroupedName in state.designSet.property) {
+ propertyDetails = state.designSet.property[ungroupedName];
+
+ if ('multiValue' in propertyDetails) {
+ multiValue = propertyDetails.multiValue;
+ }
+
+ if ('structuredValue' in propertyDetails) {
+ structuredValue = propertyDetails.structuredValue;
+ }
+
+ if (value && 'detectType' in propertyDetails) {
+ valueType = propertyDetails.detectType(value);
+ }
+ }
+
+ // attempt to determine value
+ if (!valueType) {
+ if (!('value' in params)) {
+ if (propertyDetails) {
+ valueType = propertyDetails.defaultType;
+ } else {
+ valueType = DEFAULT_VALUE_TYPE$1;
+ }
+ } else {
+ // possible to avoid this?
+ valueType = params.value.toLowerCase();
+ }
+ }
+
+ delete params.value;
+
+ /**
+ * Note on `var result` juggling:
+ *
+ * I observed that building the array in pieces has adverse
+ * effects on performance, so where possible we inline the creation.
+ * It is a little ugly but resulted in ~2000 additional ops/sec.
+ */
+
+ let result;
+ if (multiValue && structuredValue) {
+ value = parse._parseMultiValue(value, structuredValue, valueType, [], multiValue, state.designSet, structuredValue);
+ result = [ungroupedName, params, valueType, value];
+ } else if (multiValue) {
+ result = [ungroupedName, params, valueType];
+ parse._parseMultiValue(value, multiValue, valueType, result, null, state.designSet, false);
+ } else if (structuredValue) {
+ value = parse._parseMultiValue(value, structuredValue, valueType, [], null, state.designSet, structuredValue);
+ result = [ungroupedName, params, valueType, value];
+ } else {
+ value = parse._parseValue(value, valueType, state.designSet, false);
+ result = [ungroupedName, params, valueType, value];
+ }
+ // rfc6350 requires that in vCard 4.0 the first component is the VERSION
+ // component with as value 4.0, note that 3.0 does not have this requirement.
+ if (state.component[0] === 'vcard' && state.component[1].length === 0 &&
+ !(name === 'version' && value === '4.0')) {
+ state.designSet = design.getDesignSet("vcard3");
+ }
+ state.component[1].push(result);
+};
+
+/**
+ * Parse a value from the raw value into the jCard/jCal value.
+ *
+ * @private
+ * @function ICAL.parse._parseValue
+ * @param {String} value Original value
+ * @param {String} type Type of value
+ * @param {Object} designSet The design data to use for this value
+ * @return {Object} varies on type
+ */
+parse._parseValue = function(value, type, designSet, structuredValue) {
+ if (type in designSet.value && 'fromICAL' in designSet.value[type]) {
+ return designSet.value[type].fromICAL(value, structuredValue);
+ }
+ return value;
+};
+
+/**
+ * Parse parameters from a string to object.
+ *
+ * @function ICAL.parse._parseParameters
+ * @private
+ * @param {String} line A single unfolded line
+ * @param {Number} start Position to start looking for properties
+ * @param {Object} designSet The design data to use for this property
+ * @return {Array} Array containing key/valye pairs of parsed parameters, the
+ * parsed value, and the position of the last parameter found
+ */
+parse._parseParameters = function(line, start, designSet) {
+ let lastParam = start;
+ let pos = 0;
+ let delim = PARAM_NAME_DELIMITER;
+ let result = {};
+ let name, lcname;
+ let value, valuePos = -1;
+ let type, multiValue, mvdelim;
+
+ // find the next '=' sign
+ // use lastParam and pos to find name
+ // check if " is used if so get value from "->"
+ // then increment pos to find next ;
+
+ while ((pos !== false) &&
+ (pos = line.indexOf(delim, pos + 1)) !== -1) {
+
+ name = line.slice(lastParam + 1, pos);
+ if (name.length == 0) {
+ throw new ParserError("Empty parameter name in '" + line + "'");
+ }
+ lcname = name.toLowerCase();
+ mvdelim = false;
+ multiValue = false;
+
+ if (lcname in designSet.param && designSet.param[lcname].valueType) {
+ type = designSet.param[lcname].valueType;
+ } else {
+ type = DEFAULT_PARAM_TYPE;
+ }
+
+ if (lcname in designSet.param) {
+ multiValue = designSet.param[lcname].multiValue;
+ if (designSet.param[lcname].multiValueSeparateDQuote) {
+ mvdelim = parse._rfc6868Escape('"' + multiValue + '"');
+ }
+ }
+
+ let nextChar = line[pos + 1];
+ if (nextChar === '"') {
+ valuePos = pos + 2;
+ pos = line.indexOf('"', valuePos);
+ if (multiValue && pos != -1) {
+ let extendedValue = true;
+ while (extendedValue) {
+ if (line[pos + 1] == multiValue && line[pos + 2] == '"') {
+ pos = line.indexOf('"', pos + 3);
+ } else {
+ extendedValue = false;
+ }
+ }
+ }
+ if (pos === -1) {
+ throw new ParserError(
+ 'invalid line (no matching double quote) "' + line + '"'
+ );
+ }
+ value = line.slice(valuePos, pos);
+ lastParam = line.indexOf(PARAM_DELIMITER, pos);
+ let propValuePos = line.indexOf(VALUE_DELIMITER, pos);
+ // if either no next parameter or delimeter in property value, let's stop here
+ if (lastParam === -1 || (propValuePos !== -1 && lastParam > propValuePos)) {
+ pos = false;
+ }
+ } else {
+ valuePos = pos + 1;
+
+ // move to next ";"
+ let nextPos = line.indexOf(PARAM_DELIMITER, valuePos);
+ let propValuePos = line.indexOf(VALUE_DELIMITER, valuePos);
+ if (propValuePos !== -1 && nextPos > propValuePos) {
+ // this is a delimiter in the property value, let's stop here
+ nextPos = propValuePos;
+ pos = false;
+ } else if (nextPos === -1) {
+ // no ";"
+ if (propValuePos === -1) {
+ nextPos = line.length;
+ } else {
+ nextPos = propValuePos;
+ }
+ pos = false;
+ } else {
+ lastParam = nextPos;
+ pos = nextPos;
+ }
+
+ value = line.slice(valuePos, nextPos);
+ }
+
+ const length_before = value.length;
+ value = parse._rfc6868Escape(value);
+ valuePos += length_before - value.length;
+ if (multiValue) {
+ let delimiter = mvdelim || multiValue;
+ value = parse._parseMultiValue(value, delimiter, type, [], null, designSet);
+ } else {
+ value = parse._parseValue(value, type, designSet);
+ }
+
+ if (multiValue && (lcname in result)) {
+ if (Array.isArray(result[lcname])) {
+ result[lcname].push(value);
+ } else {
+ result[lcname] = [
+ result[lcname],
+ value
+ ];
+ }
+ } else {
+ result[lcname] = value;
+ }
+ }
+ return [result, value, valuePos];
+};
+
+/**
+ * Internal helper for rfc6868. Exposing this on ICAL.parse so that
+ * hackers can disable the rfc6868 parsing if the really need to.
+ *
+ * @function ICAL.parse._rfc6868Escape
+ * @param {String} val The value to escape
+ * @return {String} The escaped value
+ */
+parse._rfc6868Escape = function(val) {
+ return val.replace(/\^['n^]/g, function(x) {
+ return RFC6868_REPLACE_MAP$1[x];
+ });
+};
+
+/**
+ * Parse a multi value string. This function is used either for parsing
+ * actual multi-value property's values, or for handling parameter values. It
+ * can be used for both multi-value properties and structured value properties.
+ *
+ * @private
+ * @function ICAL.parse._parseMultiValue
+ * @param {String} buffer The buffer containing the full value
+ * @param {String} delim The multi-value delimiter
+ * @param {String} type The value type to be parsed
+ * @param {Array.>} result The array to append results to, varies on value type
+ * @param {String} innerMulti The inner delimiter to split each value with
+ * @param {designSet} designSet The design data for this value
+ * @return {?|Array.>} Either an array of results, or the first result
+ */
+parse._parseMultiValue = function(buffer, delim, type, result, innerMulti, designSet, structuredValue) {
+ let pos = 0;
+ let lastPos = 0;
+ let value;
+ if (delim.length === 0) {
+ return buffer;
+ }
+
+ // split each piece
+ while ((pos = unescapedIndexOf(buffer, delim, lastPos)) !== -1) {
+ value = buffer.slice(lastPos, pos);
+ if (innerMulti) {
+ value = parse._parseMultiValue(value, innerMulti, type, [], null, designSet, structuredValue);
+ } else {
+ value = parse._parseValue(value, type, designSet, structuredValue);
+ }
+ result.push(value);
+ lastPos = pos + delim.length;
+ }
+
+ // on the last piece take the rest of string
+ value = buffer.slice(lastPos);
+ if (innerMulti) {
+ value = parse._parseMultiValue(value, innerMulti, type, [], null, designSet, structuredValue);
+ } else {
+ value = parse._parseValue(value, type, designSet, structuredValue);
+ }
+ result.push(value);
+
+ return result.length == 1 ? result[0] : result;
+};
+
+/**
+ * Process a complete buffer of iCalendar/vCard data line by line, correctly
+ * unfolding content. Each line will be processed with the given callback
+ *
+ * @private
+ * @function ICAL.parse._eachLine
+ * @param {String} buffer The buffer to process
+ * @param {function(?String, String)} callback The callback for each line
+ */
+parse._eachLine = function(buffer, callback) {
+ let len = buffer.length;
+ let lastPos = buffer.search(CHAR);
+ let pos = lastPos;
+ let line;
+ let firstChar;
+
+ let newlineOffset;
+
+ do {
+ pos = buffer.indexOf('\n', lastPos) + 1;
+
+ if (pos > 1 && buffer[pos - 2] === '\r') {
+ newlineOffset = 2;
+ } else {
+ newlineOffset = 1;
+ }
+
+ if (pos === 0) {
+ pos = len;
+ newlineOffset = 0;
+ }
+
+ firstChar = buffer[lastPos];
+
+ if (firstChar === ' ' || firstChar === '\t') {
+ // add to line
+ line += buffer.slice(lastPos + 1, pos - newlineOffset);
+ } else {
+ if (line)
+ callback(null, line);
+ // push line
+ line = buffer.slice(lastPos, pos - newlineOffset);
+ }
+
+ lastPos = pos;
+ } while (pos !== len);
+
+ // extra ending line
+ line = line.trim();
+
+ if (line.length)
+ callback(null, line);
+};
+
+/* 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 */
+
+
+const OPTIONS = ["tzid", "location", "tznames", "latitude", "longitude"];
+
+/**
+ * Timezone representation.
+ *
+ * @example
+ * var vcalendar;
+ * var timezoneComp = vcalendar.getFirstSubcomponent('vtimezone');
+ * var tzid = timezoneComp.getFirstPropertyValue('tzid');
+ *
+ * var timezone = new ICAL.Timezone({
+ * component: timezoneComp,
+ * tzid
+ * });
+ *
+ * @memberof ICAL
+ */
+class Timezone {
+ static _compare_change_fn(a, b) {
+ if (a.year < b.year) return -1;
+ else if (a.year > b.year) return 1;
+
+ if (a.month < b.month) return -1;
+ else if (a.month > b.month) return 1;
+
+ if (a.day < b.day) return -1;
+ else if (a.day > b.day) return 1;
+
+ if (a.hour < b.hour) return -1;
+ else if (a.hour > b.hour) return 1;
+
+ if (a.minute < b.minute) return -1;
+ else if (a.minute > b.minute) return 1;
+
+ if (a.second < b.second) return -1;
+ else if (a.second > b.second) return 1;
+
+ return 0;
+ }
+
+ /**
+ * Convert the date/time from one zone to the next.
+ *
+ * @param {Time} tt The time to convert
+ * @param {Timezone} from_zone The source zone to convert from
+ * @param {Timezone} to_zone The target zone to convert to
+ * @return {Time} The converted date/time object
+ */
+ static convert_time(tt, from_zone, to_zone) {
+ if (tt.isDate ||
+ from_zone.tzid == to_zone.tzid ||
+ from_zone == Timezone.localTimezone ||
+ to_zone == Timezone.localTimezone) {
+ tt.zone = to_zone;
+ return tt;
+ }
+
+ let utcOffset = from_zone.utcOffset(tt);
+ tt.adjust(0, 0, 0, - utcOffset);
+
+ utcOffset = to_zone.utcOffset(tt);
+ tt.adjust(0, 0, 0, utcOffset);
+
+ return null;
+ }
+
+ /**
+ * Creates a new ICAL.Timezone instance from the passed data object.
+ *
+ * @param {Component|Object} aData options for class
+ * @param {String|Component} aData.component
+ * If aData is a simple object, then this member can be set to either a
+ * string containing the component data, or an already parsed
+ * ICAL.Component
+ * @param {String} aData.tzid The timezone identifier
+ * @param {String} aData.location The timezone locationw
+ * @param {String} aData.tznames An alternative string representation of the
+ * timezone
+ * @param {Number} aData.latitude The latitude of the timezone
+ * @param {Number} aData.longitude The longitude of the timezone
+ */
+ static fromData(aData) {
+ let tt = new Timezone();
+ return tt.fromData(aData);
+ }
+
+ /**
+ * The instance describing the UTC timezone
+ * @type {Timezone}
+ * @constant
+ * @instance
+ */
+ static #utcTimezone = null;
+ static get utcTimezone() {
+ if (!this.#utcTimezone) {
+ this.#utcTimezone = Timezone.fromData({
+ tzid: "UTC"
+ });
+ }
+ return this.#utcTimezone;
+ }
+
+ /**
+ * The instance describing the local timezone
+ * @type {Timezone}
+ * @constant
+ * @instance
+ */
+ static #localTimezone = null;
+ static get localTimezone() {
+ if (!this.#localTimezone) {
+ this.#localTimezone = Timezone.fromData({
+ tzid: "floating"
+ });
+ }
+ return this.#localTimezone;
+ }
+
+ /**
+ * Adjust a timezone change object.
+ * @private
+ * @param {Object} change The timezone change object
+ * @param {Number} days The extra amount of days
+ * @param {Number} hours The extra amount of hours
+ * @param {Number} minutes The extra amount of minutes
+ * @param {Number} seconds The extra amount of seconds
+ */
+ static adjust_change(change, days, hours, minutes, seconds) {
+ return Time.prototype.adjust.call(
+ change,
+ days,
+ hours,
+ minutes,
+ seconds,
+ change
+ );
+ }
+
+ static _minimumExpansionYear = -1;
+ static EXTRA_COVERAGE = 5;
+
+ /**
+ * Creates a new ICAL.Timezone instance, by passing in a tzid and component.
+ *
+ * @param {Component|Object} data options for class
+ * @param {String|Component} data.component
+ * If data is a simple object, then this member can be set to either a
+ * string containing the component data, or an already parsed
+ * ICAL.Component
+ * @param {String} data.tzid The timezone identifier
+ * @param {String} data.location The timezone locationw
+ * @param {String} data.tznames An alternative string representation of the
+ * timezone
+ * @param {Number} data.latitude The latitude of the timezone
+ * @param {Number} data.longitude The longitude of the timezone
+ */
+ constructor(data) {
+ this.wrappedJSObject = this;
+ this.fromData(data);
+ }
+
+
+ /**
+ * Timezone identifier
+ * @type {String}
+ */
+ tzid = "";
+
+ /**
+ * Timezone location
+ * @type {String}
+ */
+ location = "";
+
+ /**
+ * Alternative timezone name, for the string representation
+ * @type {String}
+ */
+ tznames = "";
+
+ /**
+ * The primary latitude for the timezone.
+ * @type {Number}
+ */
+ latitude = 0.0;
+
+ /**
+ * The primary longitude for the timezone.
+ * @type {Number}
+ */
+ longitude = 0.0;
+
+ /**
+ * The vtimezone component for this timezone.
+ * @type {Component}
+ */
+ component = null;
+
+ /**
+ * The year this timezone has been expanded to. All timezone transition
+ * dates until this year are known and can be used for calculation
+ *
+ * @private
+ * @type {Number}
+ */
+ expandedUntilYear = 0;
+
+ /**
+ * The class identifier.
+ * @constant
+ * @type {String}
+ * @default "icaltimezone"
+ */
+ icalclass = "icaltimezone";
+
+ /**
+ * Sets up the current instance using members from the passed data object.
+ *
+ * @param {Component|Object} aData options for class
+ * @param {String|Component} aData.component
+ * If aData is a simple object, then this member can be set to either a
+ * string containing the component data, or an already parsed
+ * ICAL.Component
+ * @param {String} aData.tzid The timezone identifier
+ * @param {String} aData.location The timezone locationw
+ * @param {String} aData.tznames An alternative string representation of the
+ * timezone
+ * @param {Number} aData.latitude The latitude of the timezone
+ * @param {Number} aData.longitude The longitude of the timezone
+ */
+ fromData(aData) {
+ this.expandedUntilYear = 0;
+ this.changes = [];
+
+ if (aData instanceof Component) {
+ // Either a component is passed directly
+ this.component = aData;
+ } else {
+ // Otherwise the component may be in the data object
+ if (aData && "component" in aData) {
+ if (typeof aData.component == "string") {
+ // If a string was passed, parse it as a component
+ let jCal = parse(aData.component);
+ this.component = new Component(jCal);
+ } else if (aData.component instanceof Component) {
+ // If it was a component already, then just set it
+ this.component = aData.component;
+ } else {
+ // Otherwise just null out the component
+ this.component = null;
+ }
+ }
+
+ // Copy remaining passed properties
+ for (let prop of OPTIONS) {
+ if (aData && prop in aData) {
+ this[prop] = aData[prop];
+ }
+ }
+ }
+
+ // If we have a component but no TZID, attempt to get it from the
+ // component's properties.
+ if (this.component instanceof Component && !this.tzid) {
+ this.tzid = this.component.getFirstPropertyValue('tzid');
+ }
+
+ return this;
+ }
+
+ /**
+ * Finds the utcOffset the given time would occur in this timezone.
+ *
+ * @param {Time} tt The time to check for
+ * @return {Number} utc offset in seconds
+ */
+ utcOffset(tt) {
+ if (this == Timezone.utcTimezone || this == Timezone.localTimezone) {
+ return 0;
+ }
+
+ this._ensureCoverage(tt.year);
+
+ if (!this.changes.length) {
+ return 0;
+ }
+
+ let tt_change = {
+ year: tt.year,
+ month: tt.month,
+ day: tt.day,
+ hour: tt.hour,
+ minute: tt.minute,
+ second: tt.second
+ };
+
+ let change_num = this._findNearbyChange(tt_change);
+ let change_num_to_use = -1;
+ let step = 1;
+
+ // TODO: replace with bin search?
+ for (;;) {
+ let change = clone(this.changes[change_num], true);
+ if (change.utcOffset < change.prevUtcOffset) {
+ Timezone.adjust_change(change, 0, 0, 0, change.utcOffset);
+ } else {
+ Timezone.adjust_change(change, 0, 0, 0,
+ change.prevUtcOffset);
+ }
+
+ let cmp = Timezone._compare_change_fn(tt_change, change);
+
+ if (cmp >= 0) {
+ change_num_to_use = change_num;
+ } else {
+ step = -1;
+ }
+
+ if (step == -1 && change_num_to_use != -1) {
+ break;
+ }
+
+ change_num += step;
+
+ if (change_num < 0) {
+ return 0;
+ }
+
+ if (change_num >= this.changes.length) {
+ break;
+ }
+ }
+
+ let zone_change = this.changes[change_num_to_use];
+ let utcOffset_change = zone_change.utcOffset - zone_change.prevUtcOffset;
+
+ if (utcOffset_change < 0 && change_num_to_use > 0) {
+ let tmp_change = clone(zone_change, true);
+ Timezone.adjust_change(tmp_change, 0, 0, 0, tmp_change.prevUtcOffset);
+
+ if (Timezone._compare_change_fn(tt_change, tmp_change) < 0) {
+ let prev_zone_change = this.changes[change_num_to_use - 1];
+
+ let want_daylight = false; // TODO
+
+ if (zone_change.is_daylight != want_daylight &&
+ prev_zone_change.is_daylight == want_daylight) {
+ zone_change = prev_zone_change;
+ }
+ }
+ }
+
+ // TODO return is_daylight?
+ return zone_change.utcOffset;
+ }
+
+ _findNearbyChange(change) {
+ // find the closest match
+ let idx = binsearchInsert(
+ this.changes,
+ change,
+ Timezone._compare_change_fn
+ );
+
+ if (idx >= this.changes.length) {
+ return this.changes.length - 1;
+ }
+
+ return idx;
+ }
+
+ _ensureCoverage(aYear) {
+ if (Timezone._minimumExpansionYear == -1) {
+ let today = Time.now();
+ Timezone._minimumExpansionYear = today.year;
+ }
+
+ let changesEndYear = aYear;
+ if (changesEndYear < Timezone._minimumExpansionYear) {
+ changesEndYear = Timezone._minimumExpansionYear;
+ }
+
+ changesEndYear += Timezone.EXTRA_COVERAGE;
+
+ if (!this.changes.length || this.expandedUntilYear < aYear) {
+ let subcomps = this.component.getAllSubcomponents();
+ let compLen = subcomps.length;
+ let compIdx = 0;
+
+ for (; compIdx < compLen; compIdx++) {
+ this._expandComponent(
+ subcomps[compIdx], changesEndYear, this.changes
+ );
+ }
+
+ this.changes.sort(Timezone._compare_change_fn);
+ this.expandedUntilYear = changesEndYear;
+ }
+ }
+
+ _expandComponent(aComponent, aYear, changes) {
+ if (!aComponent.hasProperty("dtstart") ||
+ !aComponent.hasProperty("tzoffsetto") ||
+ !aComponent.hasProperty("tzoffsetfrom")) {
+ return null;
+ }
+
+ let dtstart = aComponent.getFirstProperty("dtstart").getFirstValue();
+ let change;
+
+ function convert_tzoffset(offset) {
+ return offset.factor * (offset.hours * 3600 + offset.minutes * 60);
+ }
+
+ function init_changes() {
+ let changebase = {};
+ changebase.is_daylight = (aComponent.name == "daylight");
+ changebase.utcOffset = convert_tzoffset(
+ aComponent.getFirstProperty("tzoffsetto").getFirstValue()
+ );
+
+ changebase.prevUtcOffset = convert_tzoffset(
+ aComponent.getFirstProperty("tzoffsetfrom").getFirstValue()
+ );
+
+ return changebase;
+ }
+
+ if (!aComponent.hasProperty("rrule") && !aComponent.hasProperty("rdate")) {
+ change = init_changes();
+ change.year = dtstart.year;
+ change.month = dtstart.month;
+ change.day = dtstart.day;
+ change.hour = dtstart.hour;
+ change.minute = dtstart.minute;
+ change.second = dtstart.second;
+
+ Timezone.adjust_change(change, 0, 0, 0, -change.prevUtcOffset);
+ changes.push(change);
+ } else {
+ let props = aComponent.getAllProperties("rdate");
+ for (let rdate of props) {
+ let time = rdate.getFirstValue();
+ change = init_changes();
+
+ change.year = time.year;
+ change.month = time.month;
+ change.day = time.day;
+
+ if (time.isDate) {
+ change.hour = dtstart.hour;
+ change.minute = dtstart.minute;
+ change.second = dtstart.second;
+
+ if (dtstart.zone != Timezone.utcTimezone) {
+ Timezone.adjust_change(change, 0, 0, 0, -change.prevUtcOffset);
+ }
+ } else {
+ change.hour = time.hour;
+ change.minute = time.minute;
+ change.second = time.second;
+
+ if (time.zone != Timezone.utcTimezone) {
+ Timezone.adjust_change(change, 0, 0, 0, -change.prevUtcOffset);
+ }
+ }
+
+ changes.push(change);
+ }
+
+ let rrule = aComponent.getFirstProperty("rrule");
+
+ if (rrule) {
+ rrule = rrule.getFirstValue();
+ change = init_changes();
+
+ if (rrule.until && rrule.until.zone == Timezone.utcTimezone) {
+ rrule.until.adjust(0, 0, 0, change.prevUtcOffset);
+ rrule.until.zone = Timezone.localTimezone;
+ }
+
+ let iterator = rrule.iterator(dtstart);
+
+ let occ;
+ while ((occ = iterator.next())) {
+ change = init_changes();
+ if (occ.year > aYear || !occ) {
+ break;
+ }
+
+ change.year = occ.year;
+ change.month = occ.month;
+ change.day = occ.day;
+ change.hour = occ.hour;
+ change.minute = occ.minute;
+ change.second = occ.second;
+ change.isDate = occ.isDate;
+
+ Timezone.adjust_change(change, 0, 0, 0, -change.prevUtcOffset);
+ changes.push(change);
+ }
+ }
+ }
+
+ return changes;
+ }
+
+ /**
+ * The string representation of this timezone.
+ * @return {String}
+ */
+ toString() {
+ return (this.tznames ? this.tznames : this.tzid);
+ }
+}
+
+/* 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 */
+
+
+let zones = null;
+
+/**
+ * @classdesc
+ * Singleton class to contain timezones. Right now it is all manual registry in
+ * the future we may use this class to download timezone information or handle
+ * loading pre-expanded timezones.
+ *
+ * @exports module:ICAL.TimezoneService
+ * @memberof ICAL
+ */
+const TimezoneService = {
+ get count() {
+ if (zones === null) {
+ return 0;
+ }
+
+ return Object.keys(zones).length;
+ },
+
+ reset: function() {
+ zones = Object.create(null);
+ let utc = Timezone.utcTimezone;
+
+ zones.Z = utc;
+ zones.UTC = utc;
+ zones.GMT = utc;
+ },
+ _hard_reset: function() {
+ zones = null;
+ },
+
+ /**
+ * Checks if timezone id has been registered.
+ *
+ * @param {String} tzid Timezone identifier (e.g. America/Los_Angeles)
+ * @return {Boolean} False, when not present
+ */
+ has: function(tzid) {
+ if (zones === null) {
+ return false;
+ }
+
+ return !!zones[tzid];
+ },
+
+ /**
+ * Returns a timezone by its tzid if present.
+ *
+ * @param {String} tzid Timezone identifier (e.g. America/Los_Angeles)
+ * @return {Timezone | undefined} The timezone, or undefined if not found
+ */
+ get: function(tzid) {
+ if (zones === null) {
+ this.reset();
+ }
+
+ return zones[tzid];
+ },
+
+ /**
+ * Registers a timezone object or component.
+ *
+ * @param {Component|Timezone} timezone
+ * The initialized zone or vtimezone.
+ *
+ * @param {String=} name
+ * The name of the timezone. Defaults to the component's TZID if not
+ * passed.
+ */
+ register: function(timezone, name) {
+ if (zones === null) {
+ this.reset();
+ }
+
+ // This avoids a breaking change by the change of argument order
+ // TODO remove in v3
+ if (typeof timezone === "string" && name instanceof Timezone) {
+ [timezone, name] = [name, timezone];
+ }
+
+ if (!name) {
+ if (timezone instanceof Timezone) {
+ name = timezone.tzid;
+ } else {
+ if (timezone.name === 'vtimezone') {
+ timezone = new Timezone(timezone);
+ name = timezone.tzid;
+ }
+ }
+ }
+
+ if (!name) {
+ throw new TypeError("Neither a timezone nor a name was passed");
+ }
+
+ if (timezone instanceof Timezone) {
+ zones[name] = timezone;
+ } else {
+ throw new TypeError('timezone must be ICAL.Timezone or ICAL.Component');
+ }
+ },
+
+ /**
+ * Removes a timezone by its tzid from the list.
+ *
+ * @param {String} tzid Timezone identifier (e.g. America/Los_Angeles)
+ * @return {?Timezone} The removed timezone, or null if not registered
+ */
+ remove: function(tzid) {
+ if (zones === null) {
+ return null;
+ }
+
+ return (delete zones[tzid]);
+ }
+};
+
+/* 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 */
+
+
+/**
+ * Helper functions used in various places within ical.js
+ * @module ICAL.helpers
+ */
+
+/**
+ * Compiles a list of all referenced TZIDs in all subcomponents and
+ * removes any extra VTIMEZONE subcomponents. In addition, if any TZIDs
+ * are referenced by a component, but a VTIMEZONE does not exist,
+ * an attempt will be made to generate a VTIMEZONE using ICAL.TimezoneService.
+ *
+ * @param {Component} vcal The top-level VCALENDAR component.
+ * @return {Component} The ICAL.Component that was passed in.
+ */
+function updateTimezones(vcal) {
+ let allsubs, properties, vtimezones, reqTzid, i;
+
+ if (!vcal || vcal.name !== "vcalendar") {
+ //not a top-level vcalendar component
+ return vcal;
+ }
+
+ //Store vtimezone subcomponents in an object reference by tzid.
+ //Store properties from everything else in another array
+ allsubs = vcal.getAllSubcomponents();
+ properties = [];
+ vtimezones = {};
+ for (i = 0; i < allsubs.length; i++) {
+ if (allsubs[i].name === "vtimezone") {
+ let tzid = allsubs[i].getFirstProperty("tzid").getFirstValue();
+ vtimezones[tzid] = allsubs[i];
+ } else {
+ properties = properties.concat(allsubs[i].getAllProperties());
+ }
+ }
+
+ //create an object with one entry for each required tz
+ reqTzid = {};
+ for (i = 0; i < properties.length; i++) {
+ let tzid = properties[i].getParameter("tzid");
+ if (tzid) {
+ reqTzid[tzid] = true;
+ }
+ }
+
+ //delete any vtimezones that are not on the reqTzid list.
+ for (let [tzid, comp] of Object.entries(vtimezones)) {
+ if (!reqTzid[tzid]) {
+ vcal.removeSubcomponent(comp);
+ }
+ }
+
+ //create any missing, but registered timezones
+ for (let tzid of Object.keys(reqTzid)) {
+ if (!vtimezones[tzid] && TimezoneService.has(tzid)) {
+ vcal.addSubcomponent(TimezoneService.get(tzid).component);
+ }
+ }
+
+ return vcal;
+}
+
+/**
+ * Checks if the given type is of the number type and also NaN.
+ *
+ * @param {Number} number The number to check
+ * @return {Boolean} True, if the number is strictly NaN
+ */
+function isStrictlyNaN(number) {
+ return typeof(number) === 'number' && isNaN(number);
+}
+
+/**
+ * Parses a string value that is expected to be an integer, when the valid is
+ * not an integer throws a decoration error.
+ *
+ * @param {String} string Raw string input
+ * @return {Number} Parsed integer
+ */
+function strictParseInt(string) {
+ let result = parseInt(string, 10);
+
+ if (isStrictlyNaN(result)) {
+ throw new Error(
+ 'Could not extract integer from "' + string + '"'
+ );
+ }
+
+ return result;
+}
+
+/**
+ * Creates or returns a class instance of a given type with the initialization
+ * data if the data is not already an instance of the given type.
+ *
+ * @example
+ * var time = new ICAL.Time(...);
+ * var result = ICAL.helpers.formatClassType(time, ICAL.Time);
+ *
+ * (result instanceof ICAL.Time)
+ * // => true
+ *
+ * result = ICAL.helpers.formatClassType({}, ICAL.Time);
+ * (result isntanceof ICAL.Time)
+ * // => true
+ *
+ *
+ * @param {Object} data object initialization data
+ * @param {Object} type object type (like ICAL.Time)
+ * @return {?} An instance of the found type.
+ */
+function formatClassType(data, type) {
+ if (typeof(data) === 'undefined') {
+ return undefined;
+ }
+
+ if (data instanceof type) {
+ return data;
+ }
+ return new type(data);
+}
+
+/**
+ * Identical to indexOf but will only match values when they are not preceded
+ * by a backslash character.
+ *
+ * @param {String} buffer String to search
+ * @param {String} search Value to look for
+ * @param {Number} pos Start position
+ * @return {Number} The position, or -1 if not found
+ */
+function unescapedIndexOf(buffer, search, pos) {
+ while ((pos = buffer.indexOf(search, pos)) !== -1) {
+ if (pos > 0 && buffer[pos - 1] === '\\') {
+ pos += 1;
+ } else {
+ return pos;
+ }
+ }
+ return -1;
+}
+
+/**
+ * Find the index for insertion using binary search.
+ *
+ * @param {Array} list The list to search
+ * @param {?} seekVal The value to insert
+ * @param {function(?,?)} cmpfunc The comparison func, that can
+ * compare two seekVals
+ * @return {Number} The insert position
+ */
+function binsearchInsert(list, seekVal, cmpfunc) {
+ if (!list.length)
+ return 0;
+
+ let low = 0, high = list.length - 1,
+ mid, cmpval;
+
+ while (low <= high) {
+ mid = low + Math.floor((high - low) / 2);
+ cmpval = cmpfunc(seekVal, list[mid]);
+
+ if (cmpval < 0)
+ high = mid - 1;
+ else if (cmpval > 0)
+ low = mid + 1;
+ else
+ break;
+ }
+
+ if (cmpval < 0)
+ return mid; // insertion is displacing, so use mid outright.
+ else if (cmpval > 0)
+ return mid + 1;
+ else
+ return mid;
+}
+
+/**
+ * Clone the passed object or primitive. By default a shallow clone will be
+ * executed.
+ *
+ * @param {*} aSrc The thing to clone
+ * @param {Boolean=} aDeep If true, a deep clone will be performed
+ * @return {*} The copy of the thing
+ */
+function clone(aSrc, aDeep) {
+ if (!aSrc || typeof aSrc != "object") {
+ return aSrc;
+ } else if (aSrc instanceof Date) {
+ return new Date(aSrc.getTime());
+ } else if ("clone" in aSrc) {
+ return aSrc.clone();
+ } else if (Array.isArray(aSrc)) {
+ let arr = [];
+ for (let i = 0; i < aSrc.length; i++) {
+ arr.push(aDeep ? clone(aSrc[i], true) : aSrc[i]);
+ }
+ return arr;
+ } else {
+ let obj = {};
+ for (let [name, value] of Object.entries(aSrc)) {
+ if (aDeep) {
+ obj[name] = clone(value, true);
+ } else {
+ obj[name] = value;
+ }
+ }
+ return obj;
+ }
+}
+
+/**
+ * Performs iCalendar line folding. A line ending character is inserted and
+ * the next line begins with a whitespace.
+ *
+ * @example
+ * SUMMARY:This line will be fold
+ * ed right in the middle of a word.
+ *
+ * @param {String} aLine The line to fold
+ * @return {String} The folded line
+ */
+function foldline(aLine) {
+ let result = "";
+ let line = aLine || "", pos = 0, line_length = 0;
+ //pos counts position in line for the UTF-16 presentation
+ //line_length counts the bytes for the UTF-8 presentation
+ while (line.length) {
+ let cp = line.codePointAt(pos);
+ if (cp < 128) ++line_length;
+ else if (cp < 2048) line_length += 2;//needs 2 UTF-8 bytes
+ else if (cp < 65536) line_length += 3;
+ else line_length += 4; //cp is less than 1114112
+ if (line_length < ICALmodule.foldLength + 1)
+ pos += cp > 65535 ? 2 : 1;
+ else {
+ result += ICALmodule.newLineChar + " " + line.slice(0, Math.max(0, pos));
+ line = line.slice(Math.max(0, pos));
+ pos = line_length = 0;
+ }
+ }
+ return result.slice(ICALmodule.newLineChar.length + 1);
+}
+
+/**
+ * Pads the given string or number with zeros so it will have at least two
+ * characters.
+ *
+ * @param {String|Number} data The string or number to pad
+ * @return {String} The number padded as a string
+ */
+function pad2(data) {
+ if (typeof(data) !== 'string') {
+ // handle fractions.
+ if (typeof(data) === 'number') {
+ data = parseInt(data);
+ }
+ data = String(data);
+ }
+
+ let len = data.length;
+
+ switch (len) {
+ case 0:
+ return '00';
+ case 1:
+ return '0' + data;
+ default:
+ return data;
+ }
+}
+
+/**
+ * Truncates the given number, correctly handling negative numbers.
+ *
+ * @param {Number} number The number to truncate
+ * @return {Number} The truncated number
+ */
+function trunc(number) {
+ return (number < 0 ? Math.ceil(number) : Math.floor(number));
+}
+
+/**
+ * Poor-man's cross-browser object extension. Doesn't support all the
+ * features, but enough for our usage. Note that the target's properties are
+ * not overwritten with the source properties.
+ *
+ * @example
+ * var child = ICAL.helpers.extend(parent, {
+ * "bar": 123
+ * });
+ *
+ * @param {Object} source The object to extend
+ * @param {Object} target The object to extend with
+ * @return {Object} Returns the target.
+ */
+function extend(source, target) {
+ for (let key in source) {
+ let descr = Object.getOwnPropertyDescriptor(source, key);
+ if (descr && !Object.getOwnPropertyDescriptor(target, key)) {
+ Object.defineProperty(target, key, descr);
+ }
+ }
+ return target;
+}
+
+var helpers = /*#__PURE__*/Object.freeze({
+ __proto__: null,
+ binsearchInsert: binsearchInsert,
+ clone: clone,
+ extend: extend,
+ foldline: foldline,
+ formatClassType: formatClassType,
+ isStrictlyNaN: isStrictlyNaN,
+ pad2: pad2,
+ strictParseInt: strictParseInt,
+ trunc: trunc,
+ unescapedIndexOf: unescapedIndexOf,
+ updateTimezones: updateTimezones
+});
+
+/* 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 */
+
+
+/**
+ * This class represents the "utc-offset" value type, with various calculation and manipulation
+ * methods.
+ *
+ * @memberof ICAL
+ */
+class UtcOffset {
+ /**
+ * Creates a new {@link ICAL.UtcOffset} instance from the passed string.
+ *
+ * @param {String} aString The string to parse
+ * @return {Duration} The created utc-offset instance
+ */
+ static fromString(aString) {
+ // -05:00
+ let options = {};
+ //TODO: support seconds per rfc5545 ?
+ options.factor = (aString[0] === '+') ? 1 : -1;
+ options.hours = strictParseInt(aString.slice(1, 3));
+ options.minutes = strictParseInt(aString.slice(4, 6));
+
+ return new UtcOffset(options);
+ }
+
+ /**
+ * Creates a new {@link ICAL.UtcOffset} instance from the passed seconds
+ * value.
+ *
+ * @param {Number} aSeconds The number of seconds to convert
+ */
+ static fromSeconds(aSeconds) {
+ let instance = new UtcOffset();
+ instance.fromSeconds(aSeconds);
+ return instance;
+ }
+
+ /**
+ * Creates a new ICAL.UtcOffset instance.
+ *
+ * @param {Object} aData An object with members of the utc offset
+ * @param {Number=} aData.hours The hours for the utc offset
+ * @param {Number=} aData.minutes The minutes in the utc offset
+ * @param {Number=} aData.factor The factor for the utc-offset, either -1 or 1
+ */
+ constructor(aData) {
+ this.fromData(aData);
+ }
+
+ /**
+ * The hours in the utc-offset
+ * @type {Number}
+ */
+ hours = 0;
+
+ /**
+ * The minutes in the utc-offset
+ * @type {Number}
+ */
+ minutes = 0;
+
+ /**
+ * The sign of the utc offset, 1 for positive offset, -1 for negative
+ * offsets.
+ * @type {Number}
+ */
+ factor = 1;
+
+ /**
+ * The type name, to be used in the jCal object.
+ * @constant
+ * @type {String}
+ * @default "utc-offset"
+ */
+ icaltype = "utc-offset";
+
+ /**
+ * Returns a clone of the utc offset object.
+ *
+ * @return {UtcOffset} The cloned object
+ */
+ clone() {
+ return UtcOffset.fromSeconds(this.toSeconds());
+ }
+
+ /**
+ * Sets up the current instance using members from the passed data object.
+ *
+ * @param {Object} aData An object with members of the utc offset
+ * @param {Number=} aData.hours The hours for the utc offset
+ * @param {Number=} aData.minutes The minutes in the utc offset
+ * @param {Number=} aData.factor The factor for the utc-offset, either -1 or 1
+ */
+ fromData(aData) {
+ if (aData) {
+ for (let [key, value] of Object.entries(aData)) {
+ this[key] = value;
+ }
+ }
+ this._normalize();
+ }
+
+ /**
+ * Sets up the current instance from the given seconds value. The seconds
+ * value is truncated to the minute. Offsets are wrapped when the world
+ * ends, the hour after UTC+14:00 is UTC-12:00.
+ *
+ * @param {Number} aSeconds The seconds to convert into an offset
+ */
+ fromSeconds(aSeconds) {
+ let secs = Math.abs(aSeconds);
+
+ this.factor = aSeconds < 0 ? -1 : 1;
+ this.hours = trunc(secs / 3600);
+
+ secs -= (this.hours * 3600);
+ this.minutes = trunc(secs / 60);
+ return this;
+ }
+
+ /**
+ * Convert the current offset to a value in seconds
+ *
+ * @return {Number} The offset in seconds
+ */
+ toSeconds() {
+ return this.factor * (60 * this.minutes + 3600 * this.hours);
+ }
+
+ /**
+ * Compare this utc offset with another one.
+ *
+ * @param {UtcOffset} other The other offset to compare with
+ * @return {Number} -1, 0 or 1 for less/equal/greater
+ */
+ compare(other) {
+ let a = this.toSeconds();
+ let b = other.toSeconds();
+ return (a > b) - (b > a);
+ }
+
+ _normalize() {
+ // Range: 97200 seconds (with 1 hour inbetween)
+ let secs = this.toSeconds();
+ let factor = this.factor;
+ while (secs < -43200) { // = UTC-12:00
+ secs += 97200;
+ }
+ while (secs > 50400) { // = UTC+14:00
+ secs -= 97200;
+ }
+
+ this.fromSeconds(secs);
+
+ // Avoid changing the factor when on zero seconds
+ if (secs == 0) {
+ this.factor = factor;
+ }
+ }
+
+ /**
+ * The iCalendar string representation of this utc-offset.
+ * @return {String}
+ */
+ toICALString() {
+ return design.icalendar.value['utc-offset'].toICAL(this.toString());
+ }
+
+ /**
+ * The string representation of this utc-offset.
+ * @return {String}
+ */
+ toString() {
+ return (this.factor == 1 ? "+" : "-") + pad2(this.hours) + ':' + pad2(this.minutes);
+ }
+}
+
+/* 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 */
+
+
+/**
+ * Describes a vCard time, which has slight differences to the ICAL.Time.
+ * Properties can be null if not specified, for example for dates with
+ * reduced accuracy or truncation.
+ *
+ * Note that currently not all methods are correctly re-implemented for
+ * VCardTime. For example, comparison will have undefined results when some
+ * members are null.
+ *
+ * Also, normalization is not yet implemented for this class!
+ *
+ * @memberof ICAL
+ * @extends {Time}
+ */
+class VCardTime extends Time {
+ /**
+ * Returns a new ICAL.VCardTime instance from a date and/or time string.
+ *
+ * @param {String} aValue The string to create from
+ * @param {String} aIcalType The type for this instance, e.g. date-and-or-time
+ * @return {VCardTime} The date/time instance
+ */
+ static fromDateAndOrTimeString(aValue, aIcalType) {
+ function part(v, s, e) {
+ return v ? strictParseInt(v.slice(s, s + e)) : null;
+ }
+ let parts = aValue.split('T');
+ let dt = parts[0], tmz = parts[1];
+ let splitzone = tmz ? design.vcard.value.time._splitZone(tmz) : [];
+ let zone = splitzone[0], tm = splitzone[1];
+
+ let dtlen = dt ? dt.length : 0;
+ let tmlen = tm ? tm.length : 0;
+
+ let hasDashDate = dt && dt[0] == '-' && dt[1] == '-';
+ let hasDashTime = tm && tm[0] == '-';
+
+ let o = {
+ year: hasDashDate ? null : part(dt, 0, 4),
+ month: hasDashDate && (dtlen == 4 || dtlen == 7) ? part(dt, 2, 2) : dtlen == 7 ? part(dt, 5, 2) : dtlen == 10 ? part(dt, 5, 2) : null,
+ day: dtlen == 5 ? part(dt, 3, 2) : dtlen == 7 && hasDashDate ? part(dt, 5, 2) : dtlen == 10 ? part(dt, 8, 2) : null,
+
+ hour: hasDashTime ? null : part(tm, 0, 2),
+ minute: hasDashTime && tmlen == 3 ? part(tm, 1, 2) : tmlen > 4 ? hasDashTime ? part(tm, 1, 2) : part(tm, 3, 2) : null,
+ second: tmlen == 4 ? part(tm, 2, 2) : tmlen == 6 ? part(tm, 4, 2) : tmlen == 8 ? part(tm, 6, 2) : null
+ };
+
+ if (zone == 'Z') {
+ zone = Timezone.utcTimezone;
+ } else if (zone && zone[3] == ':') {
+ zone = UtcOffset.fromString(zone);
+ } else {
+ zone = null;
+ }
+
+ return new VCardTime(o, zone, aIcalType);
+ }
+
+
+ /**
+ * Creates a new ICAL.VCardTime instance.
+ *
+ * @param {Object} data The data for the time instance
+ * @param {Number=} data.year The year for this date
+ * @param {Number=} data.month The month for this date
+ * @param {Number=} data.day The day for this date
+ * @param {Number=} data.hour The hour for this date
+ * @param {Number=} data.minute The minute for this date
+ * @param {Number=} data.second The second for this date
+ * @param {Timezone|UtcOffset} zone The timezone to use
+ * @param {String} icaltype The type for this date/time object
+ */
+ constructor(data, zone, icaltype) {
+ super(data, zone);
+ this.icaltype = icaltype || "date-and-or-time";
+ }
+
+ /**
+ * The class identifier.
+ * @constant
+ * @type {String}
+ * @default "vcardtime"
+ */
+ icalclass = "vcardtime";
+
+ /**
+ * The type name, to be used in the jCal object.
+ * @type {String}
+ * @default "date-and-or-time"
+ */
+ icaltype = "date-and-or-time";
+
+ /**
+ * Returns a clone of the vcard date/time object.
+ *
+ * @return {VCardTime} The cloned object
+ */
+ clone() {
+ return new VCardTime(this._time, this.zone, this.icaltype);
+ }
+
+ _normalize() {
+ return this;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ utcOffset() {
+ if (this.zone instanceof UtcOffset) {
+ return this.zone.toSeconds();
+ } else {
+ return Time.prototype.utcOffset.apply(this, arguments);
+ }
+ }
+
+ /**
+ * Returns an RFC 6350 compliant representation of this object.
+ *
+ * @return {String} vcard date/time string
+ */
+ toICALString() {
+ return design.vcard.value[this.icaltype].toICAL(this.toString());
+ }
+
+ /**
+ * The string representation of this date/time, in jCard form
+ * (including : and - separators).
+ * @return {String}
+ */
+ toString() {
+ let y = this.year, m = this.month, d = this.day;
+ let h = this.hour, mm = this.minute, s = this.second;
+
+ let hasYear = y !== null, hasMonth = m !== null, hasDay = d !== null;
+ let hasHour = h !== null, hasMinute = mm !== null, hasSecond = s !== null;
+
+ let datepart = (hasYear ? pad2(y) + (hasMonth || hasDay ? '-' : '') : (hasMonth || hasDay ? '--' : '')) +
+ (hasMonth ? pad2(m) : '') +
+ (hasDay ? '-' + pad2(d) : '');
+ let timepart = (hasHour ? pad2(h) : '-') + (hasHour && hasMinute ? ':' : '') +
+ (hasMinute ? pad2(mm) : '') + (!hasHour && !hasMinute ? '-' : '') +
+ (hasMinute && hasSecond ? ':' : '') +
+ (hasSecond ? pad2(s) : '');
+
+ let zone;
+ if (this.zone === Timezone.utcTimezone) {
+ zone = 'Z';
+ } else if (this.zone instanceof UtcOffset) {
+ zone = this.zone.toString();
+ } else if (this.zone === Timezone.localTimezone) {
+ zone = '';
+ } else if (this.zone instanceof Timezone) {
+ let offset = UtcOffset.fromSeconds(this.zone.utcOffset(this));
+ zone = offset.toString();
+ } else {
+ zone = '';
+ }
+
+ switch (this.icaltype) {
+ case "time":
+ return timepart + zone;
+ case "date-and-or-time":
+ case "date-time":
+ return datepart + (timepart == '--' ? '' : 'T' + timepart + zone);
+ case "date":
+ return datepart;
+ }
+ return null;
+ }
+}
+
+/* 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 */
+
+
+/**
+ * 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").weekDay} weekDay
+ * Imports the 'weekDay' type from the "types.js" module
+ */
+
+/**
+ * An iterator for a single recurrence rule. This class usually doesn't have to be instanciated
+ * directly, the convenience method {@link ICAL.Recur#iterator} can be used.
+ *
+ * @memberof ICAL
+ */
+class RecurIterator {
+ static _indexMap = {
+ "BYSECOND": 0,
+ "BYMINUTE": 1,
+ "BYHOUR": 2,
+ "BYDAY": 3,
+ "BYMONTHDAY": 4,
+ "BYYEARDAY": 5,
+ "BYWEEKNO": 6,
+ "BYMONTH": 7,
+ "BYSETPOS": 8
+ };
+
+ static _expandMap = {
+ "SECONDLY": [1, 1, 1, 1, 1, 1, 1, 1],
+ "MINUTELY": [2, 1, 1, 1, 1, 1, 1, 1],
+ "HOURLY": [2, 2, 1, 1, 1, 1, 1, 1],
+ "DAILY": [2, 2, 2, 1, 1, 1, 1, 1],
+ "WEEKLY": [2, 2, 2, 2, 3, 3, 1, 1],
+ "MONTHLY": [2, 2, 2, 2, 2, 3, 3, 1],
+ "YEARLY": [2, 2, 2, 2, 2, 2, 2, 2]
+ };
+
+ static UNKNOWN = 0;
+ static CONTRACT = 1;
+ static EXPAND = 2;
+ static ILLEGAL = 3;
+
+ /**
+ * Creates a new ICAL.RecurIterator instance. The options object may contain additional members
+ * when resuming iteration from a previous run.
+ *
+ * @param {Object} options The iterator options
+ * @param {Recur} options.rule The rule to iterate.
+ * @param {Time} options.dtstart The start date of the event.
+ * @param {Boolean=} options.initialized When true, assume that options are
+ * from a previously constructed iterator. Initialization will not be
+ * repeated.
+ */
+ constructor(options) {
+ this.fromData(options);
+ }
+
+ /**
+ * True when iteration is finished.
+ * @type {Boolean}
+ */
+ completed = false;
+
+ /**
+ * The rule that is being iterated
+ * @type {Recur}
+ */
+ rule = null;
+
+ /**
+ * The start date of the event being iterated.
+ * @type {Time}
+ */
+ dtstart = null;
+
+ /**
+ * The last occurrence that was returned from the
+ * {@link RecurIterator#next} method.
+ * @type {Time}
+ */
+ last = null;
+
+ /**
+ * The sequence number from the occurrence
+ * @type {Number}
+ */
+ occurrence_number = 0;
+
+ /**
+ * The indices used for the {@link ICAL.RecurIterator#by_data} object.
+ * @type {Object}
+ * @private
+ */
+ by_indices = null;
+
+ /**
+ * If true, the iterator has already been initialized
+ * @type {Boolean}
+ * @private
+ */
+ initialized = false;
+
+ /**
+ * The initializd by-data.
+ * @type {Object}
+ * @private
+ */
+ by_data = null;
+
+ /**
+ * The expanded yeardays
+ * @type {Array}
+ * @private
+ */
+ days = null;
+
+ /**
+ * The index in the {@link ICAL.RecurIterator#days} array.
+ * @type {Number}
+ * @private
+ */
+ days_index = 0;
+
+ /**
+ * Initialize the recurrence iterator from the passed data object. This
+ * method is usually not called directly, you can initialize the iterator
+ * through the constructor.
+ *
+ * @param {Object} options The iterator options
+ * @param {Recur} options.rule The rule to iterate.
+ * @param {Time} options.dtstart The start date of the event.
+ * @param {Boolean=} options.initialized When true, assume that options are
+ * from a previously constructed iterator. Initialization will not be
+ * repeated.
+ */
+ fromData(options) {
+ this.rule = formatClassType(options.rule, Recur);
+
+ if (!this.rule) {
+ throw new Error('iterator requires a (ICAL.Recur) rule');
+ }
+
+ this.dtstart = formatClassType(options.dtstart, Time);
+
+ if (!this.dtstart) {
+ throw new Error('iterator requires a (ICAL.Time) dtstart');
+ }
+
+ if (options.by_data) {
+ this.by_data = options.by_data;
+ } else {
+ this.by_data = clone(this.rule.parts, true);
+ }
+
+ if (options.occurrence_number)
+ this.occurrence_number = options.occurrence_number;
+
+ this.days = options.days || [];
+ if (options.last) {
+ this.last = formatClassType(options.last, Time);
+ }
+
+ this.by_indices = options.by_indices;
+
+ if (!this.by_indices) {
+ this.by_indices = {
+ "BYSECOND": 0,
+ "BYMINUTE": 0,
+ "BYHOUR": 0,
+ "BYDAY": 0,
+ "BYMONTH": 0,
+ "BYWEEKNO": 0,
+ "BYMONTHDAY": 0
+ };
+ }
+
+ this.initialized = options.initialized || false;
+
+ if (!this.initialized) {
+ try {
+ this.init();
+ } catch (e) {
+ if (e instanceof InvalidRecurrenceRuleError) {
+ // Init may error if there are no possible recurrence instances from
+ // the rule, but we don't want to bubble this error up. Instead, we
+ // create an empty iterator.
+ this.completed = true;
+ } else {
+ // Propagate other errors to consumers.
+ throw e;
+ }
+ }
+ }
+ }
+
+ /**
+ * Initialize the iterator
+ * @private
+ */
+ init() {
+ this.initialized = true;
+ this.last = this.dtstart.clone();
+ let parts = this.by_data;
+
+ if ("BYDAY" in parts) {
+ // libical does this earlier when the rule is loaded, but we postpone to
+ // now so we can preserve the original order.
+ this.sort_byday_rules(parts.BYDAY);
+ }
+
+ // The BYYEARDAY may only appear with BYDAY
+ if ("BYYEARDAY" in parts) {
+ if ("BYMONTH" in parts || "BYWEEKNO" in parts ||
+ "BYMONTHDAY" in parts) {
+ throw new Error("Invalid BYYEARDAY rule");
+ }
+ }
+
+ // BYWEEKNO and BYMONTHDAY rule parts may not both appear
+ if ("BYWEEKNO" in parts && "BYMONTHDAY" in parts) {
+ throw new Error("BYWEEKNO does not fit to BYMONTHDAY");
+ }
+
+ // For MONTHLY recurrences (FREQ=MONTHLY) neither BYYEARDAY nor
+ // BYWEEKNO may appear.
+ if (this.rule.freq == "MONTHLY" &&
+ ("BYYEARDAY" in parts || "BYWEEKNO" in parts)) {
+ throw new Error("For MONTHLY recurrences neither BYYEARDAY nor BYWEEKNO may appear");
+ }
+
+ // For WEEKLY recurrences (FREQ=WEEKLY) neither BYMONTHDAY nor
+ // BYYEARDAY may appear.
+ if (this.rule.freq == "WEEKLY" &&
+ ("BYYEARDAY" in parts || "BYMONTHDAY" in parts)) {
+ throw new Error("For WEEKLY recurrences neither BYMONTHDAY nor BYYEARDAY may appear");
+ }
+
+ // BYYEARDAY may only appear in YEARLY rules
+ if (this.rule.freq != "YEARLY" && "BYYEARDAY" in parts) {
+ throw new Error("BYYEARDAY may only appear in YEARLY rules");
+ }
+
+ this.last.second = this.setup_defaults("BYSECOND", "SECONDLY", this.dtstart.second);
+ this.last.minute = this.setup_defaults("BYMINUTE", "MINUTELY", this.dtstart.minute);
+ this.last.hour = this.setup_defaults("BYHOUR", "HOURLY", this.dtstart.hour);
+ this.last.day = this.setup_defaults("BYMONTHDAY", "DAILY", this.dtstart.day);
+ this.last.month = this.setup_defaults("BYMONTH", "MONTHLY", this.dtstart.month);
+
+ if (this.rule.freq == "WEEKLY") {
+ if ("BYDAY" in parts) {
+ let [, dow] = this.ruleDayOfWeek(parts.BYDAY[0], this.rule.wkst);
+ let wkdy = dow - this.last.dayOfWeek(this.rule.wkst);
+ if ((this.last.dayOfWeek(this.rule.wkst) < dow && wkdy >= 0) || wkdy < 0) {
+ // Initial time is after first day of BYDAY data
+ this.last.day += wkdy;
+ }
+ } else {
+ let dayName = Recur.numericDayToIcalDay(this.dtstart.dayOfWeek());
+ parts.BYDAY = [dayName];
+ }
+ }
+
+ if (this.rule.freq == "YEARLY") {
+ // Some yearly recurrence rules may be specific enough to not actually
+ // occur on a yearly basis, e.g. the 29th day of February or the fifth
+ // Monday of a given month. The standard isn't clear on the intended
+ // behavior in these cases, but `libical` at least will iterate until it
+ // finds a matching year.
+ // CAREFUL: Some rules may specify an occurrence that can never happen,
+ // e.g. the first Monday of April so long as it falls on the 15th
+ // through the 21st. Detecting these is non-trivial, so ensure that we
+ // stop iterating at some point.
+ const untilYear = this.rule.until ? this.rule.until.year : 20000;
+ while (this.last.year <= untilYear) {
+ this.expand_year_days(this.last.year);
+ if (this.days.length > 0) {
+ break;
+ }
+ this.increment_year(this.rule.interval);
+ }
+
+ if (this.days.length == 0) {
+ throw new InvalidRecurrenceRuleError();
+ }
+
+ // If there's no occurrence in this year, try the following years. This
+ // would only happen looking for day 366 or -366.
+ if (!this._nextByYearDay() && !this.next_year() && !this.next_year() && !this.next_year()) {
+ // This should not be possible, but just in case it is, stop.
+ throw new InvalidRecurrenceRuleError();
+ }
+ }
+
+ if (this.rule.freq == "MONTHLY") {
+ if (this.has_by_data("BYDAY")) {
+ let tempLast = null;
+ let initLast = this.last.clone();
+ let daysInMonth = Time.daysInMonth(this.last.month, this.last.year);
+
+ // Check every weekday in BYDAY with relative dow and pos.
+ for (let bydow of this.by_data.BYDAY) {
+ this.last = initLast.clone();
+ let [pos, dow] = this.ruleDayOfWeek(bydow);
+ let dayOfMonth = this.last.nthWeekDay(dow, pos);
+
+ // If |pos| >= 6, the byday is invalid for a monthly rule.
+ if (pos >= 6 || pos <= -6) {
+ throw new Error("Malformed values in BYDAY part");
+ }
+
+ // If a Byday with pos=+/-5 is not in the current month it
+ // must be searched in the next months.
+ if (dayOfMonth > daysInMonth || dayOfMonth <= 0) {
+ // Skip if we have already found a "last" in this month.
+ if (tempLast && tempLast.month == initLast.month) {
+ continue;
+ }
+ while (dayOfMonth > daysInMonth || dayOfMonth <= 0) {
+ this.increment_month();
+ daysInMonth = Time.daysInMonth(this.last.month, this.last.year);
+ dayOfMonth = this.last.nthWeekDay(dow, pos);
+ }
+ }
+
+ this.last.day = dayOfMonth;
+ if (!tempLast || this.last.compare(tempLast) < 0) {
+ tempLast = this.last.clone();
+ }
+ }
+ this.last = tempLast.clone();
+
+ //XXX: This feels like a hack, but we need to initialize
+ // the BYMONTHDAY case correctly and byDayAndMonthDay handles
+ // this case. It accepts a special flag which will avoid incrementing
+ // the initial value without the flag days that match the start time
+ // would be missed.
+ if (this.has_by_data('BYMONTHDAY')) {
+ this._byDayAndMonthDay(true);
+ }
+
+ if (this.last.day > daysInMonth || this.last.day == 0) {
+ throw new Error("Malformed values in BYDAY part");
+ }
+ } else if (this.has_by_data("BYMONTHDAY")) {
+ // Change the day value so that normalisation won't change the month.
+ this.last.day = 1;
+
+ // Get a sorted list of days in the starting month that match the rule.
+ let normalized = this.normalizeByMonthDayRules(
+ this.last.year,
+ this.last.month,
+ this.rule.parts.BYMONTHDAY
+ ).filter(d => d >= this.last.day);
+
+ if (normalized.length) {
+ // There's at least one valid day, use it.
+ this.last.day = normalized[0];
+ this.by_data.BYMONTHDAY = normalized;
+ } else {
+ // There's no occurrence in this month, find the next valid month.
+ // The longest possible sequence of skipped months is February-April-June,
+ // so we might need to call next_month up to three times.
+ if (!this.next_month() && !this.next_month() && !this.next_month()) {
+ throw new InvalidRecurrenceRuleError();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Retrieve the next occurrence from the iterator.
+ * @return {Time}
+ */
+ next(again = false) {
+ let before = (this.last ? this.last.clone() : null);
+
+ if ((this.rule.count && this.occurrence_number >= this.rule.count) ||
+ (this.rule.until && this.last.compare(this.rule.until) > 0)) {
+ this.completed = true;
+ }
+
+ if (this.completed) {
+ return null;
+ }
+
+ if (this.occurrence_number == 0 && this.last.compare(this.dtstart) >= 0) {
+ // First of all, give the instance that was initialized
+ this.occurrence_number++;
+ return this.last;
+ }
+
+ let valid;
+ let invalid_count = 0;
+ do {
+ valid = 1;
+
+ switch (this.rule.freq) {
+ case "SECONDLY":
+ this.next_second();
+ break;
+ case "MINUTELY":
+ this.next_minute();
+ break;
+ case "HOURLY":
+ this.next_hour();
+ break;
+ case "DAILY":
+ this.next_day();
+ break;
+ case "WEEKLY":
+ this.next_week();
+ break;
+ case "MONTHLY":
+ valid = this.next_month();
+ if (valid) {
+ invalid_count = 0;
+ } else if (++invalid_count == 336) {
+ // We've been through all 91 month variations and not found a recurrence. Stop.
+ // (12 months and 29-day February × 7 starting days.)
+ this.completed = true;
+ return null;
+ }
+ break;
+ case "YEARLY":
+ valid = this.next_year();
+ if (valid) {
+ invalid_count = 0;
+ } else if (++invalid_count == 28) {
+ // We've been through all 14 year variations and not found a recurrence. Stop.
+ // (365-day and 366-day years × 7 starting days.)
+ this.completed = true;
+ return null;
+ }
+ break;
+
+ default:
+ return null;
+ }
+ } while (!this.check_contracting_rules() ||
+ this.last.compare(this.dtstart) < 0 ||
+ !valid);
+
+ if (this.last.compare(before) == 0) {
+ if (again) {
+ throw new Error("Same occurrence found twice, protecting you from death by recursion");
+ }
+ this.next(true);
+ }
+
+ if (this.rule.until && this.last.compare(this.rule.until) > 0) {
+ this.completed = true;
+ return null;
+ } else {
+ this.occurrence_number++;
+ return this.last;
+ }
+ }
+
+ next_second() {
+ return this.next_generic("BYSECOND", "SECONDLY", "second", "minute");
+ }
+
+ increment_second(inc) {
+ return this.increment_generic(inc, "second", 60, "minute");
+ }
+
+ next_minute() {
+ return this.next_generic("BYMINUTE", "MINUTELY",
+ "minute", "hour", "next_second");
+ }
+
+ increment_minute(inc) {
+ return this.increment_generic(inc, "minute", 60, "hour");
+ }
+
+ next_hour() {
+ return this.next_generic("BYHOUR", "HOURLY", "hour",
+ "monthday", "next_minute");
+ }
+
+ increment_hour(inc) {
+ this.increment_generic(inc, "hour", 24, "monthday");
+ }
+
+ next_day() {
+ let this_freq = (this.rule.freq == "DAILY");
+
+ if (this.next_hour() == 0) {
+ return 0;
+ }
+
+ if (this_freq) {
+ this.increment_monthday(this.rule.interval);
+ } else {
+ this.increment_monthday(1);
+ }
+
+ return 0;
+ }
+
+ next_week() {
+ let end_of_data = 0;
+
+ if (this.next_weekday_by_week() == 0) {
+ return end_of_data;
+ }
+
+ if (this.has_by_data("BYWEEKNO")) {
+ this.by_indices.BYWEEKNO++;
+
+ if (this.by_indices.BYWEEKNO == this.by_data.BYWEEKNO.length) {
+ this.by_indices.BYWEEKNO = 0;
+ end_of_data = 1;
+ }
+
+ // HACK should be first month of the year
+ this.last.month = 1;
+ this.last.day = 1;
+
+ let week_no = this.by_data.BYWEEKNO[this.by_indices.BYWEEKNO];
+
+ this.last.day += 7 * week_no;
+
+ if (end_of_data) {
+ this.increment_year(1);
+ }
+ } else {
+ // Jump to the next week
+ this.increment_monthday(7 * this.rule.interval);
+ }
+
+ return end_of_data;
+ }
+
+ /**
+ * Normalize each by day rule for a given year/month.
+ * Takes into account ordering and negative rules
+ *
+ * @private
+ * @param {Number} year Current year.
+ * @param {Number} month Current month.
+ * @param {Array} rules Array of rules.
+ *
+ * @return {Array} sorted and normalized rules.
+ * Negative rules will be expanded to their
+ * correct positive values for easier processing.
+ */
+ normalizeByMonthDayRules(year, month, rules) {
+ let daysInMonth = Time.daysInMonth(month, year);
+
+ // XXX: This is probably bad for performance to allocate
+ // a new array for each month we scan, if possible
+ // we should try to optimize this...
+ let newRules = [];
+
+ let ruleIdx = 0;
+ let len = rules.length;
+ let rule;
+
+ for (; ruleIdx < len; ruleIdx++) {
+ rule = parseInt(rules[ruleIdx], 10);
+ if (isNaN(rule)) {
+ throw new Error('Invalid BYMONTHDAY value');
+ }
+
+ // if this rule falls outside of given
+ // month discard it.
+ if (Math.abs(rule) > daysInMonth) {
+ continue;
+ }
+
+ // negative case
+ if (rule < 0) {
+ // we add (not subtract it is a negative number)
+ // one from the rule because 1 === last day of month
+ rule = daysInMonth + (rule + 1);
+ } else if (rule === 0) {
+ // skip zero: it is invalid.
+ continue;
+ }
+
+ // only add unique items...
+ if (newRules.indexOf(rule) === -1) {
+ newRules.push(rule);
+ }
+
+ }
+
+ // unique and sort
+ return newRules.sort(function(a, b) { return a - b; });
+ }
+
+ /**
+ * NOTES:
+ * We are given a list of dates in the month (BYMONTHDAY) (23, etc..)
+ * Also we are given a list of days (BYDAY) (MO, 2SU, etc..) when
+ * both conditions match a given date (this.last.day) iteration stops.
+ *
+ * @private
+ * @param {Boolean=} isInit When given true will not increment the
+ * current day (this.last).
+ */
+ _byDayAndMonthDay(isInit) {
+ let byMonthDay; // setup in initMonth
+ let byDay = this.by_data.BYDAY;
+
+ let date;
+ let dateIdx = 0;
+ let dateLen; // setup in initMonth
+ let dayLen = byDay.length;
+
+ // we are not valid by default
+ let dataIsValid = 0;
+
+ let daysInMonth;
+ let self = this;
+ // we need a copy of this, because a DateTime gets normalized
+ // automatically if the day is out of range. At some points we
+ // set the last day to 0 to start counting.
+ let lastDay = this.last.day;
+
+ function initMonth() {
+ daysInMonth = Time.daysInMonth(
+ self.last.month, self.last.year
+ );
+
+ byMonthDay = self.normalizeByMonthDayRules(
+ self.last.year,
+ self.last.month,
+ self.by_data.BYMONTHDAY
+ );
+
+ dateLen = byMonthDay.length;
+
+ // For the case of more than one occurrence in one month
+ // we have to be sure to start searching after the last
+ // found date or at the last BYMONTHDAY, unless we are
+ // initializing the iterator because in this case we have
+ // to consider the last found date too.
+ while (byMonthDay[dateIdx] <= lastDay &&
+ !(isInit && byMonthDay[dateIdx] == lastDay) &&
+ dateIdx < dateLen - 1) {
+ dateIdx++;
+ }
+ }
+
+ function nextMonth() {
+ // since the day is incremented at the start
+ // of the loop below, we need to start at 0
+ lastDay = 0;
+ self.increment_month();
+ dateIdx = 0;
+ initMonth();
+ }
+
+ initMonth();
+
+ // should come after initMonth
+ if (isInit) {
+ lastDay -= 1;
+ }
+
+ // Use a counter to avoid an infinite loop with malformed rules.
+ // Stop checking after 4 years so we consider also a leap year.
+ let monthsCounter = 48;
+
+ while (!dataIsValid && monthsCounter) {
+ monthsCounter--;
+ // increment the current date. This is really
+ // important otherwise we may fall into the infinite
+ // loop trap. The initial date takes care of the case
+ // where the current date is the date we are looking
+ // for.
+ date = lastDay + 1;
+
+ if (date > daysInMonth) {
+ nextMonth();
+ continue;
+ }
+
+ // find next date
+ let next = byMonthDay[dateIdx++];
+
+ // this logic is dependent on the BYMONTHDAYS
+ // being in order (which is done by #normalizeByMonthDayRules)
+ if (next >= date) {
+ // if the next month day is in the future jump to it.
+ lastDay = next;
+ } else {
+ // in this case the 'next' monthday has past
+ // we must move to the month.
+ nextMonth();
+ continue;
+ }
+
+ // Now we can loop through the day rules to see
+ // if one matches the current month date.
+ for (let dayIdx = 0; dayIdx < dayLen; dayIdx++) {
+ let parts = this.ruleDayOfWeek(byDay[dayIdx]);
+ let pos = parts[0];
+ let dow = parts[1];
+
+ this.last.day = lastDay;
+ if (this.last.isNthWeekDay(dow, pos)) {
+ // when we find the valid one we can mark
+ // the conditions as met and break the loop.
+ // (Because we have this condition above
+ // it will also break the parent loop).
+ dataIsValid = 1;
+ break;
+ }
+ }
+
+ // It is completely possible that the combination
+ // cannot be matched in the current month.
+ // When we reach the end of possible combinations
+ // in the current month we iterate to the next one.
+ // since dateIdx is incremented right after getting
+ // "next", we don't need dateLen -1 here.
+ if (!dataIsValid && dateIdx === dateLen) {
+ nextMonth();
+ continue;
+ }
+ }
+
+ if (monthsCounter <= 0) {
+ // Checked 4 years without finding a Byday that matches
+ // a Bymonthday. Maybe the rule is not correct.
+ throw new Error("Malformed values in BYDAY combined with BYMONTHDAY parts");
+ }
+
+
+ return dataIsValid;
+ }
+
+ next_month() {
+ let data_valid = 1;
+
+ if (this.next_hour() == 0) {
+ return data_valid;
+ }
+
+ if (this.has_by_data("BYDAY") && this.has_by_data("BYMONTHDAY")) {
+ data_valid = this._byDayAndMonthDay();
+ } else if (this.has_by_data("BYDAY")) {
+ let daysInMonth = Time.daysInMonth(this.last.month, this.last.year);
+ let setpos = 0;
+ let setpos_total = 0;
+
+ if (this.has_by_data("BYSETPOS")) {
+ let last_day = this.last.day;
+ for (let day = 1; day <= daysInMonth; day++) {
+ this.last.day = day;
+ if (this.is_day_in_byday(this.last)) {
+ setpos_total++;
+ if (day <= last_day) {
+ setpos++;
+ }
+ }
+ }
+ this.last.day = last_day;
+ }
+
+ data_valid = 0;
+ let day;
+ for (day = this.last.day + 1; day <= daysInMonth; day++) {
+ this.last.day = day;
+
+ if (this.is_day_in_byday(this.last)) {
+ if (!this.has_by_data("BYSETPOS") ||
+ this.check_set_position(++setpos) ||
+ this.check_set_position(setpos - setpos_total - 1)) {
+
+ data_valid = 1;
+ break;
+ }
+ }
+ }
+
+ if (day > daysInMonth) {
+ this.last.day = 1;
+ this.increment_month();
+
+ if (this.is_day_in_byday(this.last)) {
+ if (!this.has_by_data("BYSETPOS") || this.check_set_position(1)) {
+ data_valid = 1;
+ }
+ } else {
+ data_valid = 0;
+ }
+ }
+ } else if (this.has_by_data("BYMONTHDAY")) {
+ this.by_indices.BYMONTHDAY++;
+
+ if (this.by_indices.BYMONTHDAY >= this.by_data.BYMONTHDAY.length) {
+ this.by_indices.BYMONTHDAY = 0;
+ this.increment_month();
+ if (this.by_indices.BYMONTHDAY >= this.by_data.BYMONTHDAY.length) {
+ return 0;
+ }
+ }
+
+ let daysInMonth = Time.daysInMonth(this.last.month, this.last.year);
+ let day = this.by_data.BYMONTHDAY[this.by_indices.BYMONTHDAY];
+
+ if (day < 0) {
+ day = daysInMonth + day + 1;
+ }
+
+ if (day > daysInMonth) {
+ this.last.day = 1;
+ data_valid = this.is_day_in_byday(this.last);
+ } else {
+ this.last.day = day;
+ }
+ } else {
+ this.increment_month();
+ let daysInMonth = Time.daysInMonth(this.last.month, this.last.year);
+ if (this.by_data.BYMONTHDAY[0] > daysInMonth) {
+ data_valid = 0;
+ } else {
+ this.last.day = this.by_data.BYMONTHDAY[0];
+ }
+ }
+
+ return data_valid;
+ }
+
+ next_weekday_by_week() {
+ let end_of_data = 0;
+
+ if (this.next_hour() == 0) {
+ return end_of_data;
+ }
+
+ if (!this.has_by_data("BYDAY")) {
+ return 1;
+ }
+
+ for (;;) {
+ let tt = new Time();
+ this.by_indices.BYDAY++;
+
+ if (this.by_indices.BYDAY == Object.keys(this.by_data.BYDAY).length) {
+ this.by_indices.BYDAY = 0;
+ end_of_data = 1;
+ }
+
+ let coded_day = this.by_data.BYDAY[this.by_indices.BYDAY];
+ let parts = this.ruleDayOfWeek(coded_day);
+ let dow = parts[1];
+
+ dow -= this.rule.wkst;
+
+ if (dow < 0) {
+ dow += 7;
+ }
+
+ tt.year = this.last.year;
+ tt.month = this.last.month;
+ tt.day = this.last.day;
+
+ let startOfWeek = tt.startDoyWeek(this.rule.wkst);
+
+ if (dow + startOfWeek < 1) {
+ // The selected date is in the previous year
+ if (!end_of_data) {
+ continue;
+ }
+ }
+
+ let next = Time.fromDayOfYear(startOfWeek + dow, this.last.year);
+
+ /**
+ * The normalization horrors below are due to
+ * the fact that when the year/month/day changes
+ * it can effect the other operations that come after.
+ */
+ this.last.year = next.year;
+ this.last.month = next.month;
+ this.last.day = next.day;
+
+ return end_of_data;
+ }
+ }
+
+ next_year() {
+ if (this.next_hour() == 0) {
+ return 0;
+ }
+
+ if (this.days.length == 0 || ++this.days_index == this.days.length) {
+ this.days_index = 0;
+ this.increment_year(this.rule.interval);
+ if (this.has_by_data("BYMONTHDAY")) {
+ this.by_data.BYMONTHDAY = this.normalizeByMonthDayRules(
+ this.last.year,
+ this.last.month,
+ this.rule.parts.BYMONTHDAY
+ );
+ }
+ this.expand_year_days(this.last.year);
+ if (this.days.length == 0) {
+ return 0;
+ }
+ }
+
+ return this._nextByYearDay();
+ }
+
+ _nextByYearDay() {
+ let doy = this.days[this.days_index];
+ let year = this.last.year;
+
+ if (Math.abs(doy) == 366 && !Time.isLeapYear(this.last.year)) {
+ return 0;
+ }
+
+ if (doy < 1) {
+ // Time.fromDayOfYear(doy, year) indexes relative to the
+ // start of the given year. That is different from the
+ // semantics of BYYEARDAY where negative indexes are an
+ // offset from the end of the given year.
+ doy += 1;
+ year += 1;
+ }
+ let next = Time.fromDayOfYear(doy, year);
+ this.last.day = next.day;
+ this.last.month = next.month;
+
+ return 1;
+ }
+
+ /**
+ * @param dow (eg: '1TU', '-1MO')
+ * @param {weekDay=} aWeekStart The week start weekday
+ * @return [pos, numericDow] (eg: [1, 3]) numericDow is relative to aWeekStart
+ */
+ ruleDayOfWeek(dow, aWeekStart) {
+ let matches = dow.match(/([+-]?[0-9])?(MO|TU|WE|TH|FR|SA|SU)/);
+ if (matches) {
+ let pos = parseInt(matches[1] || 0, 10);
+ dow = Recur.icalDayToNumericDay(matches[2], aWeekStart);
+ return [pos, dow];
+ } else {
+ return [0, 0];
+ }
+ }
+
+ next_generic(aRuleType, aInterval, aDateAttr, aFollowingAttr, aPreviousIncr) {
+ let has_by_rule = (aRuleType in this.by_data);
+ let this_freq = (this.rule.freq == aInterval);
+ let end_of_data = 0;
+
+ if (aPreviousIncr && this[aPreviousIncr]() == 0) {
+ return end_of_data;
+ }
+
+ if (has_by_rule) {
+ this.by_indices[aRuleType]++;
+ let dta = this.by_data[aRuleType];
+
+ if (this.by_indices[aRuleType] == dta.length) {
+ this.by_indices[aRuleType] = 0;
+ end_of_data = 1;
+ }
+ this.last[aDateAttr] = dta[this.by_indices[aRuleType]];
+ } else if (this_freq) {
+ this["increment_" + aDateAttr](this.rule.interval);
+ }
+
+ if (has_by_rule && end_of_data && this_freq) {
+ this["increment_" + aFollowingAttr](1);
+ }
+
+ return end_of_data;
+ }
+
+ increment_monthday(inc) {
+ for (let i = 0; i < inc; i++) {
+ let daysInMonth = Time.daysInMonth(this.last.month, this.last.year);
+ this.last.day++;
+
+ if (this.last.day > daysInMonth) {
+ this.last.day -= daysInMonth;
+ this.increment_month();
+ }
+ }
+ }
+
+ increment_month() {
+ this.last.day = 1;
+ if (this.has_by_data("BYMONTH")) {
+ this.by_indices.BYMONTH++;
+
+ if (this.by_indices.BYMONTH == this.by_data.BYMONTH.length) {
+ this.by_indices.BYMONTH = 0;
+ this.increment_year(1);
+ }
+
+ this.last.month = this.by_data.BYMONTH[this.by_indices.BYMONTH];
+ } else {
+ if (this.rule.freq == "MONTHLY") {
+ this.last.month += this.rule.interval;
+ } else {
+ this.last.month++;
+ }
+
+ this.last.month--;
+ let years = trunc(this.last.month / 12);
+ this.last.month %= 12;
+ this.last.month++;
+
+ if (years != 0) {
+ this.increment_year(years);
+ }
+ }
+
+ if (this.has_by_data("BYMONTHDAY")) {
+ this.by_data.BYMONTHDAY = this.normalizeByMonthDayRules(
+ this.last.year,
+ this.last.month,
+ this.rule.parts.BYMONTHDAY
+ );
+ }
+ }
+
+ increment_year(inc) {
+ // Don't jump into the next month if this.last is Feb 29.
+ this.last.day = 1;
+ this.last.year += inc;
+ }
+
+ increment_generic(inc, aDateAttr, aFactor, aNextIncrement) {
+ this.last[aDateAttr] += inc;
+ let nextunit = trunc(this.last[aDateAttr] / aFactor);
+ this.last[aDateAttr] %= aFactor;
+ if (nextunit != 0) {
+ this["increment_" + aNextIncrement](nextunit);
+ }
+ }
+
+ has_by_data(aRuleType) {
+ return (aRuleType in this.rule.parts);
+ }
+
+ expand_year_days(aYear) {
+ let t = new Time();
+ this.days = [];
+
+ // We need our own copy with a few keys set
+ let parts = {};
+ let rules = ["BYDAY", "BYWEEKNO", "BYMONTHDAY", "BYMONTH", "BYYEARDAY"];
+ for (let part of rules) {
+ if (part in this.rule.parts) {
+ parts[part] = this.rule.parts[part];
+ }
+ }
+
+ if ("BYMONTH" in parts && "BYWEEKNO" in parts) {
+ let valid = 1;
+ let validWeeks = {};
+ t.year = aYear;
+ t.isDate = true;
+
+ for (let monthIdx = 0; monthIdx < this.by_data.BYMONTH.length; monthIdx++) {
+ let month = this.by_data.BYMONTH[monthIdx];
+ t.month = month;
+ t.day = 1;
+ let first_week = t.weekNumber(this.rule.wkst);
+ t.day = Time.daysInMonth(month, aYear);
+ let last_week = t.weekNumber(this.rule.wkst);
+ for (monthIdx = first_week; monthIdx < last_week; monthIdx++) {
+ validWeeks[monthIdx] = 1;
+ }
+ }
+
+ for (let weekIdx = 0; weekIdx < this.by_data.BYWEEKNO.length && valid; weekIdx++) {
+ let weekno = this.by_data.BYWEEKNO[weekIdx];
+ if (weekno < 52) {
+ valid &= validWeeks[weekIdx];
+ } else {
+ valid = 0;
+ }
+ }
+
+ if (valid) {
+ delete parts.BYMONTH;
+ } else {
+ delete parts.BYWEEKNO;
+ }
+ }
+
+ let partCount = Object.keys(parts).length;
+
+ if (partCount == 0) {
+ let t1 = this.dtstart.clone();
+ t1.year = this.last.year;
+ this.days.push(t1.dayOfYear());
+ } else if (partCount == 1 && "BYMONTH" in parts) {
+ for (let month of this.by_data.BYMONTH) {
+ let t2 = this.dtstart.clone();
+ t2.year = aYear;
+ t2.month = month;
+ t2.isDate = true;
+ this.days.push(t2.dayOfYear());
+ }
+ } else if (partCount == 1 && "BYMONTHDAY" in parts) {
+ for (let monthday of this.by_data.BYMONTHDAY) {
+ let t3 = this.dtstart.clone();
+ if (monthday < 0) {
+ let daysInMonth = Time.daysInMonth(t3.month, aYear);
+ monthday = monthday + daysInMonth + 1;
+ }
+ t3.day = monthday;
+ t3.year = aYear;
+ t3.isDate = true;
+ this.days.push(t3.dayOfYear());
+ }
+ } else if (partCount == 2 &&
+ "BYMONTHDAY" in parts &&
+ "BYMONTH" in parts) {
+ for (let month of this.by_data.BYMONTH) {
+ let daysInMonth = Time.daysInMonth(month, aYear);
+ for (let monthday of this.by_data.BYMONTHDAY) {
+ if (monthday < 0) {
+ monthday = monthday + daysInMonth + 1;
+ }
+ t.day = monthday;
+ t.month = month;
+ t.year = aYear;
+ t.isDate = true;
+
+ this.days.push(t.dayOfYear());
+ }
+ }
+ } else if (partCount == 1 && "BYWEEKNO" in parts) ; else if (partCount == 2 &&
+ "BYWEEKNO" in parts &&
+ "BYMONTHDAY" in parts) ; else if (partCount == 1 && "BYDAY" in parts) {
+ this.days = this.days.concat(this.expand_by_day(aYear));
+ } else if (partCount == 2 && "BYDAY" in parts && "BYMONTH" in parts) {
+ for (let month of this.by_data.BYMONTH) {
+ let daysInMonth = Time.daysInMonth(month, aYear);
+
+ t.year = aYear;
+ t.month = month;
+ t.day = 1;
+ t.isDate = true;
+
+ let first_dow = t.dayOfWeek();
+ let doy_offset = t.dayOfYear() - 1;
+
+ t.day = daysInMonth;
+ let last_dow = t.dayOfWeek();
+
+ if (this.has_by_data("BYSETPOS")) {
+ let by_month_day = [];
+ for (let day = 1; day <= daysInMonth; day++) {
+ t.day = day;
+ if (this.is_day_in_byday(t)) {
+ by_month_day.push(day);
+ }
+ }
+
+ for (let spIndex = 0; spIndex < by_month_day.length; spIndex++) {
+ if (this.check_set_position(spIndex + 1) ||
+ this.check_set_position(spIndex - by_month_day.length)) {
+ this.days.push(doy_offset + by_month_day[spIndex]);
+ }
+ }
+ } else {
+ for (let coded_day of this.by_data.BYDAY) {
+ let bydayParts = this.ruleDayOfWeek(coded_day);
+ let pos = bydayParts[0];
+ let dow = bydayParts[1];
+ let month_day;
+
+ let first_matching_day = ((dow + 7 - first_dow) % 7) + 1;
+ let last_matching_day = daysInMonth - ((last_dow + 7 - dow) % 7);
+
+ if (pos == 0) {
+ for (let day = first_matching_day; day <= daysInMonth; day += 7) {
+ this.days.push(doy_offset + day);
+ }
+ } else if (pos > 0) {
+ month_day = first_matching_day + (pos - 1) * 7;
+
+ if (month_day <= daysInMonth) {
+ this.days.push(doy_offset + month_day);
+ }
+ } else {
+ month_day = last_matching_day + (pos + 1) * 7;
+
+ if (month_day > 0) {
+ this.days.push(doy_offset + month_day);
+ }
+ }
+ }
+ }
+ }
+ // Return dates in order of occurrence (1,2,3,...) instead
+ // of by groups of weekdays (1,8,15,...,2,9,16,...).
+ this.days.sort(function(a, b) { return a - b; }); // Comparator function allows to sort numbers.
+ } else if (partCount == 2 && "BYDAY" in parts && "BYMONTHDAY" in parts) {
+ let expandedDays = this.expand_by_day(aYear);
+
+ for (let day of expandedDays) {
+ let tt = Time.fromDayOfYear(day, aYear);
+ if (this.by_data.BYMONTHDAY.indexOf(tt.day) >= 0) {
+ this.days.push(day);
+ }
+ }
+ } else if (partCount == 3 &&
+ "BYDAY" in parts &&
+ "BYMONTHDAY" in parts &&
+ "BYMONTH" in parts) {
+ let expandedDays = this.expand_by_day(aYear);
+
+ for (let day of expandedDays) {
+ let tt = Time.fromDayOfYear(day, aYear);
+
+ if (this.by_data.BYMONTH.indexOf(tt.month) >= 0 &&
+ this.by_data.BYMONTHDAY.indexOf(tt.day) >= 0) {
+ this.days.push(day);
+ }
+ }
+ } else if (partCount == 2 && "BYDAY" in parts && "BYWEEKNO" in parts) {
+ let expandedDays = this.expand_by_day(aYear);
+
+ for (let day of expandedDays) {
+ let tt = Time.fromDayOfYear(day, aYear);
+ let weekno = tt.weekNumber(this.rule.wkst);
+
+ if (this.by_data.BYWEEKNO.indexOf(weekno)) {
+ this.days.push(day);
+ }
+ }
+ } else if (partCount == 3 &&
+ "BYDAY" in parts &&
+ "BYWEEKNO" in parts &&
+ "BYMONTHDAY" in parts) ; else if (partCount == 1 && "BYYEARDAY" in parts) {
+ this.days = this.days.concat(this.by_data.BYYEARDAY);
+ } else if (partCount == 2 && "BYYEARDAY" in parts && "BYDAY" in parts) {
+ let daysInYear = Time.isLeapYear(aYear) ? 366 : 365;
+ let expandedDays = new Set(this.expand_by_day(aYear));
+
+ for (let doy of this.by_data.BYYEARDAY) {
+ if (doy < 0) {
+ doy += daysInYear + 1;
+ }
+
+ if (expandedDays.has(doy)) {
+ this.days.push(doy);
+ }
+ }
+ } else {
+ this.days = [];
+ }
+
+ let daysInYear = Time.isLeapYear(aYear) ? 366 : 365;
+ this.days.sort((a, b) => {
+ if (a < 0) a += daysInYear + 1;
+ if (b < 0) b += daysInYear + 1;
+ return a - b;
+ });
+
+ return 0;
+ }
+
+ expand_by_day(aYear) {
+
+ let days_list = [];
+ let tmp = this.last.clone();
+
+ tmp.year = aYear;
+ tmp.month = 1;
+ tmp.day = 1;
+ tmp.isDate = true;
+
+ let start_dow = tmp.dayOfWeek();
+
+ tmp.month = 12;
+ tmp.day = 31;
+ tmp.isDate = true;
+
+ let end_dow = tmp.dayOfWeek();
+ let end_year_day = tmp.dayOfYear();
+
+ for (let day of this.by_data.BYDAY) {
+ let parts = this.ruleDayOfWeek(day);
+ let pos = parts[0];
+ let dow = parts[1];
+
+ if (pos == 0) {
+ let tmp_start_doy = ((dow + 7 - start_dow) % 7) + 1;
+
+ for (let doy = tmp_start_doy; doy <= end_year_day; doy += 7) {
+ days_list.push(doy);
+ }
+
+ } else if (pos > 0) {
+ let first;
+ if (dow >= start_dow) {
+ first = dow - start_dow + 1;
+ } else {
+ first = dow - start_dow + 8;
+ }
+
+ days_list.push(first + (pos - 1) * 7);
+ } else {
+ let last;
+ pos = -pos;
+
+ if (dow <= end_dow) {
+ last = end_year_day - end_dow + dow;
+ } else {
+ last = end_year_day - end_dow + dow - 7;
+ }
+
+ days_list.push(last - (pos - 1) * 7);
+ }
+ }
+ return days_list;
+ }
+
+ is_day_in_byday(tt) {
+ if (this.by_data.BYDAY) {
+ for (let day of this.by_data.BYDAY) {
+ let parts = this.ruleDayOfWeek(day);
+ let pos = parts[0];
+ let dow = parts[1];
+ let this_dow = tt.dayOfWeek();
+
+ if ((pos == 0 && dow == this_dow) ||
+ (tt.nthWeekDay(dow, pos) == tt.day)) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * Checks if given value is in BYSETPOS.
+ *
+ * @private
+ * @param {Numeric} aPos position to check for.
+ * @return {Boolean} false unless BYSETPOS rules exist
+ * and the given value is present in rules.
+ */
+ check_set_position(aPos) {
+ if (this.has_by_data('BYSETPOS')) {
+ let idx = this.by_data.BYSETPOS.indexOf(aPos);
+ // negative numbers are not false-y
+ return idx !== -1;
+ }
+ return false;
+ }
+
+ sort_byday_rules(aRules) {
+ for (let i = 0; i < aRules.length; i++) {
+ for (let j = 0; j < i; j++) {
+ let one = this.ruleDayOfWeek(aRules[j], this.rule.wkst)[1];
+ let two = this.ruleDayOfWeek(aRules[i], this.rule.wkst)[1];
+
+ if (one > two) {
+ let tmp = aRules[i];
+ aRules[i] = aRules[j];
+ aRules[j] = tmp;
+ }
+ }
+ }
+ }
+
+ check_contract_restriction(aRuleType, v) {
+ let indexMapValue = RecurIterator._indexMap[aRuleType];
+ let ruleMapValue = RecurIterator._expandMap[this.rule.freq][indexMapValue];
+ let pass = false;
+
+ if (aRuleType in this.by_data &&
+ ruleMapValue == RecurIterator.CONTRACT) {
+
+ let ruleType = this.by_data[aRuleType];
+
+ for (let bydata of ruleType) {
+ if (bydata == v) {
+ pass = true;
+ break;
+ }
+ }
+ } else {
+ // Not a contracting byrule or has no data, test passes
+ pass = true;
+ }
+ return pass;
+ }
+
+ check_contracting_rules() {
+ let dow = this.last.dayOfWeek();
+ let weekNo = this.last.weekNumber(this.rule.wkst);
+ let doy = this.last.dayOfYear();
+
+ return (this.check_contract_restriction("BYSECOND", this.last.second) &&
+ this.check_contract_restriction("BYMINUTE", this.last.minute) &&
+ this.check_contract_restriction("BYHOUR", this.last.hour) &&
+ this.check_contract_restriction("BYDAY", Recur.numericDayToIcalDay(dow)) &&
+ this.check_contract_restriction("BYWEEKNO", weekNo) &&
+ this.check_contract_restriction("BYMONTHDAY", this.last.day) &&
+ this.check_contract_restriction("BYMONTH", this.last.month) &&
+ this.check_contract_restriction("BYYEARDAY", doy));
+ }
+
+ setup_defaults(aRuleType, req, deftime) {
+ let indexMapValue = RecurIterator._indexMap[aRuleType];
+ let ruleMapValue = RecurIterator._expandMap[this.rule.freq][indexMapValue];
+
+ if (ruleMapValue != RecurIterator.CONTRACT) {
+ if (!(aRuleType in this.by_data)) {
+ this.by_data[aRuleType] = [deftime];
+ }
+ if (this.rule.freq != req) {
+ return this.by_data[aRuleType][0];
+ }
+ }
+ return deftime;
+ }
+
+ /**
+ * Convert iterator into a serialize-able object. Will preserve current
+ * iteration sequence to ensure the seamless continuation of the recurrence
+ * rule.
+ * @return {Object}
+ */
+ toJSON() {
+ let result = Object.create(null);
+
+ result.initialized = this.initialized;
+ result.rule = this.rule.toJSON();
+ result.dtstart = this.dtstart.toJSON();
+ result.by_data = this.by_data;
+ result.days = this.days;
+ result.last = this.last.toJSON();
+ result.by_indices = this.by_indices;
+ result.occurrence_number = this.occurrence_number;
+
+ return result;
+ }
+}
+
+/**
+ * An error indicating that a recurrence rule is invalid and produces no
+ * occurrences.
+ *
+ * @extends {Error}
+ * @class
+ */
+class InvalidRecurrenceRuleError extends Error {
+ constructor() {
+ super("Recurrence rule has no valid occurrences");
+ }
+}
+
+/* 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 */
+
+
+/**
+ * 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").weekDay} weekDay
+ * Imports the 'weekDay' type from the "types.js" module
+ * @typedef {import("./types.js").frequencyValues} frequencyValues
+ * Imports the 'frequencyValues' type from the "types.js" module
+ */
+
+const VALID_DAY_NAMES = /^(SU|MO|TU|WE|TH|FR|SA)$/;
+const VALID_BYDAY_PART = /^([+-])?(5[0-3]|[1-4][0-9]|[1-9])?(SU|MO|TU|WE|TH|FR|SA)$/;
+const DOW_MAP = {
+ SU: Time.SUNDAY,
+ MO: Time.MONDAY,
+ TU: Time.TUESDAY,
+ WE: Time.WEDNESDAY,
+ TH: Time.THURSDAY,
+ FR: Time.FRIDAY,
+ SA: Time.SATURDAY
+};
+
+const REVERSE_DOW_MAP = Object.fromEntries(Object.entries(DOW_MAP).map(entry => entry.reverse()));
+
+const ALLOWED_FREQ = ['SECONDLY', 'MINUTELY', 'HOURLY',
+ 'DAILY', 'WEEKLY', 'MONTHLY', 'YEARLY'];
+
+/**
+ * This class represents the "recur" value type, used for example by RRULE. It provides methods to
+ * calculate occurrences among others.
+ *
+ * @memberof ICAL
+ */
+class Recur {
+ /**
+ * Creates a new {@link ICAL.Recur} instance from the passed string.
+ *
+ * @param {String} string The string to parse
+ * @return {Recur} The created recurrence instance
+ */
+ static fromString(string) {
+ let data = this._stringToData(string, false);
+ return new Recur(data);
+ }
+
+ /**
+ * Creates a new {@link ICAL.Recur} instance using members from the passed
+ * data object.
+ *
+ * @param {Object} aData An object with members of the recurrence
+ * @param {frequencyValues=} aData.freq The frequency value
+ * @param {Number=} aData.interval The INTERVAL value
+ * @param {weekDay=} aData.wkst The week start value
+ * @param {Time=} aData.until The end of the recurrence set
+ * @param {Number=} aData.count The number of occurrences
+ * @param {Array.=} aData.bysecond The seconds for the BYSECOND part
+ * @param {Array.=} aData.byminute The minutes for the BYMINUTE part
+ * @param {Array.=} aData.byhour The hours for the BYHOUR part
+ * @param {Array.=} aData.byday The BYDAY values
+ * @param {Array.=} aData.bymonthday The days for the BYMONTHDAY part
+ * @param {Array.=} aData.byyearday The days for the BYYEARDAY part
+ * @param {Array.=} aData.byweekno The weeks for the BYWEEKNO part
+ * @param {Array.=} aData.bymonth The month for the BYMONTH part
+ * @param {Array.=} aData.bysetpos The positionals for the BYSETPOS part
+ */
+ static fromData(aData) {
+ return new Recur(aData);
+ }
+
+ /**
+ * Converts a recurrence string to a data object, suitable for the fromData
+ * method.
+ *
+ * @private
+ * @param {String} string The string to parse
+ * @param {Boolean} fmtIcal If true, the string is considered to be an
+ * iCalendar string
+ * @return {Recur} The recurrence instance
+ */
+ static _stringToData(string, fmtIcal) {
+ let dict = Object.create(null);
+
+ // split is slower in FF but fast enough.
+ // v8 however this is faster then manual split?
+ let values = string.split(';');
+ let len = values.length;
+
+ for (let i = 0; i < len; i++) {
+ let parts = values[i].split('=');
+ let ucname = parts[0].toUpperCase();
+ let lcname = parts[0].toLowerCase();
+ let name = (fmtIcal ? lcname : ucname);
+ let value = parts[1];
+
+ if (ucname in partDesign) {
+ let partArr = value.split(',');
+ let partSet = new Set();
+
+ for (let part of partArr) {
+ partSet.add(partDesign[ucname](part));
+ }
+ partArr = [...partSet];
+
+ dict[name] = (partArr.length == 1 ? partArr[0] : partArr);
+ } else if (ucname in optionDesign) {
+ optionDesign[ucname](value, dict, fmtIcal);
+ } else {
+ // Don't swallow unknown values. Just set them as they are.
+ dict[lcname] = value;
+ }
+ }
+
+ return dict;
+ }
+
+ /**
+ * Convert an ical representation of a day (SU, MO, etc..)
+ * into a numeric value of that day.
+ *
+ * @param {String} string The iCalendar day name
+ * @param {weekDay=} aWeekStart
+ * The week start weekday, defaults to SUNDAY
+ * @return {Number} Numeric value of given day
+ */
+ static icalDayToNumericDay(string, aWeekStart) {
+ //XXX: this is here so we can deal
+ // with possibly invalid string values.
+ let firstDow = aWeekStart || Time.SUNDAY;
+ return ((DOW_MAP[string] - firstDow + 7) % 7) + 1;
+ }
+
+ /**
+ * Convert a numeric day value into its ical representation (SU, MO, etc..)
+ *
+ * @param {Number} num Numeric value of given day
+ * @param {weekDay=} aWeekStart
+ * The week start weekday, defaults to SUNDAY
+ * @return {String} The ICAL day value, e.g SU,MO,...
+ */
+ static numericDayToIcalDay(num, aWeekStart) {
+ //XXX: this is here so we can deal with possibly invalid number values.
+ // Also, this allows consistent mapping between day numbers and day
+ // names for external users.
+ let firstDow = aWeekStart || Time.SUNDAY;
+ let dow = (num + firstDow - Time.SUNDAY);
+ if (dow > 7) {
+ dow -= 7;
+ }
+ return REVERSE_DOW_MAP[dow];
+ }
+
+ /**
+ * Create a new instance of the Recur class.
+ *
+ * @param {Object} data An object with members of the recurrence
+ * @param {frequencyValues=} data.freq The frequency value
+ * @param {Number=} data.interval The INTERVAL value
+ * @param {weekDay=} data.wkst The week start value
+ * @param {Time=} data.until The end of the recurrence set
+ * @param {Number=} data.count The number of occurrences
+ * @param {Array.=} data.bysecond The seconds for the BYSECOND part
+ * @param {Array.=} data.byminute The minutes for the BYMINUTE part
+ * @param {Array.=} data.byhour The hours for the BYHOUR part
+ * @param {Array.=} data.byday The BYDAY values
+ * @param {Array.=} data.bymonthday The days for the BYMONTHDAY part
+ * @param {Array.=} data.byyearday The days for the BYYEARDAY part
+ * @param {Array.=} data.byweekno The weeks for the BYWEEKNO part
+ * @param {Array.=} data.bymonth The month for the BYMONTH part
+ * @param {Array.=} data.bysetpos The positionals for the BYSETPOS part
+ */
+ constructor(data) {
+ this.wrappedJSObject = this;
+ this.parts = {};
+
+ if (data && typeof(data) === 'object') {
+ this.fromData(data);
+ }
+ }
+
+ /**
+ * An object holding the BY-parts of the recurrence rule
+ * @memberof ICAL.Recur
+ * @typedef {Object} byParts
+ * @property {Array.=} BYSECOND The seconds for the BYSECOND part
+ * @property {Array.=} BYMINUTE The minutes for the BYMINUTE part
+ * @property {Array.=} BYHOUR The hours for the BYHOUR part
+ * @property {Array.=} BYDAY The BYDAY values
+ * @property {Array.=} BYMONTHDAY The days for the BYMONTHDAY part
+ * @property {Array.=} BYYEARDAY The days for the BYYEARDAY part
+ * @property {Array.=} BYWEEKNO The weeks for the BYWEEKNO part
+ * @property {Array.=} BYMONTH The month for the BYMONTH part
+ * @property {Array.=} BYSETPOS The positionals for the BYSETPOS part
+ */
+
+ /**
+ * An object holding the BY-parts of the recurrence rule
+ * @type {byParts}
+ */
+ parts = null;
+
+ /**
+ * The interval value for the recurrence rule.
+ * @type {Number}
+ */
+ interval = 1;
+
+ /**
+ * The week start day
+ *
+ * @type {weekDay}
+ * @default ICAL.Time.MONDAY
+ */
+ wkst = Time.MONDAY;
+
+ /**
+ * The end of the recurrence
+ * @type {?Time}
+ */
+ until = null;
+
+ /**
+ * The maximum number of occurrences
+ * @type {?Number}
+ */
+ count = null;
+
+ /**
+ * The frequency value.
+ * @type {frequencyValues}
+ */
+ freq = null;
+
+ /**
+ * The class identifier.
+ * @constant
+ * @type {String}
+ * @default "icalrecur"
+ */
+ icalclass = "icalrecur";
+
+ /**
+ * The type name, to be used in the jCal object.
+ * @constant
+ * @type {String}
+ * @default "recur"
+ */
+ icaltype = "recur";
+
+ /**
+ * Create a new iterator for this recurrence rule. The passed start date
+ * must be the start date of the event, not the start of the range to
+ * search in.
+ *
+ * @example
+ * let recur = comp.getFirstPropertyValue('rrule');
+ * let dtstart = comp.getFirstPropertyValue('dtstart');
+ * let iter = recur.iterator(dtstart);
+ * for (let next = iter.next(); next; next = iter.next()) {
+ * if (next.compare(rangeStart) < 0) {
+ * continue;
+ * }
+ * console.log(next.toString());
+ * }
+ *
+ * @param {Time} aStart The item's start date
+ * @return {RecurIterator} The recurrence iterator
+ */
+ iterator(aStart) {
+ return new RecurIterator({
+ rule: this,
+ dtstart: aStart
+ });
+ }
+
+ /**
+ * Returns a clone of the recurrence object.
+ *
+ * @return {Recur} The cloned object
+ */
+ clone() {
+ return new Recur(this.toJSON());
+ }
+
+ /**
+ * Checks if the current rule is finite, i.e. has a count or until part.
+ *
+ * @return {Boolean} True, if the rule is finite
+ */
+ isFinite() {
+ return !!(this.count || this.until);
+ }
+
+ /**
+ * Checks if the current rule has a count part, and not limited by an until
+ * part.
+ *
+ * @return {Boolean} True, if the rule is by count
+ */
+ isByCount() {
+ return !!(this.count && !this.until);
+ }
+
+ /**
+ * Adds a component (part) to the recurrence rule. This is not a component
+ * in the sense of {@link ICAL.Component}, but a part of the recurrence
+ * rule, i.e. BYMONTH.
+ *
+ * @param {String} aType The name of the component part
+ * @param {Array|String} aValue The component value
+ */
+ addComponent(aType, aValue) {
+ let ucname = aType.toUpperCase();
+ if (ucname in this.parts) {
+ this.parts[ucname].push(aValue);
+ } else {
+ this.parts[ucname] = [aValue];
+ }
+ }
+
+ /**
+ * Sets the component value for the given by-part.
+ *
+ * @param {String} aType The component part name
+ * @param {Array} aValues The component values
+ */
+ setComponent(aType, aValues) {
+ this.parts[aType.toUpperCase()] = aValues.slice();
+ }
+
+ /**
+ * Gets (a copy) of the requested component value.
+ *
+ * @param {String} aType The component part name
+ * @return {Array} The component part value
+ */
+ getComponent(aType) {
+ let ucname = aType.toUpperCase();
+ return (ucname in this.parts ? this.parts[ucname].slice() : []);
+ }
+
+ /**
+ * Retrieves the next occurrence after the given recurrence id. See the
+ * guide on {@tutorial terminology} for more details.
+ *
+ * NOTE: Currently, this method iterates all occurrences from the start
+ * date. It should not be called in a loop for performance reasons. If you
+ * would like to get more than one occurrence, you can iterate the
+ * occurrences manually, see the example on the
+ * {@link ICAL.Recur#iterator iterator} method.
+ *
+ * @param {Time} aStartTime The start of the event series
+ * @param {Time} aRecurrenceId The date of the last occurrence
+ * @return {Time} The next occurrence after
+ */
+ getNextOccurrence(aStartTime, aRecurrenceId) {
+ let iter = this.iterator(aStartTime);
+ let next;
+
+ do {
+ next = iter.next();
+ } while (next && next.compare(aRecurrenceId) <= 0);
+
+ if (next && aRecurrenceId.zone) {
+ next.zone = aRecurrenceId.zone;
+ }
+
+ return next;
+ }
+
+ /**
+ * Sets up the current instance using members from the passed data object.
+ *
+ * @param {Object} data An object with members of the recurrence
+ * @param {frequencyValues=} data.freq The frequency value
+ * @param {Number=} data.interval The INTERVAL value
+ * @param {weekDay=} data.wkst The week start value
+ * @param {Time=} data.until The end of the recurrence set
+ * @param {Number=} data.count The number of occurrences
+ * @param {Array.=} data.bysecond The seconds for the BYSECOND part
+ * @param {Array.=} data.byminute The minutes for the BYMINUTE part
+ * @param {Array.=} data.byhour The hours for the BYHOUR part
+ * @param {Array.=} data.byday The BYDAY values
+ * @param {Array.=} data.bymonthday The days for the BYMONTHDAY part
+ * @param {Array.=} data.byyearday The days for the BYYEARDAY part
+ * @param {Array.=} data.byweekno The weeks for the BYWEEKNO part
+ * @param {Array.=} data.bymonth The month for the BYMONTH part
+ * @param {Array.=} data.bysetpos The positionals for the BYSETPOS part
+ */
+ fromData(data) {
+ for (let key in data) {
+ let uckey = key.toUpperCase();
+
+ if (uckey in partDesign) {
+ if (Array.isArray(data[key])) {
+ this.parts[uckey] = data[key];
+ } else {
+ this.parts[uckey] = [data[key]];
+ }
+ } else {
+ this[key] = data[key];
+ }
+ }
+
+ if (this.interval && typeof this.interval != "number") {
+ optionDesign.INTERVAL(this.interval, this);
+ }
+
+ if (this.wkst && typeof this.wkst != "number") {
+ this.wkst = Recur.icalDayToNumericDay(this.wkst);
+ }
+
+ if (this.until && !(this.until instanceof Time)) {
+ this.until = Time.fromString(this.until);
+ }
+ }
+
+ /**
+ * The jCal representation of this recurrence type.
+ * @return {Object}
+ */
+ toJSON() {
+ let res = Object.create(null);
+ res.freq = this.freq;
+
+ if (this.count) {
+ res.count = this.count;
+ }
+
+ if (this.interval > 1) {
+ res.interval = this.interval;
+ }
+
+ for (let [k, kparts] of Object.entries(this.parts)) {
+ if (Array.isArray(kparts) && kparts.length == 1) {
+ res[k.toLowerCase()] = kparts[0];
+ } else {
+ res[k.toLowerCase()] = clone(kparts);
+ }
+ }
+
+ if (this.until) {
+ res.until = this.until.toString();
+ }
+ if ('wkst' in this && this.wkst !== Time.DEFAULT_WEEK_START) {
+ res.wkst = Recur.numericDayToIcalDay(this.wkst);
+ }
+ return res;
+ }
+
+ /**
+ * The string representation of this recurrence rule.
+ * @return {String}
+ */
+ toString() {
+ // TODO retain order
+ let str = "FREQ=" + this.freq;
+ if (this.count) {
+ str += ";COUNT=" + this.count;
+ }
+ if (this.interval > 1) {
+ str += ";INTERVAL=" + this.interval;
+ }
+ for (let [k, v] of Object.entries(this.parts)) {
+ str += ";" + k + "=" + v;
+ }
+ if (this.until) {
+ str += ';UNTIL=' + this.until.toICALString();
+ }
+ if ('wkst' in this && this.wkst !== Time.DEFAULT_WEEK_START) {
+ str += ';WKST=' + Recur.numericDayToIcalDay(this.wkst);
+ }
+ return str;
+ }
+}
+
+function parseNumericValue(type, min, max, value) {
+ let result = value;
+
+ if (value[0] === '+') {
+ result = value.slice(1);
+ }
+
+ result = strictParseInt(result);
+
+ if (min !== undefined && value < min) {
+ throw new Error(
+ type + ': invalid value "' + value + '" must be > ' + min
+ );
+ }
+
+ if (max !== undefined && value > max) {
+ throw new Error(
+ type + ': invalid value "' + value + '" must be < ' + min
+ );
+ }
+
+ return result;
+}
+
+const optionDesign = {
+ FREQ: function(value, dict, fmtIcal) {
+ // yes this is actually equal or faster then regex.
+ // upside here is we can enumerate the valid values.
+ if (ALLOWED_FREQ.indexOf(value) !== -1) {
+ dict.freq = value;
+ } else {
+ throw new Error(
+ 'invalid frequency "' + value + '" expected: "' +
+ ALLOWED_FREQ.join(', ') + '"'
+ );
+ }
+ },
+
+ COUNT: function(value, dict, fmtIcal) {
+ dict.count = strictParseInt(value);
+ },
+
+ INTERVAL: function(value, dict, fmtIcal) {
+ dict.interval = strictParseInt(value);
+ if (dict.interval < 1) {
+ // 0 or negative values are not allowed, some engines seem to generate
+ // it though. Assume 1 instead.
+ dict.interval = 1;
+ }
+ },
+
+ UNTIL: function(value, dict, fmtIcal) {
+ if (value.length > 10) {
+ dict.until = design.icalendar.value['date-time'].fromICAL(value);
+ } else {
+ dict.until = design.icalendar.value.date.fromICAL(value);
+ }
+ if (!fmtIcal) {
+ dict.until = Time.fromString(dict.until);
+ }
+ },
+
+ WKST: function(value, dict, fmtIcal) {
+ if (VALID_DAY_NAMES.test(value)) {
+ dict.wkst = Recur.icalDayToNumericDay(value);
+ } else {
+ throw new Error('invalid WKST value "' + value + '"');
+ }
+ }
+};
+
+const partDesign = {
+ BYSECOND: parseNumericValue.bind(undefined, 'BYSECOND', 0, 60),
+ BYMINUTE: parseNumericValue.bind(undefined, 'BYMINUTE', 0, 59),
+ BYHOUR: parseNumericValue.bind(undefined, 'BYHOUR', 0, 23),
+ BYDAY: function(value) {
+ if (VALID_BYDAY_PART.test(value)) {
+ return value;
+ } else {
+ throw new Error('invalid BYDAY value "' + value + '"');
+ }
+ },
+ BYMONTHDAY: parseNumericValue.bind(undefined, 'BYMONTHDAY', -31, 31),
+ BYYEARDAY: parseNumericValue.bind(undefined, 'BYYEARDAY', -366, 366),
+ BYWEEKNO: parseNumericValue.bind(undefined, 'BYWEEKNO', -53, 53),
+ BYMONTH: parseNumericValue.bind(undefined, 'BYMONTH', 1, 12),
+ BYSETPOS: parseNumericValue.bind(undefined, 'BYSETPOS', -366, 366)
+};
+
+/* 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 */
+
+
+/**
+ * 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;
+ }
+};
+
+/* 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 */
+
+
+/**
+ * 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
+ */
+
+const LINE_ENDING = '\r\n';
+const DEFAULT_VALUE_TYPE = 'unknown';
+const RFC6868_REPLACE_MAP = { '"': "^'", "\n": "^n", "^": "^^" };
+
+/**
+ * Convert a full jCal/jCard array into a iCalendar/vCard string.
+ *
+ * @function ICAL.stringify
+ * @variation function
+ * @param {Array} jCal The jCal/jCard document
+ * @return {String} The stringified iCalendar/vCard document
+ */
+function stringify(jCal) {
+ if (typeof jCal[0] == "string") {
+ // This is a single component
+ jCal = [jCal];
+ }
+
+ let i = 0;
+ let len = jCal.length;
+ let result = '';
+
+ for (; i < len; i++) {
+ result += stringify.component(jCal[i]) + LINE_ENDING;
+ }
+
+ return result;
+}
+
+/**
+ * Converts an jCal component array into a ICAL string.
+ * Recursive will resolve sub-components.
+ *
+ * Exact component/property order is not saved all
+ * properties will come before subcomponents.
+ *
+ * @function ICAL.stringify.component
+ * @param {Array} component
+ * jCal/jCard fragment of a component
+ * @param {designSet} designSet
+ * The design data to use for this component
+ * @return {String} The iCalendar/vCard string
+ */
+stringify.component = function(component, designSet) {
+ let name = component[0].toUpperCase();
+ let result = 'BEGIN:' + name + LINE_ENDING;
+
+ let props = component[1];
+ let propIdx = 0;
+ let propLen = props.length;
+
+ let designSetName = component[0];
+ // rfc6350 requires that in vCard 4.0 the first component is the VERSION
+ // component with as value 4.0, note that 3.0 does not have this requirement.
+ if (designSetName === 'vcard' && component[1].length > 0 &&
+ !(component[1][0][0] === "version" && component[1][0][3] === "4.0")) {
+ designSetName = "vcard3";
+ }
+ designSet = designSet || design.getDesignSet(designSetName);
+
+ for (; propIdx < propLen; propIdx++) {
+ result += stringify.property(props[propIdx], designSet) + LINE_ENDING;
+ }
+
+ // Ignore subcomponents if none exist, e.g. in vCard.
+ let comps = component[2] || [];
+ let compIdx = 0;
+ let compLen = comps.length;
+
+ for (; compIdx < compLen; compIdx++) {
+ result += stringify.component(comps[compIdx], designSet) + LINE_ENDING;
+ }
+
+ result += 'END:' + name;
+ return result;
+};
+
+/**
+ * Converts a single jCal/jCard property to a iCalendar/vCard string.
+ *
+ * @function ICAL.stringify.property
+ * @param {Array} property
+ * jCal/jCard property array
+ * @param {designSet} designSet
+ * The design data to use for this property
+ * @param {Boolean} noFold
+ * If true, the line is not folded
+ * @return {String} The iCalendar/vCard string
+ */
+stringify.property = function(property, designSet, noFold) {
+ let name = property[0].toUpperCase();
+ let jsName = property[0];
+ let params = property[1];
+
+ if (!designSet) {
+ designSet = design.defaultSet;
+ }
+
+ let groupName = params.group;
+ let line;
+ if (designSet.propertyGroups && groupName) {
+ line = groupName.toUpperCase() + "." + name;
+ } else {
+ line = name;
+ }
+
+ for (let [paramName, value] of Object.entries(params)) {
+ if (designSet.propertyGroups && paramName == 'group') {
+ continue;
+ }
+
+ let paramDesign = designSet.param[paramName];
+ let multiValue = paramDesign && paramDesign.multiValue;
+ if (multiValue && Array.isArray(value)) {
+ value = value.map(function(val) {
+ val = stringify._rfc6868Unescape(val);
+ val = stringify.paramPropertyValue(val, paramDesign.multiValueSeparateDQuote);
+ return val;
+ });
+ value = stringify.multiValue(value, multiValue, "unknown", null, designSet);
+ } else {
+ value = stringify._rfc6868Unescape(value);
+ value = stringify.paramPropertyValue(value);
+ }
+
+ line += ';' + paramName.toUpperCase() + '=' + value;
+ }
+
+ if (property.length === 3) {
+ // If there are no values, we must assume a blank value
+ return line + ':';
+ }
+
+ let valueType = property[2];
+
+ let propDetails;
+ let multiValue = false;
+ let structuredValue = false;
+ let isDefault = false;
+
+ if (jsName in designSet.property) {
+ propDetails = designSet.property[jsName];
+
+ if ('multiValue' in propDetails) {
+ multiValue = propDetails.multiValue;
+ }
+
+ if (('structuredValue' in propDetails) && Array.isArray(property[3])) {
+ structuredValue = propDetails.structuredValue;
+ }
+
+ if ('defaultType' in propDetails) {
+ if (valueType === propDetails.defaultType) {
+ isDefault = true;
+ }
+ } else {
+ if (valueType === DEFAULT_VALUE_TYPE) {
+ isDefault = true;
+ }
+ }
+ } else {
+ if (valueType === DEFAULT_VALUE_TYPE) {
+ isDefault = true;
+ }
+ }
+
+ // push the VALUE property if type is not the default
+ // for the current property.
+ if (!isDefault) {
+ // value will never contain ;/:/, so we don't escape it here.
+ line += ';VALUE=' + valueType.toUpperCase();
+ }
+
+ line += ':';
+
+ if (multiValue && structuredValue) {
+ line += stringify.multiValue(
+ property[3], structuredValue, valueType, multiValue, designSet, structuredValue
+ );
+ } else if (multiValue) {
+ line += stringify.multiValue(
+ property.slice(3), multiValue, valueType, null, designSet, false
+ );
+ } else if (structuredValue) {
+ line += stringify.multiValue(
+ property[3], structuredValue, valueType, null, designSet, structuredValue
+ );
+ } else {
+ line += stringify.value(property[3], valueType, designSet, false);
+ }
+
+ return noFold ? line : foldline(line);
+};
+
+/**
+ * Handles escaping of property values that may contain:
+ *
+ * COLON (:), SEMICOLON (;), or COMMA (,)
+ *
+ * If any of the above are present the result is wrapped
+ * in double quotes.
+ *
+ * @function ICAL.stringify.paramPropertyValue
+ * @param {String} value Raw property value
+ * @param {boolean} force If value should be escaped even when unnecessary
+ * @return {String} Given or escaped value when needed
+ */
+stringify.paramPropertyValue = function(value, force) {
+ if (!force &&
+ (value.indexOf(',') === -1) &&
+ (value.indexOf(':') === -1) &&
+ (value.indexOf(';') === -1)) {
+
+ return value;
+ }
+
+ return '"' + value + '"';
+};
+
+/**
+ * Converts an array of ical values into a single
+ * string based on a type and a delimiter value (like ",").
+ *
+ * @function ICAL.stringify.multiValue
+ * @param {Array} values List of values to convert
+ * @param {String} delim Used to join the values (",", ";", ":")
+ * @param {String} type Lowecase ical value type
+ * (like boolean, date-time, etc..)
+ * @param {?String} innerMulti If set, each value will again be processed
+ * Used for structured values
+ * @param {designSet} designSet
+ * The design data to use for this property
+ *
+ * @return {String} iCalendar/vCard string for value
+ */
+stringify.multiValue = function(values, delim, type, innerMulti, designSet, structuredValue) {
+ let result = '';
+ let len = values.length;
+ let i = 0;
+
+ for (; i < len; i++) {
+ if (innerMulti && Array.isArray(values[i])) {
+ result += stringify.multiValue(values[i], innerMulti, type, null, designSet, structuredValue);
+ } else {
+ result += stringify.value(values[i], type, designSet, structuredValue);
+ }
+
+ if (i !== (len - 1)) {
+ result += delim;
+ }
+ }
+
+ return result;
+};
+
+/**
+ * Processes a single ical value runs the associated "toICAL" method from the
+ * design value type if available to convert the value.
+ *
+ * @function ICAL.stringify.value
+ * @param {String|Number} value A formatted value
+ * @param {String} type Lowercase iCalendar/vCard value type
+ * (like boolean, date-time, etc..)
+ * @return {String} iCalendar/vCard value for single value
+ */
+stringify.value = function(value, type, designSet, structuredValue) {
+ if (type in designSet.value && 'toICAL' in designSet.value[type]) {
+ return designSet.value[type].toICAL(value, structuredValue);
+ }
+ return value;
+};
+
+/**
+ * Internal helper for rfc6868. Exposing this on ICAL.stringify so that
+ * hackers can disable the rfc6868 parsing if the really need to.
+ *
+ * @param {String} val The value to unescape
+ * @return {String} The escaped value
+ */
+stringify._rfc6868Unescape = function(val) {
+ return val.replace(/[\n^"]/g, function(x) {
+ return RFC6868_REPLACE_MAP[x];
+ });
+};
+
+/* 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 */
+
+const NAME_INDEX$1 = 0;
+const PROP_INDEX = 1;
+const TYPE_INDEX = 2;
+const VALUE_INDEX = 3;
+
+/**
+ * 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
+ * @typedef {import("./types.js").Geo} Geo
+ * Imports the 'Geo' type from the "types.js" module
+ */
+
+/**
+ * Provides a layer on top of the raw jCal object for manipulating a single property, with its
+ * parameters and value.
+ *
+ * @memberof ICAL
+ */
+class Property {
+ /**
+ * Create an {@link ICAL.Property} by parsing the passed iCalendar string.
+ *
+ * @param {String} str The iCalendar string to parse
+ * @param {designSet=} designSet The design data to use for this property
+ * @return {Property} The created iCalendar property
+ */
+ static fromString(str, designSet) {
+ return new Property(parse.property(str, designSet));
+ }
+
+ /**
+ * Creates a new ICAL.Property instance.
+ *
+ * It is important to note that mutations done in the wrapper directly mutate the jCal object used
+ * to initialize.
+ *
+ * Can also be used to create new properties by passing the name of the property (as a String).
+ *
+ * @param {Array|String} jCal Raw jCal representation OR the new name of the property
+ * @param {Component=} parent Parent component
+ */
+ constructor(jCal, parent) {
+ this._parent = parent || null;
+
+ if (typeof(jCal) === 'string') {
+ // We are creating the property by name and need to detect the type
+ this.jCal = [jCal, {}, design.defaultType];
+ this.jCal[TYPE_INDEX] = this.getDefaultType();
+ } else {
+ this.jCal = jCal;
+ }
+ this._updateType();
+ }
+
+ /**
+ * The value type for this property
+ * @type {String}
+ */
+ get type() {
+ return this.jCal[TYPE_INDEX];
+ }
+
+ /**
+ * The name of this property, in lowercase.
+ * @type {String}
+ */
+ get name() {
+ return this.jCal[NAME_INDEX$1];
+ }
+
+ /**
+ * The parent component for this property.
+ * @type {Component}
+ */
+ get parent() {
+ return this._parent;
+ }
+
+ set parent(p) {
+ // Before setting the parent, check if the design set has changed. If it
+ // has, we later need to update the type if it was unknown before.
+ let designSetChanged = !this._parent || (p && p._designSet != this._parent._designSet);
+
+ this._parent = p;
+
+ if (this.type == design.defaultType && designSetChanged) {
+ this.jCal[TYPE_INDEX] = this.getDefaultType();
+ this._updateType();
+ }
+ }
+
+ /**
+ * The design set for this property, e.g. icalendar vs vcard
+ *
+ * @type {designSet}
+ * @private
+ */
+ get _designSet() {
+ return this.parent ? this.parent._designSet : design.defaultSet;
+ }
+
+ /**
+ * Updates the type metadata from the current jCal type and design set.
+ *
+ * @private
+ */
+ _updateType() {
+ let designSet = this._designSet;
+
+ if (this.type in designSet.value) {
+ if ('decorate' in designSet.value[this.type]) {
+ this.isDecorated = true;
+ } else {
+ this.isDecorated = false;
+ }
+
+ if (this.name in designSet.property) {
+ this.isMultiValue = ('multiValue' in designSet.property[this.name]);
+ this.isStructuredValue = ('structuredValue' in designSet.property[this.name]);
+ }
+ }
+ }
+
+ /**
+ * Hydrate a single value. The act of hydrating means turning the raw jCal
+ * value into a potentially wrapped object, for example {@link ICAL.Time}.
+ *
+ * @private
+ * @param {Number} index The index of the value to hydrate
+ * @return {?Object} The decorated value.
+ */
+ _hydrateValue(index) {
+ if (this._values && this._values[index]) {
+ return this._values[index];
+ }
+
+ // for the case where there is no value.
+ if (this.jCal.length <= (VALUE_INDEX + index)) {
+ return null;
+ }
+
+ if (this.isDecorated) {
+ if (!this._values) {
+ this._values = [];
+ }
+ return (this._values[index] = this._decorate(
+ this.jCal[VALUE_INDEX + index]
+ ));
+ } else {
+ return this.jCal[VALUE_INDEX + index];
+ }
+ }
+
+ /**
+ * Decorate a single value, returning its wrapped object. This is used by
+ * the hydrate function to actually wrap the value.
+ *
+ * @private
+ * @param {?} value The value to decorate
+ * @return {Object} The decorated value
+ */
+ _decorate(value) {
+ return this._designSet.value[this.type].decorate(value, this);
+ }
+
+ /**
+ * Undecorate a single value, returning its raw jCal data.
+ *
+ * @private
+ * @param {Object} value The value to undecorate
+ * @return {?} The undecorated value
+ */
+ _undecorate(value) {
+ return this._designSet.value[this.type].undecorate(value, this);
+ }
+
+ /**
+ * Sets the value at the given index while also hydrating it. The passed
+ * value can either be a decorated or undecorated value.
+ *
+ * @private
+ * @param {?} value The value to set
+ * @param {Number} index The index to set it at
+ */
+ _setDecoratedValue(value, index) {
+ if (!this._values) {
+ this._values = [];
+ }
+
+ if (typeof(value) === 'object' && 'icaltype' in value) {
+ // decorated value
+ this.jCal[VALUE_INDEX + index] = this._undecorate(value);
+ this._values[index] = value;
+ } else {
+ // undecorated value
+ this.jCal[VALUE_INDEX + index] = value;
+ this._values[index] = this._decorate(value);
+ }
+ }
+
+ /**
+ * Gets a parameter on the property.
+ *
+ * @param {String} name Parameter name (lowercase)
+ * @return {Array|String} Parameter value
+ */
+ getParameter(name) {
+ if (name in this.jCal[PROP_INDEX]) {
+ return this.jCal[PROP_INDEX][name];
+ } else {
+ return undefined;
+ }
+ }
+
+ /**
+ * Gets first parameter on the property.
+ *
+ * @param {String} name Parameter name (lowercase)
+ * @return {String} Parameter value
+ */
+ getFirstParameter(name) {
+ let parameters = this.getParameter(name);
+
+ if (Array.isArray(parameters)) {
+ return parameters[0];
+ }
+
+ return parameters;
+ }
+
+ /**
+ * Sets a parameter on the property.
+ *
+ * @param {String} name The parameter name
+ * @param {Array|String} value The parameter value
+ */
+ setParameter(name, value) {
+ let lcname = name.toLowerCase();
+ if (typeof value === "string" &&
+ lcname in this._designSet.param &&
+ 'multiValue' in this._designSet.param[lcname]) {
+ value = [value];
+ }
+ this.jCal[PROP_INDEX][name] = value;
+ }
+
+ /**
+ * Removes a parameter
+ *
+ * @param {String} name The parameter name
+ */
+ removeParameter(name) {
+ delete this.jCal[PROP_INDEX][name];
+ }
+
+ /**
+ * Get the default type based on this property's name.
+ *
+ * @return {String} The default type for this property
+ */
+ getDefaultType() {
+ let name = this.jCal[NAME_INDEX$1];
+ let designSet = this._designSet;
+
+ if (name in designSet.property) {
+ let details = designSet.property[name];
+ if ('defaultType' in details) {
+ return details.defaultType;
+ }
+ }
+ return design.defaultType;
+ }
+
+ /**
+ * Sets type of property and clears out any existing values of the current
+ * type.
+ *
+ * @param {String} type New iCAL type (see design.*.values)
+ */
+ resetType(type) {
+ this.removeAllValues();
+ this.jCal[TYPE_INDEX] = type;
+ this._updateType();
+ }
+
+ /**
+ * Finds the first property value.
+ *
+ * @return {Binary | Duration | Period |
+ * Recur | Time | UtcOffset | Geo | string | null} First property value
+ */
+ getFirstValue() {
+ return this._hydrateValue(0);
+ }
+
+ /**
+ * Gets all values on the property.
+ *
+ * NOTE: this creates an array during each call.
+ *
+ * @return {Array} List of values
+ */
+ getValues() {
+ let len = this.jCal.length - VALUE_INDEX;
+
+ if (len < 1) {
+ // it is possible for a property to have no value.
+ return [];
+ }
+
+ let i = 0;
+ let result = [];
+
+ for (; i < len; i++) {
+ result[i] = this._hydrateValue(i);
+ }
+
+ return result;
+ }
+
+ /**
+ * Removes all values from this property
+ */
+ removeAllValues() {
+ if (this._values) {
+ this._values.length = 0;
+ }
+ this.jCal.length = 3;
+ }
+
+ /**
+ * Sets the values of the property. Will overwrite the existing values.
+ * This can only be used for multi-value properties.
+ *
+ * @param {Array} values An array of values
+ */
+ setValues(values) {
+ if (!this.isMultiValue) {
+ throw new Error(
+ this.name + ': does not not support mulitValue.\n' +
+ 'override isMultiValue'
+ );
+ }
+
+ let len = values.length;
+ let i = 0;
+ this.removeAllValues();
+
+ if (len > 0 &&
+ typeof(values[0]) === 'object' &&
+ 'icaltype' in values[0]) {
+ this.resetType(values[0].icaltype);
+ }
+
+ if (this.isDecorated) {
+ for (; i < len; i++) {
+ this._setDecoratedValue(values[i], i);
+ }
+ } else {
+ for (; i < len; i++) {
+ this.jCal[VALUE_INDEX + i] = values[i];
+ }
+ }
+ }
+
+ /**
+ * Sets the current value of the property. If this is a multi-value
+ * property, all other values will be removed.
+ *
+ * @param {String|Object} value New property value.
+ */
+ setValue(value) {
+ this.removeAllValues();
+ if (typeof(value) === 'object' && 'icaltype' in value) {
+ this.resetType(value.icaltype);
+ }
+
+ if (this.isDecorated) {
+ this._setDecoratedValue(value, 0);
+ } else {
+ this.jCal[VALUE_INDEX] = value;
+ }
+ }
+
+ /**
+ * Returns the Object representation of this component. The returned object
+ * is a live jCal object and should be cloned if modified.
+ * @return {Object}
+ */
+ toJSON() {
+ return this.jCal;
+ }
+
+ /**
+ * The string representation of this component.
+ * @return {String}
+ */
+ toICALString() {
+ return stringify.property(
+ this.jCal, this._designSet, true
+ );
+ }
+}
+
+/* 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 */
+
+
+/**
+ * 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
+ * @typedef {import("./types.js").Geo} Geo
+ * Imports the 'Geo' type from the "types.js" module
+ */
+
+const NAME_INDEX = 0;
+const PROPERTY_INDEX = 1;
+const COMPONENT_INDEX = 2;
+
+const PROPERTY_NAME_INDEX = 0;
+const PROPERTY_VALUE_INDEX = 3;
+
+/**
+ * Wraps a jCal component, adding convenience methods to add, remove and update subcomponents and
+ * properties.
+ *
+ * @memberof ICAL
+ */
+class Component {
+ /**
+ * Create an {@link ICAL.Component} by parsing the passed iCalendar string.
+ *
+ * @param {String} str The iCalendar string to parse
+ */
+ static fromString(str) {
+ return new Component(parse.component(str));
+ }
+
+ /**
+ * Creates a new Component instance.
+ *
+ * @param {Array|String} jCal Raw jCal component data OR name of new
+ * component
+ * @param {Component=} parent Parent component to associate
+ */
+ constructor(jCal, parent) {
+ if (typeof(jCal) === 'string') {
+ // jCal spec (name, properties, components)
+ jCal = [jCal, [], []];
+ }
+
+ // mostly for legacy reasons.
+ this.jCal = jCal;
+
+ this.parent = parent || null;
+
+ if (!this.parent && this.name === 'vcalendar') {
+ this._timezoneCache = new Map();
+ }
+ }
+
+ /**
+ * Hydrated properties are inserted into the _properties array at the same
+ * position as in the jCal array, so it is possible that the array contains
+ * undefined values for unhydrdated properties. To avoid iterating the
+ * array when checking if all properties have been hydrated, we save the
+ * count here.
+ *
+ * @type {Number}
+ * @private
+ */
+ _hydratedPropertyCount = 0;
+
+ /**
+ * The same count as for _hydratedPropertyCount, but for subcomponents
+ *
+ * @type {Number}
+ * @private
+ */
+ _hydratedComponentCount = 0;
+
+ /**
+ * A cache of hydrated time zone objects which may be used by consumers, keyed
+ * by time zone ID.
+ *
+ * @type {Map}
+ * @private
+ */
+ _timezoneCache = null;
+
+ /**
+ * @private
+ */
+ _components = null;
+
+ /**
+ * @private
+ */
+ _properties = null;
+
+ /**
+ * The name of this component
+ *
+ * @type {String}
+ */
+ get name() {
+ return this.jCal[NAME_INDEX];
+ }
+
+ /**
+ * The design set for this component, e.g. icalendar vs vcard
+ *
+ * @type {designSet}
+ * @private
+ */
+ get _designSet() {
+ let parentDesign = this.parent && this.parent._designSet;
+ if (!parentDesign && this.name == "vcard") {
+ // We can't decide on vcard3 vs vcard4 just based on the component name, the version number is
+ // in the version property. We also can't use hydrated properties here because it would lead
+ // to recursion, but the spec says that the version property needs to be the very first one.
+ let versionProp = this.jCal[PROPERTY_INDEX]?.[0];
+
+ if (versionProp && versionProp[PROPERTY_NAME_INDEX] == "version" && versionProp[PROPERTY_VALUE_INDEX] == "3.0") {
+ return design.getDesignSet("vcard3");
+ }
+ }
+
+ return parentDesign || design.getDesignSet(this.name);
+ }
+
+ /**
+ * @private
+ */
+ _hydrateComponent(index) {
+ if (!this._components) {
+ this._components = [];
+ this._hydratedComponentCount = 0;
+ }
+
+ if (this._components[index]) {
+ return this._components[index];
+ }
+
+ let comp = new Component(
+ this.jCal[COMPONENT_INDEX][index],
+ this
+ );
+
+ this._hydratedComponentCount++;
+ return (this._components[index] = comp);
+ }
+
+ /**
+ * @private
+ */
+ _hydrateProperty(index) {
+ if (!this._properties) {
+ this._properties = [];
+ this._hydratedPropertyCount = 0;
+ }
+
+ if (this._properties[index]) {
+ return this._properties[index];
+ }
+
+ let prop = new Property(
+ this.jCal[PROPERTY_INDEX][index],
+ this
+ );
+
+ this._hydratedPropertyCount++;
+ return (this._properties[index] = prop);
+ }
+
+ /**
+ * Finds first sub component, optionally filtered by name.
+ *
+ * @param {String=} name Optional name to filter by
+ * @return {?Component} The found subcomponent
+ */
+ getFirstSubcomponent(name) {
+ if (name) {
+ let i = 0;
+ let comps = this.jCal[COMPONENT_INDEX];
+ let len = comps.length;
+
+ for (; i < len; i++) {
+ if (comps[i][NAME_INDEX] === name) {
+ let result = this._hydrateComponent(i);
+ return result;
+ }
+ }
+ } else {
+ if (this.jCal[COMPONENT_INDEX].length) {
+ return this._hydrateComponent(0);
+ }
+ }
+
+ // ensure we return a value (strict mode)
+ return null;
+ }
+
+ /**
+ * Finds all sub components, optionally filtering by name.
+ *
+ * @param {String=} name Optional name to filter by
+ * @return {Component[]} The found sub components
+ */
+ getAllSubcomponents(name) {
+ let jCalLen = this.jCal[COMPONENT_INDEX].length;
+ let i = 0;
+
+ if (name) {
+ let comps = this.jCal[COMPONENT_INDEX];
+ let result = [];
+
+ for (; i < jCalLen; i++) {
+ if (name === comps[i][NAME_INDEX]) {
+ result.push(
+ this._hydrateComponent(i)
+ );
+ }
+ }
+ return result;
+ } else {
+ if (!this._components ||
+ (this._hydratedComponentCount !== jCalLen)) {
+ for (; i < jCalLen; i++) {
+ this._hydrateComponent(i);
+ }
+ }
+
+ return this._components || [];
+ }
+ }
+
+ /**
+ * Returns true when a named property exists.
+ *
+ * @param {String} name The property name
+ * @return {Boolean} True, when property is found
+ */
+ hasProperty(name) {
+ let props = this.jCal[PROPERTY_INDEX];
+ let len = props.length;
+
+ let i = 0;
+ for (; i < len; i++) {
+ // 0 is property name
+ if (props[i][NAME_INDEX] === name) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Finds the first property, optionally with the given name.
+ *
+ * @param {String=} name Lowercase property name
+ * @return {?Property} The found property
+ */
+ getFirstProperty(name) {
+ if (name) {
+ let i = 0;
+ let props = this.jCal[PROPERTY_INDEX];
+ let len = props.length;
+
+ for (; i < len; i++) {
+ if (props[i][NAME_INDEX] === name) {
+ let result = this._hydrateProperty(i);
+ return result;
+ }
+ }
+ } else {
+ if (this.jCal[PROPERTY_INDEX].length) {
+ return this._hydrateProperty(0);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns first property's value, if available.
+ *
+ * @param {String=} name Lowercase property name
+ * @return {Binary | Duration | Period |
+ * Recur | Time | UtcOffset | Geo | string | null} The found property value.
+ */
+ getFirstPropertyValue(name) {
+ let prop = this.getFirstProperty(name);
+ if (prop) {
+ return prop.getFirstValue();
+ }
+
+ return null;
+ }
+
+ /**
+ * Get all properties in the component, optionally filtered by name.
+ *
+ * @param {String=} name Lowercase property name
+ * @return {Property[]} List of properties
+ */
+ getAllProperties(name) {
+ let jCalLen = this.jCal[PROPERTY_INDEX].length;
+ let i = 0;
+
+ if (name) {
+ let props = this.jCal[PROPERTY_INDEX];
+ let result = [];
+
+ for (; i < jCalLen; i++) {
+ if (name === props[i][NAME_INDEX]) {
+ result.push(
+ this._hydrateProperty(i)
+ );
+ }
+ }
+ return result;
+ } else {
+ if (!this._properties ||
+ (this._hydratedPropertyCount !== jCalLen)) {
+ for (; i < jCalLen; i++) {
+ this._hydrateProperty(i);
+ }
+ }
+
+ return this._properties || [];
+ }
+ }
+
+ /**
+ * @private
+ */
+ _removeObjectByIndex(jCalIndex, cache, index) {
+ cache = cache || [];
+ // remove cached version
+ if (cache[index]) {
+ let obj = cache[index];
+ if ("parent" in obj) {
+ obj.parent = null;
+ }
+ }
+
+ cache.splice(index, 1);
+
+ // remove it from the jCal
+ this.jCal[jCalIndex].splice(index, 1);
+ }
+
+ /**
+ * @private
+ */
+ _removeObject(jCalIndex, cache, nameOrObject) {
+ let i = 0;
+ let objects = this.jCal[jCalIndex];
+ let len = objects.length;
+ let cached = this[cache];
+
+ if (typeof(nameOrObject) === 'string') {
+ for (; i < len; i++) {
+ if (objects[i][NAME_INDEX] === nameOrObject) {
+ this._removeObjectByIndex(jCalIndex, cached, i);
+ return true;
+ }
+ }
+ } else if (cached) {
+ for (; i < len; i++) {
+ if (cached[i] && cached[i] === nameOrObject) {
+ this._removeObjectByIndex(jCalIndex, cached, i);
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @private
+ */
+ _removeAllObjects(jCalIndex, cache, name) {
+ let cached = this[cache];
+
+ // Unfortunately we have to run through all children to reset their
+ // parent property.
+ let objects = this.jCal[jCalIndex];
+ let i = objects.length - 1;
+
+ // descending search required because splice
+ // is used and will effect the indices.
+ for (; i >= 0; i--) {
+ if (!name || objects[i][NAME_INDEX] === name) {
+ this._removeObjectByIndex(jCalIndex, cached, i);
+ }
+ }
+ }
+
+ /**
+ * Adds a single sub component.
+ *
+ * @param {Component} component The component to add
+ * @return {Component} The passed in component
+ */
+ addSubcomponent(component) {
+ if (!this._components) {
+ this._components = [];
+ this._hydratedComponentCount = 0;
+ }
+
+ if (component.parent) {
+ component.parent.removeSubcomponent(component);
+ }
+
+ let idx = this.jCal[COMPONENT_INDEX].push(component.jCal);
+ this._components[idx - 1] = component;
+ this._hydratedComponentCount++;
+ component.parent = this;
+ return component;
+ }
+
+ /**
+ * Removes a single component by name or the instance of a specific
+ * component.
+ *
+ * @param {Component|String} nameOrComp Name of component, or component
+ * @return {Boolean} True when comp is removed
+ */
+ removeSubcomponent(nameOrComp) {
+ let removed = this._removeObject(COMPONENT_INDEX, '_components', nameOrComp);
+ if (removed) {
+ this._hydratedComponentCount--;
+ }
+ return removed;
+ }
+
+ /**
+ * Removes all components or (if given) all components by a particular
+ * name.
+ *
+ * @param {String=} name Lowercase component name
+ */
+ removeAllSubcomponents(name) {
+ let removed = this._removeAllObjects(COMPONENT_INDEX, '_components', name);
+ this._hydratedComponentCount = 0;
+ return removed;
+ }
+
+ /**
+ * Adds an {@link ICAL.Property} to the component.
+ *
+ * @param {Property} property The property to add
+ * @return {Property} The passed in property
+ */
+ addProperty(property) {
+ if (!(property instanceof Property)) {
+ throw new TypeError('must be instance of ICAL.Property');
+ }
+
+ if (!this._properties) {
+ this._properties = [];
+ this._hydratedPropertyCount = 0;
+ }
+
+ if (property.parent) {
+ property.parent.removeProperty(property);
+ }
+
+ let idx = this.jCal[PROPERTY_INDEX].push(property.jCal);
+ this._properties[idx - 1] = property;
+ this._hydratedPropertyCount++;
+ property.parent = this;
+ return property;
+ }
+
+ /**
+ * Helper method to add a property with a value to the component.
+ *
+ * @param {String} name Property name to add
+ * @param {String|Number|Object} value Property value
+ * @return {Property} The created property
+ */
+ addPropertyWithValue(name, value) {
+ let prop = new Property(name);
+ prop.setValue(value);
+
+ this.addProperty(prop);
+
+ return prop;
+ }
+
+ /**
+ * Helper method that will update or create a property of the given name
+ * and sets its value. If multiple properties with the given name exist,
+ * only the first is updated.
+ *
+ * @param {String} name Property name to update
+ * @param {String|Number|Object} value Property value
+ * @return {Property} The created property
+ */
+ updatePropertyWithValue(name, value) {
+ let prop = this.getFirstProperty(name);
+
+ if (prop) {
+ prop.setValue(value);
+ } else {
+ prop = this.addPropertyWithValue(name, value);
+ }
+
+ return prop;
+ }
+
+ /**
+ * Removes a single property by name or the instance of the specific
+ * property.
+ *
+ * @param {String|Property} nameOrProp Property name or instance to remove
+ * @return {Boolean} True, when deleted
+ */
+ removeProperty(nameOrProp) {
+ let removed = this._removeObject(PROPERTY_INDEX, '_properties', nameOrProp);
+ if (removed) {
+ this._hydratedPropertyCount--;
+ }
+ return removed;
+ }
+
+ /**
+ * Removes all properties associated with this component, optionally
+ * filtered by name.
+ *
+ * @param {String=} name Lowercase property name
+ * @return {Boolean} True, when deleted
+ */
+ removeAllProperties(name) {
+ let removed = this._removeAllObjects(PROPERTY_INDEX, '_properties', name);
+ this._hydratedPropertyCount = 0;
+ return removed;
+ }
+
+ /**
+ * Returns the Object representation of this component. The returned object
+ * is a live jCal object and should be cloned if modified.
+ * @return {Object}
+ */
+ toJSON() {
+ return this.jCal;
+ }
+
+ /**
+ * The string representation of this component.
+ * @return {String}
+ */
+ toString() {
+ return stringify.component(
+ this.jCal, this._designSet
+ );
+ }
+
+ /**
+ * Retrieve a time zone definition from the component tree, if any is present.
+ * If the tree contains no time zone definitions or the TZID cannot be
+ * matched, returns null.
+ *
+ * @param {String} tzid The ID of the time zone to retrieve
+ * @return {Timezone} The time zone corresponding to the ID, or null
+ */
+ getTimeZoneByID(tzid) {
+ // VTIMEZONE components can only appear as a child of the VCALENDAR
+ // component; walk the tree if we're not the root.
+ if (this.parent) {
+ return this.parent.getTimeZoneByID(tzid);
+ }
+
+ // If there is no time zone cache, we are probably parsing an incomplete
+ // file and will have no time zone definitions.
+ if (!this._timezoneCache) {
+ return null;
+ }
+
+ if (this._timezoneCache.has(tzid)) {
+ return this._timezoneCache.get(tzid);
+ }
+
+ // If the time zone is not already cached, hydrate it from the
+ // subcomponents.
+ const zones = this.getAllSubcomponents('vtimezone');
+ for (const zone of zones) {
+ if (zone.getFirstProperty('tzid').getFirstValue() === tzid) {
+ const hydratedZone = new Timezone({
+ component: zone,
+ tzid: tzid,
+ });
+
+ this._timezoneCache.set(tzid, hydratedZone);
+
+ return hydratedZone;
+ }
+ }
+
+ // Per the standard, we should always have a time zone defined in a file
+ // for any referenced TZID, but don't blow up if the file is invalid.
+ return null;
+ }
+}
+
+/* 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 */
+
+
+/**
+ * Primary class for expanding recurring rules. Can take multiple rrules, rdates, exdate(s) and
+ * iterate (in order) over each next occurrence.
+ *
+ * Once initialized this class can also be serialized saved and continue iteration from the last
+ * point.
+ *
+ * NOTE: it is intended that this class is to be used with {@link ICAL.Event} which handles recurrence
+ * exceptions.
+ *
+ * @example
+ * // assuming event is a parsed ical component
+ * var event;
+ *
+ * var expand = new ICAL.RecurExpansion({
+ * component: event,
+ * dtstart: event.getFirstPropertyValue('dtstart')
+ * });
+ *
+ * // remember there are infinite rules so it is a good idea to limit the scope of the iterations
+ * // then resume later on.
+ *
+ * // next is always an ICAL.Time or null
+ * var next;
+ *
+ * while (someCondition && (next = expand.next())) {
+ * // do something with next
+ * }
+ *
+ * // save instance for later
+ * var json = JSON.stringify(expand);
+ *
+ * //...
+ *
+ * // NOTE: if the component's properties have changed you will need to rebuild the class and start
+ * // over. This only works when the component's recurrence info is the same.
+ * var expand = new ICAL.RecurExpansion(JSON.parse(json));
+ *
+ * @memberof ICAL
+ */
+class RecurExpansion {
+ /**
+ * Creates a new ICAL.RecurExpansion instance.
+ *
+ * The options object can be filled with the specified initial values. It can also contain
+ * additional members, as a result of serializing a previous expansion state, as shown in the
+ * example.
+ *
+ * @param {Object} options
+ * Recurrence expansion options
+ * @param {Time} options.dtstart
+ * Start time of the event
+ * @param {Component=} options.component
+ * Component for expansion, required if not resuming.
+ */
+ constructor(options) {
+ this.ruleDates = [];
+ this.exDates = [];
+ this.fromData(options);
+ }
+
+ /**
+ * True when iteration is fully completed.
+ * @type {Boolean}
+ */
+ complete = false;
+
+ /**
+ * Array of rrule iterators.
+ *
+ * @type {RecurIterator[]}
+ * @private
+ */
+ ruleIterators = null;
+
+ /**
+ * Array of rdate instances.
+ *
+ * @type {Time[]}
+ * @private
+ */
+ ruleDates = null;
+
+ /**
+ * Array of exdate instances.
+ *
+ * @type {Time[]}
+ * @private
+ */
+ exDates = null;
+
+ /**
+ * Current position in ruleDates array.
+ * @type {Number}
+ * @private
+ */
+ ruleDateInc = 0;
+
+ /**
+ * Current position in exDates array
+ * @type {Number}
+ * @private
+ */
+ exDateInc = 0;
+
+ /**
+ * Current negative date.
+ *
+ * @type {Time}
+ * @private
+ */
+ exDate = null;
+
+ /**
+ * Current additional date.
+ *
+ * @type {Time}
+ * @private
+ */
+ ruleDate = null;
+
+ /**
+ * Start date of recurring rules.
+ *
+ * @type {Time}
+ */
+ dtstart = null;
+
+ /**
+ * Last expanded time
+ *
+ * @type {Time}
+ */
+ last = null;
+
+ /**
+ * Initialize the recurrence expansion from the data object. The options
+ * object may also contain additional members, see the
+ * {@link ICAL.RecurExpansion constructor} for more details.
+ *
+ * @param {Object} options
+ * Recurrence expansion options
+ * @param {Time} options.dtstart
+ * Start time of the event
+ * @param {Component=} options.component
+ * Component for expansion, required if not resuming.
+ */
+ fromData(options) {
+ let start = formatClassType(options.dtstart, Time);
+
+ if (!start) {
+ throw new Error('.dtstart (ICAL.Time) must be given');
+ } else {
+ this.dtstart = start;
+ }
+
+ if (options.component) {
+ this._init(options.component);
+ } else {
+ this.last = formatClassType(options.last, Time) || start.clone();
+
+ if (!options.ruleIterators) {
+ throw new Error('.ruleIterators or .component must be given');
+ }
+
+ this.ruleIterators = options.ruleIterators.map(function(item) {
+ return formatClassType(item, RecurIterator);
+ });
+
+ this.ruleDateInc = options.ruleDateInc;
+ this.exDateInc = options.exDateInc;
+
+ if (options.ruleDates) {
+ this.ruleDates = options.ruleDates.map(item => formatClassType(item, Time));
+ this.ruleDate = this.ruleDates[this.ruleDateInc];
+ }
+
+ if (options.exDates) {
+ this.exDates = options.exDates.map(item => formatClassType(item, Time));
+ this.exDate = this.exDates[this.exDateInc];
+ }
+
+ if (typeof(options.complete) !== 'undefined') {
+ this.complete = options.complete;
+ }
+ }
+ }
+
+ /**
+ * Compare two ICAL.Time objects. When the second parameter is a DATE and the first parameter is
+ * DATE-TIME, strip the time and compare only the days.
+ *
+ * @private
+ * @param {Time} a The one object to compare
+ * @param {Time} b The other object to compare
+ */
+ _compare_special(a, b) {
+ if (!a.isDate && b.isDate)
+ return new Time({ year: a.year, month: a.month, day: a.day }).compare(b);
+ return a.compare(b);
+ }
+
+ /**
+ * Retrieve the next occurrence in the series.
+ * @return {Time}
+ */
+ next() {
+ let iter;
+ let next;
+ let compare;
+
+ let maxTries = 500;
+ let currentTry = 0;
+
+ while (true) {
+ if (currentTry++ > maxTries) {
+ throw new Error(
+ 'max tries have occurred, rule may be impossible to fulfill.'
+ );
+ }
+
+ next = this.ruleDate;
+ iter = this._nextRecurrenceIter(this.last);
+
+ // no more matches
+ // because we increment the rule day or rule
+ // _after_ we choose a value this should be
+ // the only spot where we need to worry about the
+ // end of events.
+ if (!next && !iter) {
+ // there are no more iterators or rdates
+ this.complete = true;
+ break;
+ }
+
+ // no next rule day or recurrence rule is first.
+ if (!next || (iter && next.compare(iter.last) > 0)) {
+ // must be cloned, recur will reuse the time element.
+ next = iter.last.clone();
+ // move to next so we can continue
+ iter.next();
+ }
+
+ // if the ruleDate is still next increment it.
+ if (this.ruleDate === next) {
+ this._nextRuleDay();
+ }
+
+ this.last = next;
+
+ // check the negative rules
+ if (this.exDate) {
+ // EXDATE can be in DATE format, but DTSTART is in DATE-TIME format
+ compare = this._compare_special(this.last, this.exDate);
+
+ if (compare > 0) {
+ this._nextExDay();
+ }
+
+ // if the current rule is excluded skip it.
+ if (compare === 0) {
+ this._nextExDay();
+ continue;
+ }
+ }
+
+ //XXX: The spec states that after we resolve the final
+ // list of dates we execute exdate this seems somewhat counter
+ // intuitive to what I have seen most servers do so for now
+ // I exclude based on the original date not the one that may
+ // have been modified by the exception.
+ return this.last;
+ }
+ }
+
+ /**
+ * Converts object into a serialize-able format. This format can be passed
+ * back into the expansion to resume iteration.
+ * @return {Object}
+ */
+ toJSON() {
+ function toJSON(item) {
+ return item.toJSON();
+ }
+
+ let result = Object.create(null);
+ result.ruleIterators = this.ruleIterators.map(toJSON);
+
+ if (this.ruleDates) {
+ result.ruleDates = this.ruleDates.map(toJSON);
+ }
+
+ if (this.exDates) {
+ result.exDates = this.exDates.map(toJSON);
+ }
+
+ result.ruleDateInc = this.ruleDateInc;
+ result.exDateInc = this.exDateInc;
+ result.last = this.last.toJSON();
+ result.dtstart = this.dtstart.toJSON();
+ result.complete = this.complete;
+
+ return result;
+ }
+
+ /**
+ * Extract all dates from the properties in the given component. The
+ * properties will be filtered by the property name.
+ *
+ * @private
+ * @param {Component} component The component to search in
+ * @param {String} propertyName The property name to search for
+ * @return {Time[]} The extracted dates.
+ */
+ _extractDates(component, propertyName) {
+ let result = [];
+ let props = component.getAllProperties(propertyName);
+
+ for (let i = 0, len = props.length; i < len; i++) {
+ for (let prop of props[i].getValues()) {
+ let idx = binsearchInsert(
+ result,
+ prop,
+ (a, b) => a.compare(b)
+ );
+
+ // ordered insert
+ result.splice(idx, 0, prop);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Initialize the recurrence expansion.
+ *
+ * @private
+ * @param {Component} component The component to initialize from.
+ */
+ _init(component) {
+ this.ruleIterators = [];
+
+ this.last = this.dtstart.clone();
+
+ // to provide api consistency non-recurring
+ // events can also use the iterator though it will
+ // only return a single time.
+ if (!component.hasProperty('rdate') &&
+ !component.hasProperty('rrule') &&
+ !component.hasProperty('recurrence-id')) {
+ this.ruleDate = this.last.clone();
+ this.complete = true;
+ return;
+ }
+
+ if (component.hasProperty('rdate')) {
+ this.ruleDates = this._extractDates(component, 'rdate');
+
+ // special hack for cases where first rdate is prior
+ // to the start date. We only check for the first rdate.
+ // This is mostly for google's crazy recurring date logic
+ // (contacts birthdays).
+ if ((this.ruleDates[0]) &&
+ (this.ruleDates[0].compare(this.dtstart) < 0)) {
+
+ this.ruleDateInc = 0;
+ this.last = this.ruleDates[0].clone();
+ } else {
+ this.ruleDateInc = binsearchInsert(
+ this.ruleDates,
+ this.last,
+ (a, b) => a.compare(b)
+ );
+ }
+
+ this.ruleDate = this.ruleDates[this.ruleDateInc];
+ }
+
+ if (component.hasProperty('rrule')) {
+ let rules = component.getAllProperties('rrule');
+ let i = 0;
+ let len = rules.length;
+
+ let rule;
+ let iter;
+
+ for (; i < len; i++) {
+ rule = rules[i].getFirstValue();
+ iter = rule.iterator(this.dtstart);
+ this.ruleIterators.push(iter);
+
+ // increment to the next occurrence so future
+ // calls to next return times beyond the initial iteration.
+ // XXX: I find this suspicious might be a bug?
+ iter.next();
+ }
+ }
+
+ if (component.hasProperty('exdate')) {
+ this.exDates = this._extractDates(component, 'exdate');
+ // if we have a .last day we increment the index to beyond it.
+ // When DTSTART is in DATE-TIME format, EXDATE is in DATE format and EXDATE is
+ // the date of DTSTART, _compare_special finds this out and compareTime fails.
+ this.exDateInc = binsearchInsert(
+ this.exDates,
+ this.last,
+ this._compare_special
+ );
+
+ this.exDate = this.exDates[this.exDateInc];
+ }
+ }
+
+ /**
+ * Advance to the next exdate
+ * @private
+ */
+ _nextExDay() {
+ this.exDate = this.exDates[++this.exDateInc];
+ }
+
+ /**
+ * Advance to the next rule date
+ * @private
+ */
+ _nextRuleDay() {
+ this.ruleDate = this.ruleDates[++this.ruleDateInc];
+ }
+
+ /**
+ * Find and return the recurrence rule with the most recent event and
+ * return it.
+ *
+ * @private
+ * @return {?RecurIterator} Found iterator.
+ */
+ _nextRecurrenceIter() {
+ let iters = this.ruleIterators;
+
+ if (iters.length === 0) {
+ return null;
+ }
+
+ let len = iters.length;
+ let iter;
+ let iterTime;
+ let iterIdx = 0;
+ let chosenIter;
+
+ // loop through each iterator
+ for (; iterIdx < len; iterIdx++) {
+ iter = iters[iterIdx];
+ iterTime = iter.last;
+
+ // if iteration is complete
+ // then we must exclude it from
+ // the search and remove it.
+ if (iter.completed) {
+ len--;
+ if (iterIdx !== 0) {
+ iterIdx--;
+ }
+ iters.splice(iterIdx, 1);
+ continue;
+ }
+
+ // find the most recent possible choice
+ if (!chosenIter || chosenIter.last.compare(iterTime) > 0) {
+ // that iterator is saved
+ chosenIter = iter;
+ }
+ }
+
+ // the chosen iterator is returned but not mutated
+ // this iterator contains the most recent event.
+ return chosenIter;
+ }
+}
+
+/* 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 */
+
+
+/**
+ * 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").frequencyValues} frequencyValues
+ * Imports the 'frequencyValues' type from the "types.js" module
+ * @typedef {import("./types.js").occurrenceDetails} occurrenceDetails
+ * Imports the 'occurrenceDetails' type from the "types.js" module
+ */
+
+/**
+ * ICAL.js is organized into multiple layers. The bottom layer is a raw jCal
+ * object, followed by the component/property layer. The highest level is the
+ * event representation, which this class is part of. See the
+ * {@tutorial layers} guide for more details.
+ *
+ * @memberof ICAL
+ */
+class Event {
+ /**
+ * Creates a new ICAL.Event instance.
+ *
+ * @param {Component=} component The ICAL.Component to base this event on
+ * @param {Object} [options] Options for this event
+ * @param {Boolean=} options.strictExceptions When true, will verify exceptions are related by
+ * their UUID
+ * @param {Array=} options.exceptions
+ * Exceptions to this event, either as components or events. If not
+ * specified exceptions will automatically be set in relation of
+ * component's parent
+ */
+ constructor(component, options) {
+ if (!(component instanceof Component)) {
+ options = component;
+ component = null;
+ }
+
+ if (component) {
+ this.component = component;
+ } else {
+ this.component = new Component('vevent');
+ }
+
+ this._rangeExceptionCache = Object.create(null);
+ this.exceptions = Object.create(null);
+ this.rangeExceptions = [];
+
+ if (options && options.strictExceptions) {
+ this.strictExceptions = options.strictExceptions;
+ }
+
+ if (options && options.exceptions) {
+ options.exceptions.forEach(this.relateException, this);
+ } else if (this.component.parent && !this.isRecurrenceException()) {
+ this.component.parent.getAllSubcomponents('vevent').forEach(function(event) {
+ if (event.hasProperty('recurrence-id')) {
+ this.relateException(event);
+ }
+ }, this);
+ }
+ }
+
+
+ static THISANDFUTURE = 'THISANDFUTURE';
+
+ /**
+ * List of related event exceptions.
+ *
+ * @type {Event[]}
+ */
+ exceptions = null;
+
+ /**
+ * When true, will verify exceptions are related by their UUID.
+ *
+ * @type {Boolean}
+ */
+ strictExceptions = false;
+
+ /**
+ * Relates a given event exception to this object. If the given component
+ * does not share the UID of this event it cannot be related and will throw
+ * an exception.
+ *
+ * If this component is an exception it cannot have other exceptions
+ * related to it.
+ *
+ * @param {Component|Event} obj Component or event
+ */
+ relateException(obj) {
+ if (this.isRecurrenceException()) {
+ throw new Error('cannot relate exception to exceptions');
+ }
+
+ if (obj instanceof Component) {
+ obj = new Event(obj);
+ }
+
+ if (this.strictExceptions && obj.uid !== this.uid) {
+ throw new Error('attempted to relate unrelated exception');
+ }
+
+ let id = obj.recurrenceId.toString();
+
+ // we don't sort or manage exceptions directly
+ // here the recurrence expander handles that.
+ this.exceptions[id] = obj;
+
+ // index RANGE=THISANDFUTURE exceptions so we can
+ // look them up later in getOccurrenceDetails.
+ if (obj.modifiesFuture()) {
+ let item = [
+ obj.recurrenceId.toUnixTime(), id
+ ];
+
+ // we keep them sorted so we can find the nearest
+ // value later on...
+ let idx = binsearchInsert(
+ this.rangeExceptions,
+ item,
+ compareRangeException
+ );
+
+ this.rangeExceptions.splice(idx, 0, item);
+ }
+ }
+
+ /**
+ * Checks if this record is an exception and has the RANGE=THISANDFUTURE
+ * value.
+ *
+ * @return {Boolean} True, when exception is within range
+ */
+ modifiesFuture() {
+ if (!this.component.hasProperty('recurrence-id')) {
+ return false;
+ }
+
+ let range = this.component.getFirstProperty('recurrence-id').getParameter('range');
+ return range === Event.THISANDFUTURE;
+ }
+
+ /**
+ * Finds the range exception nearest to the given date.
+ *
+ * @param {Time} time usually an occurrence time of an event
+ * @return {?Event} the related event/exception or null
+ */
+ findRangeException(time) {
+ if (!this.rangeExceptions.length) {
+ return null;
+ }
+
+ let utc = time.toUnixTime();
+ let idx = binsearchInsert(
+ this.rangeExceptions,
+ [utc],
+ compareRangeException
+ );
+
+ idx -= 1;
+
+ // occurs before
+ if (idx < 0) {
+ return null;
+ }
+
+ let rangeItem = this.rangeExceptions[idx];
+
+ /* c8 ignore next 4 */
+ if (utc < rangeItem[0]) {
+ // sanity check only
+ return null;
+ }
+
+ return rangeItem[1];
+ }
+
+ /**
+ * Returns the occurrence details based on its start time. If the
+ * occurrence has an exception will return the details for that exception.
+ *
+ * NOTE: this method is intend to be used in conjunction
+ * with the {@link ICAL.Event#iterator iterator} method.
+ *
+ * @param {Time} occurrence time occurrence
+ * @return {occurrenceDetails} Information about the occurrence
+ */
+ getOccurrenceDetails(occurrence) {
+ let id = occurrence.toString();
+ let utcId = occurrence.convertToZone(Timezone.utcTimezone).toString();
+ let item;
+ let result = {
+ //XXX: Clone?
+ recurrenceId: occurrence
+ };
+
+ if (id in this.exceptions) {
+ item = result.item = this.exceptions[id];
+ result.startDate = item.startDate;
+ result.endDate = item.endDate;
+ result.item = item;
+ } else if (utcId in this.exceptions) {
+ item = this.exceptions[utcId];
+ result.startDate = item.startDate;
+ result.endDate = item.endDate;
+ result.item = item;
+ } else {
+ // range exceptions (RANGE=THISANDFUTURE) have a
+ // lower priority then direct exceptions but
+ // must be accounted for first. Their item is
+ // always the first exception with the range prop.
+ let rangeExceptionId = this.findRangeException(
+ occurrence
+ );
+ let end;
+
+ if (rangeExceptionId) {
+ let exception = this.exceptions[rangeExceptionId];
+
+ // range exception must modify standard time
+ // by the difference (if any) in start/end times.
+ result.item = exception;
+
+ let startDiff = this._rangeExceptionCache[rangeExceptionId];
+
+ if (!startDiff) {
+ let original = exception.recurrenceId.clone();
+ let newStart = exception.startDate.clone();
+
+ // zones must be same otherwise subtract may be incorrect.
+ original.zone = newStart.zone;
+ startDiff = newStart.subtractDate(original);
+
+ this._rangeExceptionCache[rangeExceptionId] = startDiff;
+ }
+
+ let start = occurrence.clone();
+ start.zone = exception.startDate.zone;
+ start.addDuration(startDiff);
+
+ end = start.clone();
+ end.addDuration(exception.duration);
+
+ result.startDate = start;
+ result.endDate = end;
+ } else {
+ // no range exception standard expansion
+ end = occurrence.clone();
+ end.addDuration(this.duration);
+
+ result.endDate = end;
+ result.startDate = occurrence;
+ result.item = this;
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Builds a recur expansion instance for a specific point in time (defaults
+ * to startDate).
+ *
+ * @param {Time=} startTime Starting point for expansion
+ * @return {RecurExpansion} Expansion object
+ */
+ iterator(startTime) {
+ return new RecurExpansion({
+ component: this.component,
+ dtstart: startTime || this.startDate
+ });
+ }
+
+ /**
+ * Checks if the event is recurring
+ *
+ * @return {Boolean} True, if event is recurring
+ */
+ isRecurring() {
+ let comp = this.component;
+ return comp.hasProperty('rrule') || comp.hasProperty('rdate');
+ }
+
+ /**
+ * Checks if the event describes a recurrence exception. See
+ * {@tutorial terminology} for details.
+ *
+ * @return {Boolean} True, if the event describes a recurrence exception
+ */
+ isRecurrenceException() {
+ return this.component.hasProperty('recurrence-id');
+ }
+
+ /**
+ * Returns the types of recurrences this event may have.
+ *
+ * Returned as an object with the following possible keys:
+ *
+ * - YEARLY
+ * - MONTHLY
+ * - WEEKLY
+ * - DAILY
+ * - MINUTELY
+ * - SECONDLY
+ *
+ * @return {Object.}
+ * Object of recurrence flags
+ */
+ getRecurrenceTypes() {
+ let rules = this.component.getAllProperties('rrule');
+ let i = 0;
+ let len = rules.length;
+ let result = Object.create(null);
+
+ for (; i < len; i++) {
+ let value = rules[i].getFirstValue();
+ result[value.freq] = true;
+ }
+
+ return result;
+ }
+
+ /**
+ * The uid of this event
+ * @type {String}
+ */
+ get uid() {
+ return this._firstProp('uid');
+ }
+
+ set uid(value) {
+ this._setProp('uid', value);
+ }
+
+ /**
+ * The start date
+ * @type {Time}
+ */
+ get startDate() {
+ return this._firstProp('dtstart');
+ }
+
+ set startDate(value) {
+ this._setTime('dtstart', value);
+ }
+
+ /**
+ * The end date. This can be the result directly from the property, or the
+ * end date calculated from start date and duration. Setting the property
+ * will remove any duration properties.
+ * @type {Time}
+ */
+ get endDate() {
+ let endDate = this._firstProp('dtend');
+ if (!endDate) {
+ let duration = this._firstProp('duration');
+ endDate = this.startDate.clone();
+ if (duration) {
+ endDate.addDuration(duration);
+ } else if (endDate.isDate) {
+ endDate.day += 1;
+ }
+ }
+ return endDate;
+ }
+
+ set endDate(value) {
+ if (this.component.hasProperty('duration')) {
+ this.component.removeProperty('duration');
+ }
+ this._setTime('dtend', value);
+ }
+
+ /**
+ * The duration. This can be the result directly from the property, or the
+ * duration calculated from start date and end date. Setting the property
+ * will remove any `dtend` properties.
+ * @type {Duration}
+ */
+ get duration() {
+ let duration = this._firstProp('duration');
+ if (!duration) {
+ return this.endDate.subtractDateTz(this.startDate);
+ }
+ return duration;
+ }
+
+ set duration(value) {
+ if (this.component.hasProperty('dtend')) {
+ this.component.removeProperty('dtend');
+ }
+
+ this._setProp('duration', value);
+ }
+
+ /**
+ * The location of the event.
+ * @type {String}
+ */
+ get location() {
+ return this._firstProp('location');
+ }
+
+ set location(value) {
+ this._setProp('location', value);
+ }
+
+ /**
+ * The attendees in the event
+ * @type {Property[]}
+ */
+ get attendees() {
+ //XXX: This is way lame we should have a better
+ // data structure for this later.
+ return this.component.getAllProperties('attendee');
+ }
+
+ /**
+ * The event summary
+ * @type {String}
+ */
+ get summary() {
+ return this._firstProp('summary');
+ }
+
+ set summary(value) {
+ this._setProp('summary', value);
+ }
+
+ /**
+ * The event description.
+ * @type {String}
+ */
+ get description() {
+ return this._firstProp('description');
+ }
+
+ set description(value) {
+ this._setProp('description', value);
+ }
+
+ /**
+ * The event color from [rfc7986](https://datatracker.ietf.org/doc/html/rfc7986)
+ * @type {String}
+ */
+ get color() {
+ return this._firstProp('color');
+ }
+
+ set color(value) {
+ this._setProp('color', value);
+ }
+
+ /**
+ * The organizer value as an uri. In most cases this is a mailto: uri, but
+ * it can also be something else, like urn:uuid:...
+ * @type {String}
+ */
+ get organizer() {
+ return this._firstProp('organizer');
+ }
+
+ set organizer(value) {
+ this._setProp('organizer', value);
+ }
+
+ /**
+ * The sequence value for this event. Used for scheduling
+ * see {@tutorial terminology}.
+ * @type {Number}
+ */
+ get sequence() {
+ return this._firstProp('sequence');
+ }
+
+ set sequence(value) {
+ this._setProp('sequence', value);
+ }
+
+ /**
+ * The recurrence id for this event. See {@tutorial terminology} for details.
+ * @type {Time}
+ */
+ get recurrenceId() {
+ return this._firstProp('recurrence-id');
+ }
+
+ set recurrenceId(value) {
+ this._setTime('recurrence-id', value);
+ }
+
+ /**
+ * Set/update a time property's value.
+ * This will also update the TZID of the property.
+ *
+ * TODO: this method handles the case where we are switching
+ * from a known timezone to an implied timezone (one without TZID).
+ * This does _not_ handle the case of moving between a known
+ * (by TimezoneService) timezone to an unknown timezone...
+ *
+ * We will not add/remove/update the VTIMEZONE subcomponents
+ * leading to invalid ICAL data...
+ * @private
+ * @param {String} propName The property name
+ * @param {Time} time The time to set
+ */
+ _setTime(propName, time) {
+ let prop = this.component.getFirstProperty(propName);
+
+ if (!prop) {
+ prop = new Property(propName);
+ this.component.addProperty(prop);
+ }
+
+ // utc and local don't get a tzid
+ if (
+ time.zone === Timezone.localTimezone ||
+ time.zone === Timezone.utcTimezone
+ ) {
+ // remove the tzid
+ prop.removeParameter('tzid');
+ } else {
+ prop.setParameter('tzid', time.zone.tzid);
+ }
+
+ prop.setValue(time);
+ }
+
+ _setProp(name, value) {
+ this.component.updatePropertyWithValue(name, value);
+ }
+
+ _firstProp(name) {
+ return this.component.getFirstPropertyValue(name);
+ }
+
+ /**
+ * The string representation of this event.
+ * @return {String}
+ */
+ toString() {
+ return this.component.toString();
+ }
+}
+
+function compareRangeException(a, b) {
+ if (a[0] > b[0]) return 1;
+ if (b[0] > a[0]) return -1;
+ return 0;
+}
+
+/* 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 */
+
+
+/**
+ * The ComponentParser is used to process a String or jCal Object,
+ * firing callbacks for various found components, as well as completion.
+ *
+ * @example
+ * var options = {
+ * // when false no events will be emitted for type
+ * parseEvent: true,
+ * parseTimezone: true
+ * };
+ *
+ * var parser = new ICAL.ComponentParser(options);
+ *
+ * parser.onevent(eventComponent) {
+ * //...
+ * }
+ *
+ * // ontimezone, etc...
+ *
+ * parser.oncomplete = function() {
+ *
+ * };
+ *
+ * parser.process(stringOrComponent);
+ *
+ * @memberof ICAL
+ */
+class ComponentParser {
+ /**
+ * Creates a new ICAL.ComponentParser instance.
+ *
+ * @param {Object=} options Component parser options
+ * @param {Boolean} options.parseEvent Whether events should be parsed
+ * @param {Boolean} options.parseTimezeone Whether timezones should be parsed
+ */
+ constructor(options) {
+ if (typeof(options) === 'undefined') {
+ options = {};
+ }
+
+ for (let [key, value] of Object.entries(options)) {
+ this[key] = value;
+ }
+ }
+
+ /**
+ * When true, parse events
+ *
+ * @type {Boolean}
+ */
+ parseEvent = true;
+
+ /**
+ * When true, parse timezones
+ *
+ * @type {Boolean}
+ */
+ parseTimezone = true;
+
+
+ /* SAX like events here for reference */
+
+ /**
+ * Fired when parsing is complete
+ * @callback
+ */
+ oncomplete = /* c8 ignore next */ function() {};
+
+ /**
+ * Fired if an error occurs during parsing.
+ *
+ * @callback
+ * @param {Error} err details of error
+ */
+ onerror = /* c8 ignore next */ function(err) {};
+
+ /**
+ * Fired when a top level component (VTIMEZONE) is found
+ *
+ * @callback
+ * @param {Timezone} component Timezone object
+ */
+ ontimezone = /* c8 ignore next */ function(component) {};
+
+ /**
+ * Fired when a top level component (VEVENT) is found.
+ *
+ * @callback
+ * @param {Event} component Top level component
+ */
+ onevent = /* c8 ignore next */ function(component) {};
+
+ /**
+ * Process a string or parse ical object. This function itself will return
+ * nothing but will start the parsing process.
+ *
+ * Events must be registered prior to calling this method.
+ *
+ * @param {Component|String|Object} ical The component to process,
+ * either in its final form, as a jCal Object, or string representation
+ */
+ process(ical) {
+ //TODO: this is sync now in the future we will have a incremental parser.
+ if (typeof(ical) === 'string') {
+ ical = parse(ical);
+ }
+
+ if (!(ical instanceof Component)) {
+ ical = new Component(ical);
+ }
+
+ let components = ical.getAllSubcomponents();
+ let i = 0;
+ let len = components.length;
+ let component;
+
+ for (; i < len; i++) {
+ component = components[i];
+
+ switch (component.name) {
+ case 'vtimezone':
+ if (this.parseTimezone) {
+ let tzid = component.getFirstPropertyValue('tzid');
+ if (tzid) {
+ this.ontimezone(new Timezone({
+ tzid: tzid,
+ component: component
+ }));
+ }
+ }
+ break;
+ case 'vevent':
+ if (this.parseEvent) {
+ this.onevent(new Event(component));
+ }
+ break;
+ default:
+ continue;
+ }
+ }
+
+ //XXX: ideally we should do a "nextTick" here
+ // so in all cases this is actually async.
+ this.oncomplete();
+ }
+}
+
+/* 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 */
+
+
+/**
+ * The main ICAL module. Provides access to everything else.
+ *
+ * @alias ICAL
+ * @namespace ICAL
+ * @property {ICAL.design} design
+ * @property {ICAL.helpers} helpers
+ */
+var ICALmodule = {
+ /**
+ * The number of characters before iCalendar line folding should occur
+ * @type {Number}
+ * @default 75
+ */
+ foldLength: 75,
+
+ debug: false,
+
+ /**
+ * The character(s) to be used for a newline. The default value is provided by
+ * rfc5545.
+ * @type {String}
+ * @default "\r\n"
+ */
+ newLineChar: '\r\n',
+
+ Binary,
+ Component,
+ ComponentParser,
+ Duration,
+ Event,
+ Period,
+ Property,
+ Recur,
+ RecurExpansion,
+ RecurIterator,
+ Time,
+ Timezone,
+ TimezoneService,
+ UtcOffset,
+ VCardTime,
+
+ parse,
+ stringify,
+
+ design,
+ helpers
+};
+
+export { ICALmodule as default };
diff --git a/cron/scripts/node_modules/ical.js/dist/ical.min.js b/cron/scripts/node_modules/ical.js/dist/ical.min.js
new file mode 100644
index 0000000..2153348
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/ical.min.js
@@ -0,0 +1,5 @@
+/* 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 */
+class t{static fromString(e){return new t(e)}constructor(t){this.value=t}icaltype="binary";decodeValue(){return this._b64_decode(this.value)}setEncodedValue(t){this.value=this._b64_encode(t)}_b64_encode(t){let e,i,r,n,s,a,o,l,h="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",u=0,c=0,d="",m=[];if(!t)return t;do{e=t.charCodeAt(u++),i=t.charCodeAt(u++),r=t.charCodeAt(u++),l=e<<16|i<<8|r,n=l>>18&63,s=l>>12&63,a=l>>6&63,o=63&l,m[c++]=h.charAt(n)+h.charAt(s)+h.charAt(a)+h.charAt(o)}while(u>16&255,i=l>>8&255,r=255&l,m[c++]=64==a?String.fromCharCode(e):64==o?String.fromCharCode(e,i):String.fromCharCode(e,i,r)}while(ui)-(e0?-1:0}toString(){return this.start+"/"+(this.end||this.duration)}toJSON(){return[this.start.toString(),(this.end||this.duration).toString()]}toICALString(){return this.start.toICALString()+"/"+(this.end||this.duration).toICALString()}}class a{static _dowCache={};static _wnCache={};static daysInMonth(t,e){let i=30;return t<1||t>12||(i=[0,31,28,31,30,31,30,31,31,30,31,30,31][t],2==t&&(i+=a.isLeapYear(e))),i}static isLeapYear(t){return t<=1752?t%4==0:t%4==0&&t%100!=0||t%400==0}static fromDayOfYear(t,e){let i=e,r=t,n=new a;n.auto_normalize=!1;let s=a.isLeapYear(i)?1:0;if(r<1)return i--,s=a.isLeapYear(i)?1:0,r+=a.daysInYearPassedMonth[s][12],a.fromDayOfYear(r,i);if(r>a.daysInYearPassedMonth[s][12])return s=a.isLeapYear(i)?1:0,r-=a.daysInYearPassedMonth[s][12],i++,a.fromDayOfYear(r,i);n.year=i,n.isDate=!0;for(let t=11;t>=0;t--)if(r>a.daysInYearPassedMonth[s][t]){n.month=t+1,n.day=r-a.daysInYearPassedMonth[s][t];break}return n.auto_normalize=!0,n}static fromStringv2(t){return new a({year:parseInt(t.slice(0,4),10),month:parseInt(t.slice(5,7),10),day:parseInt(t.slice(8,10),10),isDate:!0})}static fromDateString(t){return new a({year:_(t.slice(0,4)),month:_(t.slice(5,7)),day:_(t.slice(8,10)),isDate:!0})}static fromDateTimeString(t,e){if(t.length<19)throw new Error('invalid date-time value: "'+t+'"');let i,r;"Z"===t.slice(-1)?i=m.utcTimezone:e&&(r=e.getParameter("tzid"),e.parent&&("standard"===e.parent.name||"daylight"===e.parent.name?i=m.localTimezone:r&&(i=e.parent.getTimeZoneByID(r))));const n={year:_(t.slice(0,4)),month:_(t.slice(5,7)),day:_(t.slice(8,10)),hour:_(t.slice(11,13)),minute:_(t.slice(14,16)),second:_(t.slice(17,19))};return r&&!i&&(n.timezone=r),new a(n,i)}static fromString(t,e){return t.length>10?a.fromDateTimeString(t,e):a.fromDateString(t)}static fromJSDate(t,e){return(new a).fromJSDate(t,e)}static fromData=function(t,e){return(new a).fromData(t,e)};static now(){return a.fromJSDate(new Date,!1)}static weekOneStarts(t,e){let i=a.fromData({year:t,month:1,day:1,isDate:!0}),r=i.dayOfWeek(),n=e||a.DEFAULT_WEEK_START;return r>a.THURSDAY&&(i.day+=7),n>a.THURSDAY&&(i.day-=7),i.day-=r-n,i}static getDominicalLetter(t){let e="GFEDCBA",i=(t+(t/4|0)+(t/400|0)-(t/100|0)-1)%7;return a.isLeapYear(t)?e[(i+6)%7]+e[i]:e[i]}static#t=null;static get epochTime(){return this.#t||(this.#t=a.fromData({year:1970,month:1,day:1,hour:0,minute:0,second:0,isDate:!1,timezone:"Z"})),this.#t}static _cmp_attr(t,e,i){return t[i]>e[i]?1:t[i]=0){o.day=1,0!=n&&n--,s=o.day;let e=t-o.dayOfWeek();e<0&&(e+=7),s+=e,s-=t,i=t}else{o.day=r,n++,i=o.dayOfWeek()-t,i<0&&(i+=7),i=r-i}return i+=7*n,s+i}isNthWeekDay(t,e){let i=this.dayOfWeek();return 0===e&&i===t||this.nthWeekDay(t,e)===this.day}weekNumber(t){let e,i=(this.year<<12)+(this.month<<8)+(this.day<<3)+t;if(i in a._wnCache)return a._wnCache[i];let r=this.clone();r.isDate=!0;let n=this.year;12==r.month&&r.day>25?(e=a.weekOneStarts(n+1,t),r.compare(e)<0?e=a.weekOneStarts(n,t):n++):(e=a.weekOneStarts(n,t),r.compare(e)<0&&(e=a.weekOneStarts(--n,t)));let s=b(r.subtractDate(e).toSeconds()/86400/7)+1;return a._wnCache[i]=s,s}addDuration(t){let e=t.isNegative?-1:1,i=this.second,r=this.minute,n=this.hour,s=this.day;i+=e*t.seconds,r+=e*t.minutes,n+=e*t.hours,s+=e*t.days,s+=7*e*t.weeks,this.second=i,this.minute=r,this.hour=n,this.day=s,this._cachedUnixTime=null}subtractDate(t){let e=this.toUnixTime()+this.utcOffset(),i=t.toUnixTime()+t.utcOffset();return r.fromSeconds(e-i)}subtractDateTz(t){let e=this.toUnixTime(),i=t.toUnixTime();return r.fromSeconds(e-i)}compare(t){if(t instanceof s)return-1*t.compare(this);{let e=this.toUnixTime(),i=t.toUnixTime();return e>i?1:i>e?-1:0}}compareDateOnlyTz(t,e){let i=this.convertToZone(e),r=t.convertToZone(e),n=0;return 0!=(n=a._cmp_attr(i,r,"year"))||0!=(n=a._cmp_attr(i,r,"month"))||(n=a._cmp_attr(i,r,"day")),n}convertToZone(t){let e=this.clone(),i=this.zone.tzid==t.tzid;return this.isDate||i||m.convert_time(e,this.zone,t),e.zone=t,e}utcOffset(){return this.zone==m.localTimezone||this.zone==m.utcTimezone?0:this.zone.utcOffset(this)}toICALString(){let t=this.toString();return t.length>10?ct.icalendar.value["date-time"].toICAL(t):ct.icalendar.value.date.toICAL(t)}toString(){let t=this.year+"-"+O(this.month)+"-"+O(this.day);return this.isDate||(t+="T"+O(this.hour)+":"+O(this.minute)+":"+O(this.second),this.zone===m.utcTimezone&&(t+="Z")),t}toJSDate(){return this.zone==m.localTimezone?this.isDate?new Date(this.year,this.month-1,this.day):new Date(this.year,this.month-1,this.day,this.hour,this.minute,this.second,0):new Date(1e3*this.toUnixTime())}_normalize(){return this._time.isDate&&(this._time.hour=0,this._time.minute=0,this._time.second=0),this.adjust(0,0,0,0),this}adjust(t,e,i,r,n){let s,o,l,h,u,c,d,m=0,f=0,p=n||this._time;if(p.isDate||(l=p.second+r,p.second=l%60,s=b(l/60),p.second<0&&(p.second+=60,s--),h=p.minute+i+s,p.minute=h%60,o=b(h/60),p.minute<0&&(p.minute+=60,o--),u=p.hour+e+o,p.hour=u%24,m=b(u/24),p.hour<0&&(p.hour+=24,m--)),p.month>12?f=b((p.month-1)/12):p.month<1&&(f=b(p.month/12)-1),p.year+=f,p.month-=12*f,c=p.day+t+m,c>0)for(;d=a.daysInMonth(p.month,p.year),!(c<=d);)p.month++,p.month>12&&(p.year++,p.month=1),c-=d;else for(;c<=0;)1==p.month?(p.year--,p.month=12):p.month--,c+=a.daysInMonth(p.month,p.year);return p.day=c,this._cachedUnixTime=null,this}fromUnixTime(t){this.zone=m.utcTimezone;let e=new Date(1e3*t);this.year=e.getUTCFullYear(),this.month=e.getUTCMonth()+1,this.day=e.getUTCDate(),this._time.isDate?(this.hour=0,this.minute=0,this.second=0):(this.hour=e.getUTCHours(),this.minute=e.getUTCMinutes(),this.second=e.getUTCSeconds()),this._cachedUnixTime=null}toUnixTime(){if(null!==this._cachedUnixTime)return this._cachedUnixTime;let t=this.utcOffset(),e=Date.UTC(this.year,this.month-1,this.day,this.hour,this.minute,this.second-t);return this._cachedUnixTime=e/1e3,this._cachedUnixTime}toJSON(){let t,e=["year","month","day","hour","minute","second","isDate"],i=Object.create(null),r=0,n=e.length;for(;r1)throw new c("invalid ical body. component began but did not end");return e=null,1==i.length?i[0]:i}u.property=function(t,e){let i={component:[[],[]],designSet:e||ct.defaultSet};return u._handleContentLine(t,i),i.component[1][0]},u.component=function(t){return u(t)};class c extends Error{name=this.constructor.name}u.ParserError=c,u._handleContentLine=function(t,e){let i,r,n,s,a,o,h=t.indexOf(l),d=t.indexOf(";"),m={};if(-1!==d&&-1!==h&&d>h&&(d=-1),-1!==d){if(n=t.slice(0,Math.max(0,d)).toLowerCase(),a=u._parseParameters(t.slice(Math.max(0,d)),0,e.designSet),-1==a[2])throw new c("Invalid parameters in '"+t+"'");let o;if(m=a[0],o="string"==typeof a[1]?a[1].length:a[1].reduce(((t,e)=>t+e.length),0),i=o+a[2]+d,-1===(r=t.slice(Math.max(0,i)).indexOf(l)))throw new c("Missing parameter value in '"+t+"'");s=t.slice(Math.max(0,i+r+1))}else{if(-1===h)throw new c('invalid line (no token ";" or ":") "'+t+'"');if(n=t.slice(0,Math.max(0,h)).toLowerCase(),s=t.slice(Math.max(0,h+1)),"begin"===n){let t=[s.toLowerCase(),[],[]];return 1===e.stack.length?e.component.push(t):e.component[2].push(t),e.stack.push(e.component),e.component=t,void(e.designSet||(e.designSet=ct.getDesignSet(e.component[0])))}if("end"===n)return void(e.component=e.stack.pop())}let f,p,y,_,g=!1,D=!1;e.designSet.propertyGroups&&-1!==n.indexOf(".")?(p=n.split("."),m.group=p[0],y=p[1]):y=n,y in e.designSet.property&&(f=e.designSet.property[y],"multiValue"in f&&(g=f.multiValue),"structuredValue"in f&&(D=f.structuredValue),s&&"detectType"in f&&(o=f.detectType(s))),o||(o="value"in m?m.value.toLowerCase():f?f.defaultType:"unknown"),delete m.value,g&&D?(s=u._parseMultiValue(s,D,o,[],g,e.designSet,D),_=[y,m,o,s]):g?(_=[y,m,o],u._parseMultiValue(s,g,o,_,null,e.designSet,!1)):D?(s=u._parseMultiValue(s,D,o,[],null,e.designSet,D),_=[y,m,o,s]):(s=u._parseValue(s,o,e.designSet,!1),_=[y,m,o,s]),"vcard"!==e.component[0]||0!==e.component[1].length||"version"===n&&"4.0"===s||(e.designSet=ct.getDesignSet("vcard3")),e.component[1].push(_)},u._parseValue=function(t,e,i,r){return e in i.value&&"fromICAL"in i.value[e]?i.value[e].fromICAL(t,r):t},u._parseParameters=function(t,e,i){let r,n,s,a,o,h,d=e,m=0,f={},p=-1;for(;!1!==m&&-1!==(m=t.indexOf("=",m+1));){if(r=t.slice(d+1,m),0==r.length)throw new c("Empty parameter name in '"+t+"'");if(n=r.toLowerCase(),h=!1,o=!1,a=n in i.param&&i.param[n].valueType?i.param[n].valueType:"text",n in i.param&&(o=i.param[n].multiValue,i.param[n].multiValueSeparateDQuote&&(h=u._rfc6868Escape('"'+o+'"'))),'"'===t[m+1]){if(p=m+2,m=t.indexOf('"',p),o&&-1!=m){let e=!0;for(;e;)t[m+1]==o&&'"'==t[m+2]?m=t.indexOf('"',m+3):e=!1}if(-1===m)throw new c('invalid line (no matching double quote) "'+t+'"');s=t.slice(p,m),d=t.indexOf(";",m);let e=t.indexOf(l,m);(-1===d||-1!==e&&d>e)&&(m=!1)}else{p=m+1;let e=t.indexOf(";",p),i=t.indexOf(l,p);-1!==i&&e>i?(e=i,m=!1):-1===e?(e=-1===i?t.length:i,m=!1):(d=e,m=e),s=t.slice(p,e)}const e=s.length;if(s=u._rfc6868Escape(s),p+=e-s.length,o){let t=h||o;s=u._parseMultiValue(s,t,a,[],null,i)}else s=u._parseValue(s,a,i);o&&n in f?Array.isArray(f[n])?f[n].push(s):f[n]=[f[n],s]:f[n]=s}return[f,s,p]},u._rfc6868Escape=function(t){return t.replace(/\^['n^]/g,(function(t){return h[t]}))},u._parseMultiValue=function(t,e,i,r,n,s,a){let o,l=0,h=0;if(0===e.length)return t;for(;-1!==(l=D(t,e,h));)o=t.slice(h,l),o=n?u._parseMultiValue(o,n,i,[],null,s,a):u._parseValue(o,i,s,a),r.push(o),h=l+e.length;return o=t.slice(h),o=n?u._parseMultiValue(o,n,i,[],null,s,a):u._parseValue(o,i,s,a),r.push(o),1==r.length?r[0]:r},u._eachLine=function(t,e){let i,r,n,s=t.length,a=t.search(o),l=a;do{l=t.indexOf("\n",a)+1,n=l>1&&"\r"===t[l-2]?2:1,0===l&&(l=s,n=0),r=t[a]," "===r||"\t"===r?i+=t.slice(a+1,l-n):(i&&e(null,i),i=t.slice(a,l-n)),a=l}while(l!==s);i=i.trim(),i.length&&e(null,i)};const d=["tzid","location","tznames","latitude","longitude"];class m{static _compare_change_fn(t,e){return t.yeare.year?1:t.monthe.month?1:t.daye.day?1:t.houre.hour?1:t.minutee.minute?1:t.seconde.second?1:0}static convert_time(t,e,i){if(t.isDate||e.tzid==i.tzid||e==m.localTimezone||i==m.localTimezone)return t.zone=i,t;let r=e.utcOffset(t);return t.adjust(0,0,0,-r),r=i.utcOffset(t),t.adjust(0,0,0,r),null}static fromData(t){return(new m).fromData(t)}static#e=null;static get utcTimezone(){return this.#e||(this.#e=m.fromData({tzid:"UTC"})),this.#e}static#i=null;static get localTimezone(){return this.#i||(this.#i=m.fromData({tzid:"floating"})),this.#i}static adjust_change(t,e,i,r,n){return a.prototype.adjust.call(t,e,i,r,n,t)}static _minimumExpansionYear=-1;static EXTRA_COVERAGE=5;constructor(t){this.wrappedJSObject=this,this.fromData(t)}tzid="";location="";tznames="";latitude=0;longitude=0;component=null;expandedUntilYear=0;icalclass="icaltimezone";fromData(t){if(this.expandedUntilYear=0,this.changes=[],t instanceof _t)this.component=t;else{if(t&&"component"in t)if("string"==typeof t.component){let e=u(t.component);this.component=new _t(e)}else t.component instanceof _t?this.component=t.component:this.component=null;for(let e of d)t&&e in t&&(this[e]=t[e])}return this.component instanceof _t&&!this.tzid&&(this.tzid=this.component.getFirstPropertyValue("tzid")),this}utcOffset(t){if(this==m.utcTimezone||this==m.localTimezone)return 0;if(this._ensureCoverage(t.year),!this.changes.length)return 0;let e={year:t.year,month:t.month,day:t.day,hour:t.hour,minute:t.minute,second:t.second},i=this._findNearbyChange(e),r=-1,n=1;for(;;){let t=Y(this.changes[i],!0);if(t.utcOffset=0?r=i:n=-1,-1==n&&-1!=r)break;if(i+=n,i<0)return 0;if(i>=this.changes.length)break}let s=this.changes[r];if(s.utcOffset-s.prevUtcOffset<0&&r>0){let t=Y(s,!0);if(m.adjust_change(t,0,0,0,t.prevUtcOffset),m._compare_change_fn(e,t)<0){let t=this.changes[r-1],e=!1;s.is_daylight!=e&&t.is_daylight==e&&(s=t)}}return s.utcOffset}_findNearbyChange(t){let e=T(this.changes,t,m._compare_change_fn);return e>=this.changes.length?this.changes.length-1:e}_ensureCoverage(t){if(-1==m._minimumExpansionYear){let t=a.now();m._minimumExpansionYear=t.year}let e=t;if(ee)&&t);)r.year=t.year,r.month=t.month,r.day=t.day,r.hour=t.hour,r.minute=t.minute,r.second=t.second,r.isDate=t.isDate,m.adjust_change(r,0,0,0,-r.prevUtcOffset),i.push(r)}}else r=a(),r.year=n.year,r.month=n.month,r.day=n.day,r.hour=n.hour,r.minute=n.minute,r.second=n.second,m.adjust_change(r,0,0,0,-r.prevUtcOffset),i.push(r);return i}toString(){return this.tznames?this.tznames:this.tzid}}let f=null;const p={get count(){return null===f?0:Object.keys(f).length},reset:function(){f=Object.create(null);let t=m.utcTimezone;f.Z=t,f.UTC=t,f.GMT=t},_hard_reset:function(){f=null},has:function(t){return null!==f&&!!f[t]},get:function(t){return null===f&&this.reset(),f[t]},register:function(t,e){if(null===f&&this.reset(),"string"==typeof t&&e instanceof m&&([t,e]=[e,t]),e||(t instanceof m?e=t.tzid:"vtimezone"===t.name&&(e=(t=new m(t)).tzid)),!e)throw new TypeError("Neither a timezone nor a name was passed");if(!(t instanceof m))throw new TypeError("timezone must be ICAL.Timezone or ICAL.Component");f[e]=t},remove:function(t){return null===f?null:delete f[t]}};function y(t){return"number"==typeof t&&isNaN(t)}function _(t){let e=parseInt(t,10);if(y(e))throw new Error('Could not extract integer from "'+t+'"');return e}function g(t,e){if(void 0!==t)return t instanceof e?t:new e(t)}function D(t,e,i){for(;-1!==(i=t.indexOf(e,i));){if(!(i>0&&"\\"===t[i-1]))return i;i+=1}return-1}function T(t,e,i){if(!t.length)return 0;let r,n,s=0,a=t.length-1;for(;s<=a;)if(r=s+Math.floor((a-s)/2),n=i(e,t[r]),n<0)a=r-1;else{if(!(n>0))break;s=r+1}return n<0?r:n>0?r+1:r}function Y(t,e){if(t&&"object"==typeof t){if(t instanceof Date)return new Date(t.getTime());if("clone"in t)return t.clone();if(Array.isArray(t)){let i=[];for(let r=0;r65535?2:1:(e+=Yt.newLineChar+" "+i.slice(0,Math.max(0,r)),i=i.slice(Math.max(0,r)),r=n=0)}return e.slice(Yt.newLineChar.length+1)}function O(t){switch("string"!=typeof t&&("number"==typeof t&&(t=parseInt(t)),t=String(t)),t.length){case 0:return"00";case 1:return"0"+t;default:return t}}function b(t){return t<0?Math.ceil(t):Math.floor(t)}function S(t,e){for(let i in t){let r=Object.getOwnPropertyDescriptor(t,i);r&&!Object.getOwnPropertyDescriptor(e,i)&&Object.defineProperty(e,i,r)}return e}var E=Object.freeze({__proto__:null,binsearchInsert:T,clone:Y,extend:S,foldline:A,formatClassType:g,isStrictlyNaN:y,pad2:O,strictParseInt:_,trunc:b,unescapedIndexOf:D,updateTimezones:function(t){let e,i,r,n,s;if(!t||"vcalendar"!==t.name)return t;for(e=t.getAllSubcomponents(),i=[],r={},s=0;si)-(i>e)}_normalize(){let t=this.toSeconds(),e=this.factor;for(;t<-43200;)t+=97200;for(;t>50400;)t-=97200;this.fromSeconds(t),0==t&&(this.factor=e)}toICALString(){return ct.icalendar.value["utc-offset"].toICAL(this.toString())}toString(){return(1==this.factor?"+":"-")+O(this.hours)+":"+O(this.minutes)}}class C extends a{static fromDateAndOrTimeString(t,e){function i(t,e,i){return t?_(t.slice(e,e+i)):null}let r=t.split("T"),n=r[0],s=r[1],a=s?ct.vcard.value.time._splitZone(s):[],o=a[0],l=a[1],h=n?n.length:0,u=l?l.length:0,c=n&&"-"==n[0]&&"-"==n[1],d=l&&"-"==l[0],f={year:c?null:i(n,0,4),month:!c||4!=h&&7!=h?7==h||10==h?i(n,5,2):null:i(n,2,2),day:5==h?i(n,3,2):7==h&&c?i(n,5,2):10==h?i(n,8,2):null,hour:d?null:i(l,0,2),minute:d&&3==u?i(l,1,2):u>4?i(l,d?1:3,2):null,second:4==u?i(l,2,2):6==u?i(l,4,2):8==u?i(l,6,2):null};return o="Z"==o?m.utcTimezone:o&&":"==o[3]?w.fromString(o):null,new C(f,o,e)}constructor(t,e,i){super(t,e),this.icaltype=i||"date-and-or-time"}icalclass="vcardtime";icaltype="date-and-or-time";clone(){return new C(this._time,this.zone,this.icaltype)}_normalize(){return this}utcOffset(){return this.zone instanceof w?this.zone.toSeconds():a.prototype.utcOffset.apply(this,arguments)}toICALString(){return ct.vcard.value[this.icaltype].toICAL(this.toString())}toString(){let t,e=this.year,i=this.month,r=this.day,n=this.hour,s=this.minute,a=this.second,o=null!==i,l=null!==r,h=null!==n,u=null!==s,c=null!==a,d=(null!==e?O(e)+(o||l?"-":""):o||l?"--":"")+(o?O(i):"")+(l?"-"+O(r):""),f=(h?O(n):"-")+(h&&u?":":"")+(u?O(s):"")+(h||u?"":"-")+(u&&c?":":"")+(c?O(a):"");if(this.zone===m.utcTimezone)t="Z";else if(this.zone instanceof w)t=this.zone.toString();else if(this.zone===m.localTimezone)t="";else if(this.zone instanceof m){t=w.fromSeconds(this.zone.utcOffset(this)).toString()}else t="";switch(this.icaltype){case"time":return f+t;case"date-and-or-time":case"date-time":return d+("--"==f?"":"T"+f+t);case"date":return d}return null}}class x{static _indexMap={BYSECOND:0,BYMINUTE:1,BYHOUR:2,BYDAY:3,BYMONTHDAY:4,BYYEARDAY:5,BYWEEKNO:6,BYMONTH:7,BYSETPOS:8};static _expandMap={SECONDLY:[1,1,1,1,1,1,1,1],MINUTELY:[2,1,1,1,1,1,1,1],HOURLY:[2,2,1,1,1,1,1,1],DAILY:[2,2,2,1,1,1,1,1],WEEKLY:[2,2,2,2,3,3,1,1],MONTHLY:[2,2,2,2,2,3,3,1],YEARLY:[2,2,2,2,2,2,2,2]};static UNKNOWN=0;static CONTRACT=1;static EXPAND=2;static ILLEGAL=3;constructor(t){this.fromData(t)}completed=!1;rule=null;dtstart=null;last=null;occurrence_number=0;by_indices=null;initialized=!1;by_data=null;days=null;days_index=0;fromData(t){if(this.rule=g(t.rule,L),!this.rule)throw new Error("iterator requires a (ICAL.Recur) rule");if(this.dtstart=g(t.dtstart,a),!this.dtstart)throw new Error("iterator requires a (ICAL.Time) dtstart");if(t.by_data?this.by_data=t.by_data:this.by_data=Y(this.rule.parts,!0),t.occurrence_number&&(this.occurrence_number=t.occurrence_number),this.days=t.days||[],t.last&&(this.last=g(t.last,a)),this.by_indices=t.by_indices,this.by_indices||(this.by_indices={BYSECOND:0,BYMINUTE:0,BYHOUR:0,BYDAY:0,BYMONTH:0,BYWEEKNO:0,BYMONTHDAY:0}),this.initialized=t.initialized||!1,!this.initialized)try{this.init()}catch(t){if(!(t instanceof N))throw t;this.completed=!0}}init(){this.initialized=!0,this.last=this.dtstart.clone();let t=this.by_data;if("BYDAY"in t&&this.sort_byday_rules(t.BYDAY),"BYYEARDAY"in t&&("BYMONTH"in t||"BYWEEKNO"in t||"BYMONTHDAY"in t))throw new Error("Invalid BYYEARDAY rule");if("BYWEEKNO"in t&&"BYMONTHDAY"in t)throw new Error("BYWEEKNO does not fit to BYMONTHDAY");if("MONTHLY"==this.rule.freq&&("BYYEARDAY"in t||"BYWEEKNO"in t))throw new Error("For MONTHLY recurrences neither BYYEARDAY nor BYWEEKNO may appear");if("WEEKLY"==this.rule.freq&&("BYYEARDAY"in t||"BYMONTHDAY"in t))throw new Error("For WEEKLY recurrences neither BYMONTHDAY nor BYYEARDAY may appear");if("YEARLY"!=this.rule.freq&&"BYYEARDAY"in t)throw new Error("BYYEARDAY may only appear in YEARLY rules");if(this.last.second=this.setup_defaults("BYSECOND","SECONDLY",this.dtstart.second),this.last.minute=this.setup_defaults("BYMINUTE","MINUTELY",this.dtstart.minute),this.last.hour=this.setup_defaults("BYHOUR","HOURLY",this.dtstart.hour),this.last.day=this.setup_defaults("BYMONTHDAY","DAILY",this.dtstart.day),this.last.month=this.setup_defaults("BYMONTH","MONTHLY",this.dtstart.month),"WEEKLY"==this.rule.freq)if("BYDAY"in t){let[,e]=this.ruleDayOfWeek(t.BYDAY[0],this.rule.wkst),i=e-this.last.dayOfWeek(this.rule.wkst);(this.last.dayOfWeek(this.rule.wkst)=0||i<0)&&(this.last.day+=i)}else{let e=L.numericDayToIcalDay(this.dtstart.dayOfWeek());t.BYDAY=[e]}if("YEARLY"==this.rule.freq){const t=this.rule.until?this.rule.until.year:2e4;for(;this.last.year<=t&&(this.expand_year_days(this.last.year),!(this.days.length>0));)this.increment_year(this.rule.interval);if(0==this.days.length)throw new N;if(!(this._nextByYearDay()||this.next_year()||this.next_year()||this.next_year()))throw new N}if("MONTHLY"==this.rule.freq)if(this.has_by_data("BYDAY")){let t=null,e=this.last.clone(),i=a.daysInMonth(this.last.month,this.last.year);for(let r of this.by_data.BYDAY){this.last=e.clone();let[n,s]=this.ruleDayOfWeek(r),o=this.last.nthWeekDay(s,n);if(n>=6||n<=-6)throw new Error("Malformed values in BYDAY part");if(o>i||o<=0){if(t&&t.month==e.month)continue;for(;o>i||o<=0;)this.increment_month(),i=a.daysInMonth(this.last.month,this.last.year),o=this.last.nthWeekDay(s,n)}this.last.day=o,(!t||this.last.compare(t)<0)&&(t=this.last.clone())}if(this.last=t.clone(),this.has_by_data("BYMONTHDAY")&&this._byDayAndMonthDay(!0),this.last.day>i||0==this.last.day)throw new Error("Malformed values in BYDAY part")}else if(this.has_by_data("BYMONTHDAY")){this.last.day=1;let t=this.normalizeByMonthDayRules(this.last.year,this.last.month,this.rule.parts.BYMONTHDAY).filter((t=>t>=this.last.day));if(t.length)this.last.day=t[0],this.by_data.BYMONTHDAY=t;else if(!this.next_month()&&!this.next_month()&&!this.next_month())throw new N}}next(t=!1){let e,i=this.last?this.last.clone():null;if((this.rule.count&&this.occurrence_number>=this.rule.count||this.rule.until&&this.last.compare(this.rule.until)>0)&&(this.completed=!0),this.completed)return null;if(0==this.occurrence_number&&this.last.compare(this.dtstart)>=0)return this.occurrence_number++,this.last;let r=0;do{switch(e=1,this.rule.freq){case"SECONDLY":this.next_second();break;case"MINUTELY":this.next_minute();break;case"HOURLY":this.next_hour();break;case"DAILY":this.next_day();break;case"WEEKLY":this.next_week();break;case"MONTHLY":if(e=this.next_month(),e)r=0;else if(336==++r)return this.completed=!0,null;break;case"YEARLY":if(e=this.next_year(),e)r=0;else if(28==++r)return this.completed=!0,null;break;default:return null}}while(!this.check_contracting_rules()||this.last.compare(this.dtstart)<0||!e);if(0==this.last.compare(i)){if(t)throw new Error("Same occurrence found twice, protecting you from death by recursion");this.next(!0)}return this.rule.until&&this.last.compare(this.rule.until)>0?(this.completed=!0,null):(this.occurrence_number++,this.last)}next_second(){return this.next_generic("BYSECOND","SECONDLY","second","minute")}increment_second(t){return this.increment_generic(t,"second",60,"minute")}next_minute(){return this.next_generic("BYMINUTE","MINUTELY","minute","hour","next_second")}increment_minute(t){return this.increment_generic(t,"minute",60,"hour")}next_hour(){return this.next_generic("BYHOUR","HOURLY","hour","monthday","next_minute")}increment_hour(t){this.increment_generic(t,"hour",24,"monthday")}next_day(){let t="DAILY"==this.rule.freq;return 0==this.next_hour()||(t?this.increment_monthday(this.rule.interval):this.increment_monthday(1)),0}next_week(){let t=0;if(0==this.next_weekday_by_week())return t;if(this.has_by_data("BYWEEKNO")){this.by_indices.BYWEEKNO++,this.by_indices.BYWEEKNO==this.by_data.BYWEEKNO.length&&(this.by_indices.BYWEEKNO=0,t=1),this.last.month=1,this.last.day=1;let e=this.by_data.BYWEEKNO[this.by_indices.BYWEEKNO];this.last.day+=7*e,t&&this.increment_year(1)}else this.increment_monthday(7*this.rule.interval);return t}normalizeByMonthDayRules(t,e,i){let r,n=a.daysInMonth(e,t),s=[],o=0,l=i.length;for(;on)){if(r<0)r=n+(r+1);else if(0===r)continue;-1===s.indexOf(r)&&s.push(r)}}return s.sort((function(t,e){return t-e}))}_byDayAndMonthDay(t){let e,i,r,n,s=this.by_data.BYDAY,o=0,l=s.length,h=0,u=this,c=this.last.day;function d(){for(n=a.daysInMonth(u.last.month,u.last.year),e=u.normalizeByMonthDayRules(u.last.year,u.last.month,u.by_data.BYMONTHDAY),r=e.length;e[o]<=c&&(!t||e[o]!=c)&&on){m();continue}let t=e[o++];if(t>=i){c=t;for(let t=0;ti&&(this.last.day=1,this.increment_month(),this.is_day_in_byday(this.last)?this.has_by_data("BYSETPOS")&&!this.check_set_position(1)||(t=1):t=0)}else if(this.has_by_data("BYMONTHDAY")){if(this.by_indices.BYMONTHDAY++,this.by_indices.BYMONTHDAY>=this.by_data.BYMONTHDAY.length&&(this.by_indices.BYMONTHDAY=0,this.increment_month(),this.by_indices.BYMONTHDAY>=this.by_data.BYMONTHDAY.length))return 0;let e=a.daysInMonth(this.last.month,this.last.year),i=this.by_data.BYMONTHDAY[this.by_indices.BYMONTHDAY];i<0&&(i=e+i+1),i>e?(this.last.day=1,t=this.is_day_in_byday(this.last)):this.last.day=i}else{this.increment_month();let e=a.daysInMonth(this.last.month,this.last.year);this.by_data.BYMONTHDAY[0]>e?t=0:this.last.day=this.by_data.BYMONTHDAY[0]}return t}next_weekday_by_week(){let t=0;if(0==this.next_hour())return t;if(!this.has_by_data("BYDAY"))return 1;for(;;){let e=new a;this.by_indices.BYDAY++,this.by_indices.BYDAY==Object.keys(this.by_data.BYDAY).length&&(this.by_indices.BYDAY=0,t=1);let i=this.by_data.BYDAY[this.by_indices.BYDAY],r=this.ruleDayOfWeek(i)[1];r-=this.rule.wkst,r<0&&(r+=7),e.year=this.last.year,e.month=this.last.month,e.day=this.last.day;let n=e.startDoyWeek(this.rule.wkst);if(r+n<1&&!t)continue;let s=a.fromDayOfYear(n+r,this.last.year);return this.last.year=s.year,this.last.month=s.month,this.last.day=s.day,t}}next_year(){return 0==this.next_hour()?0:0!=this.days.length&&++this.days_index!=this.days.length||(this.days_index=0,this.increment_year(this.rule.interval),this.has_by_data("BYMONTHDAY")&&(this.by_data.BYMONTHDAY=this.normalizeByMonthDayRules(this.last.year,this.last.month,this.rule.parts.BYMONTHDAY)),this.expand_year_days(this.last.year),0!=this.days.length)?this._nextByYearDay():0}_nextByYearDay(){let t=this.days[this.days_index],e=this.last.year;if(366==Math.abs(t)&&!a.isLeapYear(this.last.year))return 0;t<1&&(t+=1,e+=1);let i=a.fromDayOfYear(t,e);return this.last.day=i.day,this.last.month=i.month,1}ruleDayOfWeek(t,e){let i=t.match(/([+-]?[0-9])?(MO|TU|WE|TH|FR|SA|SU)/);if(i){return[parseInt(i[1]||0,10),t=L.icalDayToNumericDay(i[2],e)]}return[0,0]}next_generic(t,e,i,r,n){let s=t in this.by_data,a=this.rule.freq==e,o=0;if(n&&0==this[n]())return o;if(s){this.by_indices[t]++;let e=this.by_data[t];this.by_indices[t]==e.length&&(this.by_indices[t]=0,o=1),this.last[i]=e[this.by_indices[t]]}else a&&this["increment_"+i](this.rule.interval);return s&&o&&a&&this["increment_"+r](1),o}increment_monthday(t){for(let e=0;et&&(this.last.day-=t,this.increment_month())}}increment_month(){if(this.last.day=1,this.has_by_data("BYMONTH"))this.by_indices.BYMONTH++,this.by_indices.BYMONTH==this.by_data.BYMONTH.length&&(this.by_indices.BYMONTH=0,this.increment_year(1)),this.last.month=this.by_data.BYMONTH[this.by_indices.BYMONTH];else{"MONTHLY"==this.rule.freq?this.last.month+=this.rule.interval:this.last.month++,this.last.month--;let t=b(this.last.month/12);this.last.month%=12,this.last.month++,0!=t&&this.increment_year(t)}this.has_by_data("BYMONTHDAY")&&(this.by_data.BYMONTHDAY=this.normalizeByMonthDayRules(this.last.year,this.last.month,this.rule.parts.BYMONTHDAY))}increment_year(t){this.last.day=1,this.last.year+=t}increment_generic(t,e,i,r){this.last[e]+=t;let n=b(this.last[e]/i);this.last[e]%=i,0!=n&&this["increment_"+r](n)}has_by_data(t){return t in this.rule.parts}expand_year_days(t){let e=new a;this.days=[];let i={},r=["BYDAY","BYWEEKNO","BYMONTHDAY","BYMONTH","BYYEARDAY"];for(let t of r)t in this.rule.parts&&(i[t]=this.rule.parts[t]);if("BYMONTH"in i&&"BYWEEKNO"in i){let r=1,n={};e.year=t,e.isDate=!0;for(let i=0;i0?(e=h+7*(a-1),e<=r&&this.days.push(s+e)):(e=u+7*(a+1),e>0&&this.days.push(s+e))}}this.days.sort((function(t,e){return t-e}))}else if(2==n&&"BYDAY"in i&&"BYMONTHDAY"in i){let e=this.expand_by_day(t);for(let i of e){let e=a.fromDayOfYear(i,t);this.by_data.BYMONTHDAY.indexOf(e.day)>=0&&this.days.push(i)}}else if(3==n&&"BYDAY"in i&&"BYMONTHDAY"in i&&"BYMONTH"in i){let e=this.expand_by_day(t);for(let i of e){let e=a.fromDayOfYear(i,t);this.by_data.BYMONTH.indexOf(e.month)>=0&&this.by_data.BYMONTHDAY.indexOf(e.day)>=0&&this.days.push(i)}}else if(2==n&&"BYDAY"in i&&"BYWEEKNO"in i){let e=this.expand_by_day(t);for(let i of e){let e=a.fromDayOfYear(i,t).weekNumber(this.rule.wkst);this.by_data.BYWEEKNO.indexOf(e)&&this.days.push(i)}}else if(3==n&&"BYDAY"in i&&"BYWEEKNO"in i&&"BYMONTHDAY"in i);else if(1==n&&"BYYEARDAY"in i)this.days=this.days.concat(this.by_data.BYYEARDAY);else if(2==n&&"BYYEARDAY"in i&&"BYDAY"in i){let e=a.isLeapYear(t)?366:365,i=new Set(this.expand_by_day(t));for(let t of this.by_data.BYYEARDAY)t<0&&(t+=e+1),i.has(t)&&this.days.push(t)}else this.days=[];let s=a.isLeapYear(t)?366:365;return this.days.sort(((t,e)=>(t<0&&(t+=s+1),e<0&&(e+=s+1),t-e))),0}expand_by_day(t){let e=[],i=this.last.clone();i.year=t,i.month=1,i.day=1,i.isDate=!0;let r=i.dayOfWeek();i.month=12,i.day=31,i.isDate=!0;let n=i.dayOfWeek(),s=i.dayOfYear();for(let t of this.by_data.BYDAY){let i=this.ruleDayOfWeek(t),a=i[0],o=i[1];if(0==a){for(let t=(o+7-r)%7+1;t<=s;t+=7)e.push(t)}else if(a>0){let t;t=o>=r?o-r+1:o-r+8,e.push(t+7*(a-1))}else{let t;a=-a,t=o<=n?s-n+o:s-n+o-7,e.push(t-7*(a-1))}}return e}is_day_in_byday(t){if(this.by_data.BYDAY)for(let e of this.by_data.BYDAY){let i=this.ruleDayOfWeek(e),r=i[0],n=i[1],s=t.dayOfWeek();if(0==r&&n==s||t.nthWeekDay(n,r)==t.day)return 1}return 0}check_set_position(t){if(this.has_by_data("BYSETPOS")){return-1!==this.by_data.BYSETPOS.indexOf(t)}return!1}sort_byday_rules(t){for(let e=0;ethis.ruleDayOfWeek(t[e],this.rule.wkst)[1]){let r=t[e];t[e]=t[i],t[i]=r}}}check_contract_restriction(t,e){let i=x._indexMap[t],r=x._expandMap[this.rule.freq][i],n=!1;if(t in this.by_data&&r==x.CONTRACT){let i=this.by_data[t];for(let t of i)if(t==e){n=!0;break}}else n=!0;return n}check_contracting_rules(){let t=this.last.dayOfWeek(),e=this.last.weekNumber(this.rule.wkst),i=this.last.dayOfYear();return this.check_contract_restriction("BYSECOND",this.last.second)&&this.check_contract_restriction("BYMINUTE",this.last.minute)&&this.check_contract_restriction("BYHOUR",this.last.hour)&&this.check_contract_restriction("BYDAY",L.numericDayToIcalDay(t))&&this.check_contract_restriction("BYWEEKNO",e)&&this.check_contract_restriction("BYMONTHDAY",this.last.day)&&this.check_contract_restriction("BYMONTH",this.last.month)&&this.check_contract_restriction("BYYEARDAY",i)}setup_defaults(t,e,i){let r=x._indexMap[t];return x._expandMap[this.rule.freq][r]!=x.CONTRACT&&(t in this.by_data||(this.by_data[t]=[i]),this.rule.freq!=e)?this.by_data[t][0]:i}toJSON(){let t=Object.create(null);return t.initialized=this.initialized,t.rule=this.rule.toJSON(),t.dtstart=this.dtstart.toJSON(),t.by_data=this.by_data,t.days=this.days,t.last=this.last.toJSON(),t.by_indices=this.by_indices,t.occurrence_number=this.occurrence_number,t}}class N extends Error{constructor(){super("Recurrence rule has no valid occurrences")}}const v=/^(SU|MO|TU|WE|TH|FR|SA)$/,I=/^([+-])?(5[0-3]|[1-4][0-9]|[1-9])?(SU|MO|TU|WE|TH|FR|SA)$/,B={SU:a.SUNDAY,MO:a.MONDAY,TU:a.TUESDAY,WE:a.WEDNESDAY,TH:a.THURSDAY,FR:a.FRIDAY,SA:a.SATURDAY},M=Object.fromEntries(Object.entries(B).map((t=>t.reverse()))),z=["SECONDLY","MINUTELY","HOURLY","DAILY","WEEKLY","MONTHLY","YEARLY"];class L{static fromString(t){let e=this._stringToData(t,!1);return new L(e)}static fromData(t){return new L(t)}static _stringToData(t,e){let i=Object.create(null),r=t.split(";"),n=r.length;for(let t=0;t7&&(i-=7),M[i]}constructor(t){this.wrappedJSObject=this,this.parts={},t&&"object"==typeof t&&this.fromData(t)}parts=null;interval=1;wkst=a.MONDAY;until=null;count=null;freq=null;icalclass="icalrecur";icaltype="recur";iterator(t){return new x({rule:this,dtstart:t})}clone(){return new L(this.toJSON())}isFinite(){return!(!this.count&&!this.until)}isByCount(){return!(!this.count||this.until)}addComponent(t,e){let i=t.toUpperCase();i in this.parts?this.parts[i].push(e):this.parts[i]=[e]}setComponent(t,e){this.parts[t.toUpperCase()]=e.slice()}getComponent(t){let e=t.toUpperCase();return e in this.parts?this.parts[e].slice():[]}getNextOccurrence(t,e){let i,r=this.iterator(t);do{i=r.next()}while(i&&i.compare(e)<=0);return i&&e.zone&&(i.zone=e.zone),i}fromData(t){for(let e in t){let i=e.toUpperCase();i in U?Array.isArray(t[e])?this.parts[i]=t[e]:this.parts[i]=[t[e]]:this[e]=t[e]}this.interval&&"number"!=typeof this.interval&&P.INTERVAL(this.interval,this),this.wkst&&"number"!=typeof this.wkst&&(this.wkst=L.icalDayToNumericDay(this.wkst)),!this.until||this.until instanceof a||(this.until=a.fromString(this.until))}toJSON(){let t=Object.create(null);t.freq=this.freq,this.count&&(t.count=this.count),this.interval>1&&(t.interval=this.interval);for(let[e,i]of Object.entries(this.parts))Array.isArray(i)&&1==i.length?t[e.toLowerCase()]=i[0]:t[e.toLowerCase()]=Y(i);return this.until&&(t.until=this.until.toString()),"wkst"in this&&this.wkst!==a.DEFAULT_WEEK_START&&(t.wkst=L.numericDayToIcalDay(this.wkst)),t}toString(){let t="FREQ="+this.freq;this.count&&(t+=";COUNT="+this.count),this.interval>1&&(t+=";INTERVAL="+this.interval);for(let[e,i]of Object.entries(this.parts))t+=";"+e+"="+i;return this.until&&(t+=";UNTIL="+this.until.toICALString()),"wkst"in this&&this.wkst!==a.DEFAULT_WEEK_START&&(t+=";WKST="+L.numericDayToIcalDay(this.wkst)),t}}function k(t,e,i,r){let n=r;if("+"===r[0]&&(n=r.slice(1)),n=_(n),void 0!==e&&r '+e);if(void 0!==i&&r>i)throw new Error(t+': invalid value "'+r+'" must be < '+e);return n}const P={FREQ:function(t,e,i){if(-1===z.indexOf(t))throw new Error('invalid frequency "'+t+'" expected: "'+z.join(", ")+'"');e.freq=t},COUNT:function(t,e,i){e.count=_(t)},INTERVAL:function(t,e,i){e.interval=_(t),e.interval<1&&(e.interval=1)},UNTIL:function(t,e,i){t.length>10?e.until=ct.icalendar.value["date-time"].fromICAL(t):e.until=ct.icalendar.value.date.fromICAL(t),i||(e.until=a.fromString(e.until))},WKST:function(t,e,i){if(!v.test(t))throw new Error('invalid WKST value "'+t+'"');e.wkst=L.icalDayToNumericDay(t)}},U={BYSECOND:k.bind(void 0,"BYSECOND",0,60),BYMINUTE:k.bind(void 0,"BYMINUTE",0,59),BYHOUR:k.bind(void 0,"BYHOUR",0,23),BYDAY:function(t){if(I.test(t))return t;throw new Error('invalid BYDAY value "'+t+'"')},BYMONTHDAY:k.bind(void 0,"BYMONTHDAY",-31,31),BYYEARDAY:k.bind(void 0,"BYYEARDAY",-366,366),BYWEEKNO:k.bind(void 0,"BYWEEKNO",-53,53),BYMONTH:k.bind(void 0,"BYMONTH",1,12),BYSETPOS:k.bind(void 0,"BYSETPOS",-366,366)},j=/\\\\|\\,|\\[Nn]/g,V=/\\|,|\n/g;function H(t,e){return{matches:/.*/,fromICAL:function(e,i){return function(t,e,i){if(-1===t.indexOf("\\"))return t;i&&(e=new RegExp(e.source+"|\\\\"+i,e.flags));return t.replace(e,$)}(e,t,i)},toICAL:function(t,i){let r=e;return i&&(r=new RegExp(r.source+"|"+i,r.flags)),t.replace(r,(function(t){switch(t){case"\\":return"\\\\";case";":return"\\;";case",":return"\\,";case"\n":return"\\n";default:return t}}))}}}const R={defaultType:"text"},W={defaultType:"text",multiValue:","},F={defaultType:"text",structuredValue:";"},K={defaultType:"integer"},q={defaultType:"date-time",allowedTypes:["date-time","date"]},J={defaultType:"date-time"},Z={defaultType:"uri"},G={defaultType:"utc-offset"},X={defaultType:"recur"},Q={defaultType:"date-and-or-time",allowedTypes:["date-time","date","text"]};function $(t){switch(t){case"\\\\":return"\\";case"\\;":return";";case"\\,":return",";case"\\n":case"\\N":return"\n";default:return t}}let tt={categories:W,url:Z,version:R,uid:R},et={boolean:{values:["TRUE","FALSE"],fromICAL:function(t){return"TRUE"===t},toICAL:function(t){return t?"TRUE":"FALSE"}},float:{matches:/^[+-]?\d+\.\d+$/,fromICAL:function(t){let e=parseFloat(t);return y(e)?0:e},toICAL:function(t){return String(t)}},integer:{fromICAL:function(t){let e=parseInt(t);return y(e)?0:e},toICAL:function(t){return String(t)}},"utc-offset":{toICAL:function(t){return t.length<7?t.slice(0,3)+t.slice(4,6):t.slice(0,3)+t.slice(4,6)+t.slice(7,9)},fromICAL:function(t){return t.length<6?t.slice(0,3)+":"+t.slice(3,5):t.slice(0,3)+":"+t.slice(3,5)+":"+t.slice(5,7)},decorate:function(t){return w.fromString(t)},undecorate:function(t){return t.toString()}}};const it=S(et,{text:H(/\\\\|\\;|\\,|\\[Nn]/g,/\\|;|,|\n/g),uri:{},binary:{decorate:function(e){return t.fromString(e)},undecorate:function(t){return t.toString()}},"cal-address":{},date:{decorate:function(t,e){return ct.strict?a.fromDateString(t,e):a.fromString(t,e)},undecorate:function(t){return t.toString()},fromICAL:function(t){return!ct.strict&&t.length>=15?it["date-time"].fromICAL(t):t.slice(0,4)+"-"+t.slice(4,6)+"-"+t.slice(6,8)},toICAL:function(t){let e=t.length;return 10==e?t.slice(0,4)+t.slice(5,7)+t.slice(8,10):e>=19?it["date-time"].toICAL(t):t}},"date-time":{fromICAL:function(t){if(ct.strict||8!=t.length){let e=t.slice(0,4)+"-"+t.slice(4,6)+"-"+t.slice(6,8)+"T"+t.slice(9,11)+":"+t.slice(11,13)+":"+t.slice(13,15);return t[15]&&"Z"===t[15]&&(e+="Z"),e}return it.date.fromICAL(t)},toICAL:function(t){let e=t.length;if(10!=e||ct.strict){if(e>=19){let e=t.slice(0,4)+t.slice(5,7)+t.slice(8,13)+t.slice(14,16)+t.slice(17,19);return t[19]&&"Z"===t[19]&&(e+="Z"),e}return t}return it.date.toICAL(t)},decorate:function(t,e){return ct.strict?a.fromDateTimeString(t,e):a.fromString(t,e)},undecorate:function(t){return t.toString()}},duration:{decorate:function(t){return r.fromString(t)},undecorate:function(t){return t.toString()}},period:{fromICAL:function(t){let e=t.split("/");return e[0]=it["date-time"].fromICAL(e[0]),r.isValueString(e[1])||(e[1]=it["date-time"].fromICAL(e[1])),e},toICAL:function(t){return t=t.slice(),ct.strict||10!=t[0].length?t[0]=it["date-time"].toICAL(t[0]):t[0]=it.date.toICAL(t[0]),r.isValueString(t[1])||(ct.strict||10!=t[1].length?t[1]=it["date-time"].toICAL(t[1]):t[1]=it.date.toICAL(t[1])),t.join("/")},decorate:function(t,e){return s.fromJSON(t,e,!ct.strict)},undecorate:function(t){return t.toJSON()}},recur:{fromICAL:function(t){return L._stringToData(t,!0)},toICAL:function(t){let e="";for(let[i,r]of Object.entries(t))"until"==i?r=r.length>10?it["date-time"].toICAL(r):it.date.toICAL(r):"wkst"==i?"number"==typeof r&&(r=L.numericDayToIcalDay(r)):Array.isArray(r)&&(r=r.join(",")),e+=i.toUpperCase()+"="+r+";";return e.slice(0,Math.max(0,e.length-1))},decorate:function(t){return L.fromData(t)},undecorate:function(t){return t.toJSON()}},time:{fromICAL:function(t){if(t.length<6)return t;let e=t.slice(0,2)+":"+t.slice(2,4)+":"+t.slice(4,6);return"Z"===t[6]&&(e+="Z"),e},toICAL:function(t){if(t.length<8)return t;let e=t.slice(0,2)+t.slice(3,5)+t.slice(6,8);return"Z"===t[8]&&(e+="Z"),e}}});let rt=S(tt,{action:R,attach:{defaultType:"uri"},attendee:{defaultType:"cal-address"},calscale:R,class:R,comment:R,completed:J,contact:R,created:J,description:R,dtend:q,dtstamp:J,dtstart:q,due:q,duration:{defaultType:"duration"},exdate:{defaultType:"date-time",allowedTypes:["date-time","date"],multiValue:","},exrule:X,freebusy:{defaultType:"period",multiValue:","},geo:{defaultType:"float",structuredValue:";"},"last-modified":J,location:R,method:R,organizer:{defaultType:"cal-address"},"percent-complete":K,priority:K,prodid:R,"related-to":R,repeat:K,rdate:{defaultType:"date-time",allowedTypes:["date-time","date","period"],multiValue:",",detectType:function(t){return-1!==t.indexOf("/")?"period":-1===t.indexOf("T")?"date":"date-time"}},"recurrence-id":q,resources:W,"request-status":F,rrule:X,sequence:K,status:R,summary:R,transp:R,trigger:{defaultType:"duration",allowedTypes:["duration","date-time"]},tzoffsetfrom:G,tzoffsetto:G,tzurl:Z,tzid:R,tzname:R});const nt=S(et,{text:H(j,V),uri:H(j,V),date:{decorate:function(t){return C.fromDateAndOrTimeString(t,"date")},undecorate:function(t){return t.toString()},fromICAL:function(t){return 8==t.length?it.date.fromICAL(t):"-"==t[0]&&6==t.length?t.slice(0,4)+"-"+t.slice(4):t},toICAL:function(t){return 10==t.length?it.date.toICAL(t):"-"==t[0]&&7==t.length?t.slice(0,4)+t.slice(5):t}},time:{decorate:function(t){return C.fromDateAndOrTimeString("T"+t,"time")},undecorate:function(t){return t.toString()},fromICAL:function(t){let e=nt.time._splitZone(t,!0),i=e[0],r=e[1];return 6==r.length?r=r.slice(0,2)+":"+r.slice(2,4)+":"+r.slice(4,6):4==r.length&&"-"!=r[0]?r=r.slice(0,2)+":"+r.slice(2,4):5==r.length&&(r=r.slice(0,3)+":"+r.slice(3,5)),5!=i.length||"-"!=i[0]&&"+"!=i[0]||(i=i.slice(0,3)+":"+i.slice(3)),r+i},toICAL:function(t){let e=nt.time._splitZone(t),i=e[0],r=e[1];return 8==r.length?r=r.slice(0,2)+r.slice(3,5)+r.slice(6,8):5==r.length&&"-"!=r[0]?r=r.slice(0,2)+r.slice(3,5):6==r.length&&(r=r.slice(0,3)+r.slice(4,6)),6!=i.length||"-"!=i[0]&&"+"!=i[0]||(i=i.slice(0,3)+i.slice(4)),r+i},_splitZone:function(t,e){let i,r,n=t.length-1,s=t.length-(e?5:6),a=t[s];return"Z"==t[n]?(i=t[n],r=t.slice(0,Math.max(0,n))):t.length>6&&("-"==a||"+"==a)?(i=t.slice(s),r=t.slice(0,Math.max(0,s))):(i="",r=t),[i,r]}},"date-time":{decorate:function(t){return C.fromDateAndOrTimeString(t,"date-time")},undecorate:function(t){return t.toString()},fromICAL:function(t){return nt["date-and-or-time"].fromICAL(t)},toICAL:function(t){return nt["date-and-or-time"].toICAL(t)}},"date-and-or-time":{decorate:function(t){return C.fromDateAndOrTimeString(t,"date-and-or-time")},undecorate:function(t){return t.toString()},fromICAL:function(t){let e=t.split("T");return(e[0]?nt.date.fromICAL(e[0]):"")+(e[1]?"T"+nt.time.fromICAL(e[1]):"")},toICAL:function(t){let e=t.split("T");return nt.date.toICAL(e[0])+(e[1]?"T"+nt.time.toICAL(e[1]):"")}},timestamp:it["date-time"],"language-tag":{matches:/^[a-zA-Z0-9-]+$/},"phone-number":{fromICAL:function(t){return Array.from(t).filter((function(t){return"\\"===t?void 0:t})).join("")},toICAL:function(t){return Array.from(t).map((function(t){return","===t||";"===t?"\\"+t:t})).join("")}}});let st=S(tt,{adr:{defaultType:"text",structuredValue:";",multiValue:","},anniversary:Q,bday:Q,caladruri:Z,caluri:Z,clientpidmap:F,email:R,fburl:Z,fn:R,gender:F,geo:Z,impp:Z,key:Z,kind:R,lang:{defaultType:"language-tag"},logo:Z,member:Z,n:{defaultType:"text",structuredValue:";",multiValue:","},nickname:W,note:R,org:{defaultType:"text",structuredValue:";"},photo:Z,related:Z,rev:{defaultType:"timestamp"},role:R,sound:Z,source:Z,tel:{defaultType:"uri",allowedTypes:["uri","text"]},title:R,tz:{defaultType:"text",allowedTypes:["text","utc-offset","uri"]},xml:R}),at=S(et,{binary:it.binary,date:nt.date,"date-time":nt["date-time"],"phone-number":nt["phone-number"],uri:it.uri,text:nt.text,time:it.time,vcard:it.text,"utc-offset":{toICAL:function(t){return t.slice(0,7)},fromICAL:function(t){return t.slice(0,7)},decorate:function(t){return w.fromString(t)},undecorate:function(t){return t.toString()}}}),ot=S(tt,{fn:R,n:{defaultType:"text",structuredValue:";",multiValue:","},nickname:W,photo:{defaultType:"binary",allowedTypes:["binary","uri"]},bday:{defaultType:"date-time",allowedTypes:["date-time","date"],detectType:function(t){return-1===t.indexOf("T")?"date":"date-time"}},adr:{defaultType:"text",structuredValue:";",multiValue:","},label:R,tel:{defaultType:"phone-number"},email:R,mailer:R,tz:{defaultType:"utc-offset",allowedTypes:["utc-offset","text"]},geo:{defaultType:"float",structuredValue:";"},title:R,role:R,logo:{defaultType:"binary",allowedTypes:["binary","uri"]},agent:{defaultType:"vcard",allowedTypes:["vcard","text","uri"]},org:F,note:W,prodid:R,rev:{defaultType:"date-time",allowedTypes:["date-time","date"],detectType:function(t){return-1===t.indexOf("T")?"date":"date-time"}},"sort-string":R,sound:{defaultType:"binary",allowedTypes:["binary","uri"]},class:R,key:{defaultType:"binary",allowedTypes:["binary","text"]}}),lt={name:"ical",value:it,param:{cutype:{values:["INDIVIDUAL","GROUP","RESOURCE","ROOM","UNKNOWN"],allowXName:!0,allowIanaToken:!0},"delegated-from":{valueType:"cal-address",multiValue:",",multiValueSeparateDQuote:!0},"delegated-to":{valueType:"cal-address",multiValue:",",multiValueSeparateDQuote:!0},encoding:{values:["8BIT","BASE64"]},fbtype:{values:["FREE","BUSY","BUSY-UNAVAILABLE","BUSY-TENTATIVE"],allowXName:!0,allowIanaToken:!0},member:{valueType:"cal-address",multiValue:",",multiValueSeparateDQuote:!0},partstat:{values:["NEEDS-ACTION","ACCEPTED","DECLINED","TENTATIVE","DELEGATED","COMPLETED","IN-PROCESS"],allowXName:!0,allowIanaToken:!0},range:{values:["THISANDFUTURE"]},related:{values:["START","END"]},reltype:{values:["PARENT","CHILD","SIBLING"],allowXName:!0,allowIanaToken:!0},role:{values:["REQ-PARTICIPANT","CHAIR","OPT-PARTICIPANT","NON-PARTICIPANT"],allowXName:!0,allowIanaToken:!0},rsvp:{values:["TRUE","FALSE"]},"sent-by":{valueType:"cal-address"},tzid:{matches:/^\//},value:{values:["binary","boolean","cal-address","date","date-time","duration","float","integer","period","recur","text","time","uri","utc-offset"],allowXName:!0,allowIanaToken:!0}},property:rt,propertyGroups:!1},ht={name:"vcard4",value:nt,param:{type:{valueType:"text",multiValue:","},value:{values:["text","uri","date","time","date-time","date-and-or-time","timestamp","boolean","integer","float","utc-offset","language-tag"],allowXName:!0,allowIanaToken:!0}},property:st,propertyGroups:!0},ut={name:"vcard3",value:at,param:{type:{valueType:"text",multiValue:","},value:{values:["text","uri","date","date-time","phone-number","time","boolean","integer","float","utc-offset","vcard","binary"],allowXName:!0,allowIanaToken:!0}},property:ot,propertyGroups:!0};const ct={strict:!0,defaultSet:lt,defaultType:"unknown",components:{vcard:ht,vcard3:ut,vevent:lt,vtodo:lt,vjournal:lt,valarm:lt,vtimezone:lt,daylight:lt,standard:lt},icalendar:lt,vcard:ht,vcard3:ut,getDesignSet:function(t){return t&&t in ct.components?ct.components[t]:ct.defaultSet}},dt="\r\n",mt="unknown",ft={'"':"^'","\n":"^n","^":"^^"};function pt(t){"string"==typeof t[0]&&(t=[t]);let e=0,i=t.length,r="";for(;e0&&("version"!==t[1][0][0]||"4.0"!==t[1][0][3])&&(o="vcard3"),e=e||ct.getDesignSet(o);s0&&"object"==typeof t[0]&&"icaltype"in t[0]&&this.resetType(t[0].icaltype),this.isDecorated)for(;i=0;s--)i&&n[s][0]!==i||this._removeObjectByIndex(t,r,s)}addSubcomponent(t){this._components||(this._components=[],this._hydratedComponentCount=0),t.parent&&t.parent.removeSubcomponent(t);let e=this.jCal[2].push(t.jCal);return this._components[e-1]=t,this._hydratedComponentCount++,t.parent=this,t}removeSubcomponent(t){let e=this._removeObject(2,"_components",t);return e&&this._hydratedComponentCount--,e}removeAllSubcomponents(t){let e=this._removeAllObjects(2,"_components",t);return this._hydratedComponentCount=0,e}addProperty(t){if(!(t instanceof yt))throw new TypeError("must be instance of ICAL.Property");this._properties||(this._properties=[],this._hydratedPropertyCount=0),t.parent&&t.parent.removeProperty(t);let e=this.jCal[1].push(t.jCal);return this._properties[e-1]=t,this._hydratedPropertyCount++,t.parent=this,t}addPropertyWithValue(t,e){let i=new yt(t);return i.setValue(e),this.addProperty(i),i}updatePropertyWithValue(t,e){let i=this.getFirstProperty(t);return i?i.setValue(e):i=this.addPropertyWithValue(t,e),i}removeProperty(t){let e=this._removeObject(1,"_properties",t);return e&&this._hydratedPropertyCount--,e}removeAllProperties(t){let e=this._removeAllObjects(1,"_properties",t);return this._hydratedPropertyCount=0,e}toJSON(){return this.jCal}toString(){return pt.component(this.jCal,this._designSet)}getTimeZoneByID(t){if(this.parent)return this.parent.getTimeZoneByID(t);if(!this._timezoneCache)return null;if(this._timezoneCache.has(t))return this._timezoneCache.get(t);const e=this.getAllSubcomponents("vtimezone");for(const i of e)if(i.getFirstProperty("tzid").getFirstValue()===t){const e=new m({component:i,tzid:t});return this._timezoneCache.set(t,e),e}return null}}class gt{constructor(t){this.ruleDates=[],this.exDates=[],this.fromData(t)}complete=!1;ruleIterators=null;ruleDates=null;exDates=null;ruleDateInc=0;exDateInc=0;exDate=null;ruleDate=null;dtstart=null;last=null;fromData(t){let e=g(t.dtstart,a);if(!e)throw new Error(".dtstart (ICAL.Time) must be given");if(this.dtstart=e,t.component)this._init(t.component);else{if(this.last=g(t.last,a)||e.clone(),!t.ruleIterators)throw new Error(".ruleIterators or .component must be given");this.ruleIterators=t.ruleIterators.map((function(t){return g(t,x)})),this.ruleDateInc=t.ruleDateInc,this.exDateInc=t.exDateInc,t.ruleDates&&(this.ruleDates=t.ruleDates.map((t=>g(t,a))),this.ruleDate=this.ruleDates[this.ruleDateInc]),t.exDates&&(this.exDates=t.exDates.map((t=>g(t,a))),this.exDate=this.exDates[this.exDateInc]),void 0!==t.complete&&(this.complete=t.complete)}}_compare_special(t,e){return!t.isDate&&e.isDate?new a({year:t.year,month:t.month,day:t.day}).compare(e):t.compare(e)}next(){let t,e,i,r=0;for(;;){if(r++>500)throw new Error("max tries have occurred, rule may be impossible to fulfill.");if(e=this.ruleDate,t=this._nextRecurrenceIter(this.last),!e&&!t){this.complete=!0;break}if((!e||t&&e.compare(t.last)>0)&&(e=t.last.clone(),t.next()),this.ruleDate===e&&this._nextRuleDay(),this.last=e,!this.exDate||(i=this._compare_special(this.last,this.exDate),i>0&&this._nextExDay(),0!==i))return this.last;this._nextExDay()}}toJSON(){function t(t){return t.toJSON()}let e=Object.create(null);return e.ruleIterators=this.ruleIterators.map(t),this.ruleDates&&(e.ruleDates=this.ruleDates.map(t)),this.exDates&&(e.exDates=this.exDates.map(t)),e.ruleDateInc=this.ruleDateInc,e.exDateInc=this.exDateInc,e.last=this.last.toJSON(),e.dtstart=this.dtstart.toJSON(),e.complete=this.complete,e}_extractDates(t,e){let i=[],r=t.getAllProperties(e);for(let t=0,e=r.length;tt.compare(e)));i.splice(t,0,e)}return i}_init(t){if(this.ruleIterators=[],this.last=this.dtstart.clone(),!t.hasProperty("rdate")&&!t.hasProperty("rrule")&&!t.hasProperty("recurrence-id"))return this.ruleDate=this.last.clone(),void(this.complete=!0);if(t.hasProperty("rdate")&&(this.ruleDates=this._extractDates(t,"rdate"),this.ruleDates[0]&&this.ruleDates[0].compare(this.dtstart)<0?(this.ruleDateInc=0,this.last=this.ruleDates[0].clone()):this.ruleDateInc=T(this.ruleDates,this.last,((t,e)=>t.compare(e))),this.ruleDate=this.ruleDates[this.ruleDateInc]),t.hasProperty("rrule")){let e,i,r=t.getAllProperties("rrule"),n=0,s=r.length;for(;n0)&&(r=e);return r}}class Dt{constructor(t,e){t instanceof _t||(e=t,t=null),this.component=t||new _t("vevent"),this._rangeExceptionCache=Object.create(null),this.exceptions=Object.create(null),this.rangeExceptions=[],e&&e.strictExceptions&&(this.strictExceptions=e.strictExceptions),e&&e.exceptions?e.exceptions.forEach(this.relateException,this):this.component.parent&&!this.isRecurrenceException()&&this.component.parent.getAllSubcomponents("vevent").forEach((function(t){t.hasProperty("recurrence-id")&&this.relateException(t)}),this)}static THISANDFUTURE="THISANDFUTURE";exceptions=null;strictExceptions=!1;relateException(t){if(this.isRecurrenceException())throw new Error("cannot relate exception to exceptions");if(t instanceof _t&&(t=new Dt(t)),this.strictExceptions&&t.uid!==this.uid)throw new Error("attempted to relate unrelated exception");let e=t.recurrenceId.toString();if(this.exceptions[e]=t,t.modifiesFuture()){let i=[t.recurrenceId.toUnixTime(),e],r=T(this.rangeExceptions,i,Tt);this.rangeExceptions.splice(r,0,i)}}modifiesFuture(){if(!this.component.hasProperty("recurrence-id"))return!1;return this.component.getFirstProperty("recurrence-id").getParameter("range")===Dt.THISANDFUTURE}findRangeException(t){if(!this.rangeExceptions.length)return null;let e=t.toUnixTime(),i=T(this.rangeExceptions,[e],Tt);if(i-=1,i<0)return null;let r=this.rangeExceptions[i];return ee[0]?1:e[0]>t[0]?-1:0}var Yt={foldLength:75,debug:!1,newLineChar:"\r\n",Binary:t,Component:_t,ComponentParser:class{constructor(t){void 0===t&&(t={});for(let[e,i]of Object.entries(t))this[e]=i}parseEvent=!0;parseTimezone=!0;oncomplete=function(){};onerror=function(t){};ontimezone=function(t){};onevent=function(t){};process(t){"string"==typeof t&&(t=u(t)),t instanceof _t||(t=new _t(t));let e,i=t.getAllSubcomponents(),r=0,n=i.length;for(;r void;
+ /**
+ * Fired if an error occurs during parsing.
+ *
+ * @callback
+ * @param {Error} err details of error
+ */
+ onerror: (err: any) => void;
+ /**
+ * Fired when a top level component (VTIMEZONE) is found
+ *
+ * @callback
+ * @param {Timezone} component Timezone object
+ */
+ ontimezone: (component: any) => void;
+ /**
+ * Fired when a top level component (VEVENT) is found.
+ *
+ * @callback
+ * @param {Event} component Top level component
+ */
+ onevent: (component: any) => void;
+ /**
+ * Process a string or parse ical object. This function itself will return
+ * nothing but will start the parsing process.
+ *
+ * Events must be registered prior to calling this method.
+ *
+ * @param {Component|String|Object} ical The component to process,
+ * either in its final form, as a jCal Object, or string representation
+ */
+ process(ical: Component | string | any): void;
+}
+import Component from "./component.js";
+//# sourceMappingURL=component_parser.d.ts.map
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/component_parser.d.ts.map b/cron/scripts/node_modules/ical.js/dist/types/component_parser.d.ts.map
new file mode 100644
index 0000000..7d9d80a
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/component_parser.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"component_parser.d.ts","sourceRoot":"","sources":["../../lib/ical/component_parser.js"],"names":[],"mappings":";AAUA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH;IACE;;;;;;OAMG;IACH,uCAQC;IAED;;;;OAIG;IACH,oBAAkB;IAElB;;;;OAIG;IACH,uBAAqB;IAKrB;;;OAGG;IACH,uBAAgD;IAEhD;;;;;OAKG;IACH,4BAAgD;IAEhD;;;;;OAKG;IACH,qCAAyD;IAEzD;;;;;OAKG;IACH,kCAAsD;IAEtD;;;;;;;;OAQG;IACH,cAHW,SAAS,eAAc,QA8CjC;CACF;sBArJqB,gBAAgB"}
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/design.d.ts b/cron/scripts/node_modules/ical.js/dist/types/design.d.ts
new file mode 100644
index 0000000..910de55
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/design.d.ts
@@ -0,0 +1,31 @@
+export default design;
+/**
+ * Imports the 'designSet' type from the "types.js" module
+ */
+export type designSet = import("./types.js").designSet;
+declare namespace design {
+ export let strict: boolean;
+ export { icalSet as defaultSet };
+ export let defaultType: string;
+ export let components: any;
+ export { icalSet as icalendar };
+ export { vcardSet as vcard };
+ export { vcard3Set as vcard3 };
+ export function getDesignSet(componentName: string): import("./types.js").designSet;
+}
+/**
+ * iCalendar design set
+ * @type {designSet}
+ */
+declare let icalSet: designSet;
+/**
+ * vCard 4.0 design set
+ * @type {designSet}
+ */
+declare let vcardSet: designSet;
+/**
+ * vCard 3.0 design set
+ * @type {designSet}
+ */
+declare let vcard3Set: designSet;
+//# sourceMappingURL=design.d.ts.map
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/design.d.ts.map b/cron/scripts/node_modules/ical.js/dist/types/design.d.ts.map
new file mode 100644
index 0000000..f2d3001
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/design.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"design.d.ts","sourceRoot":"","sources":["../../lib/ical/design.js"],"names":[],"mappings":";;;;wBAmBa,OAAO,YAAY,EAAE,SAAS;;;;;;;;;IA4+B3B,oFAGb;;AA1HH;;;GAGG;AACH,qBAFU,SAAS,CAQjB;AAEF;;;GAGG;AACH,sBAFU,SAAS,CAQjB;AAEF;;;GAGG;AACH,uBAFU,SAAS,CAQjB"}
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/duration.d.ts b/cron/scripts/node_modules/ical.js/dist/types/duration.d.ts
new file mode 100644
index 0000000..934215a
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/duration.d.ts
@@ -0,0 +1,188 @@
+export default Duration;
+/**
+ * This class represents the "duration" value type, with various calculation
+ * and manipulation methods.
+ *
+ * @memberof ICAL
+ */
+declare class Duration {
+ /**
+ * Returns a new ICAL.Duration instance from the passed seconds value.
+ *
+ * @param {Number} aSeconds The seconds to create the instance from
+ * @return {Duration} The newly created duration instance
+ */
+ static fromSeconds(aSeconds: number): Duration;
+ /**
+ * Checks if the given string is an iCalendar duration value.
+ *
+ * @param {String} value The raw ical value
+ * @return {Boolean} True, if the given value is of the
+ * duration ical type
+ */
+ static isValueString(string: any): boolean;
+ /**
+ * Creates a new {@link ICAL.Duration} instance from the passed string.
+ *
+ * @param {String} aStr The string to parse
+ * @return {Duration} The created duration instance
+ */
+ static fromString(aStr: string): Duration;
+ /**
+ * Creates a new ICAL.Duration instance from the given data object.
+ *
+ * @param {Object} aData An object with members of the duration
+ * @param {Number=} aData.weeks Duration in weeks
+ * @param {Number=} aData.days Duration in days
+ * @param {Number=} aData.hours Duration in hours
+ * @param {Number=} aData.minutes Duration in minutes
+ * @param {Number=} aData.seconds Duration in seconds
+ * @param {Boolean=} aData.isNegative If true, the duration is negative
+ * @return {Duration} The createad duration instance
+ */
+ static fromData(aData: {
+ weeks?: number | undefined;
+ days?: number | undefined;
+ hours?: number | undefined;
+ minutes?: number | undefined;
+ seconds?: number | undefined;
+ isNegative?: boolean | undefined;
+ }): Duration;
+ /**
+ * Creates a new ICAL.Duration instance.
+ *
+ * @param {Object} data An object with members of the duration
+ * @param {Number=} data.weeks Duration in weeks
+ * @param {Number=} data.days Duration in days
+ * @param {Number=} data.hours Duration in hours
+ * @param {Number=} data.minutes Duration in minutes
+ * @param {Number=} data.seconds Duration in seconds
+ * @param {Boolean=} data.isNegative If true, the duration is negative
+ */
+ constructor(data: {
+ weeks?: number | undefined;
+ days?: number | undefined;
+ hours?: number | undefined;
+ minutes?: number | undefined;
+ seconds?: number | undefined;
+ isNegative?: boolean | undefined;
+ });
+ wrappedJSObject: this;
+ /**
+ * The weeks in this duration
+ * @type {Number}
+ * @default 0
+ */
+ weeks: number;
+ /**
+ * The days in this duration
+ * @type {Number}
+ * @default 0
+ */
+ days: number;
+ /**
+ * The days in this duration
+ * @type {Number}
+ * @default 0
+ */
+ hours: number;
+ /**
+ * The minutes in this duration
+ * @type {Number}
+ * @default 0
+ */
+ minutes: number;
+ /**
+ * The seconds in this duration
+ * @type {Number}
+ * @default 0
+ */
+ seconds: number;
+ /**
+ * The seconds in this duration
+ * @type {Boolean}
+ * @default false
+ */
+ isNegative: boolean;
+ /**
+ * The class identifier.
+ * @constant
+ * @type {String}
+ * @default "icalduration"
+ */
+ icalclass: string;
+ /**
+ * The type name, to be used in the jCal object.
+ * @constant
+ * @type {String}
+ * @default "duration"
+ */
+ icaltype: string;
+ /**
+ * Returns a clone of the duration object.
+ *
+ * @return {Duration} The cloned object
+ */
+ clone(): Duration;
+ /**
+ * The duration value expressed as a number of seconds.
+ *
+ * @return {Number} The duration value in seconds
+ */
+ toSeconds(): number;
+ /**
+ * Reads the passed seconds value into this duration object. Afterwards,
+ * members like {@link ICAL.Duration#days days} and {@link ICAL.Duration#weeks weeks} will be set up
+ * accordingly.
+ *
+ * @param {Number} aSeconds The duration value in seconds
+ * @return {Duration} Returns this instance
+ */
+ fromSeconds(aSeconds: number): Duration;
+ /**
+ * Sets up the current instance using members from the passed data object.
+ *
+ * @param {Object} aData An object with members of the duration
+ * @param {Number=} aData.weeks Duration in weeks
+ * @param {Number=} aData.days Duration in days
+ * @param {Number=} aData.hours Duration in hours
+ * @param {Number=} aData.minutes Duration in minutes
+ * @param {Number=} aData.seconds Duration in seconds
+ * @param {Boolean=} aData.isNegative If true, the duration is negative
+ */
+ fromData(aData: {
+ weeks?: number | undefined;
+ days?: number | undefined;
+ hours?: number | undefined;
+ minutes?: number | undefined;
+ seconds?: number | undefined;
+ isNegative?: boolean | undefined;
+ }): void;
+ /**
+ * Resets the duration instance to the default values, i.e. PT0S
+ */
+ reset(): void;
+ /**
+ * Compares the duration instance with another one.
+ *
+ * @param {Duration} aOther The instance to compare with
+ * @return {Number} -1, 0 or 1 for less/equal/greater
+ */
+ compare(aOther: Duration): number;
+ /**
+ * Normalizes the duration instance. For example, a duration with a value
+ * of 61 seconds will be normalized to 1 minute and 1 second.
+ */
+ normalize(): void;
+ /**
+ * The string representation of this duration.
+ * @return {String}
+ */
+ toString(): string;
+ /**
+ * The iCalendar string representation of this duration.
+ * @return {String}
+ */
+ toICALString(): string;
+}
+//# sourceMappingURL=duration.d.ts.map
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/duration.d.ts.map b/cron/scripts/node_modules/ical.js/dist/types/duration.d.ts.map
new file mode 100644
index 0000000..fb217d1
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/duration.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"duration.d.ts","sourceRoot":"","sources":["../../lib/ical/duration.js"],"names":[],"mappings":";AAUA;;;;;GAKG;AACH;IACE;;;;;OAKG;IACH,sCAFY,QAAQ,CAInB;IAED;;;;;;OAMG;IACH,2CAEC;IAED;;;;;OAKG;IACH,iCAFY,QAAQ,CAuBnB;IAED;;;;;;;;;;;OAWG;IACH;QAR0B,KAAK;QACL,IAAI;QACJ,KAAK;QACL,OAAO;QACP,OAAO;QACN,UAAU;QACzB,QAAQ,CAInB;IAED;;;;;;;;;;OAUG;IACH;QAPyB,KAAK;QACL,IAAI;QACJ,KAAK;QACL,OAAO;QACP,OAAO;QACN,UAAU;OAKnC;IAFC,sBAA2B;IAI7B;;;;OAIG;IACH,cAAU;IAEV;;;;OAIG;IACH,aAAS;IAET;;;;OAIG;IACH,cAAU;IAEV;;;;OAIG;IACH,gBAAY;IAEZ;;;;OAIG;IACH,gBAAY;IAEZ;;;;OAIG;IACH,oBAAmB;IAEnB;;;;;OAKG;IACH,kBAA2B;IAE3B;;;;;OAKG;IACH,iBAAsB;IAEtB;;;;OAIG;IACH,SAFY,QAAQ,CAInB;IAED;;;;OAIG;IACH,oBAIC;IAED;;;;;;;OAOG;IACH,+BAFY,QAAQ,CA0BnB;IAED;;;;;;;;;;OAUG;IACH;QAP0B,KAAK;QACL,IAAI;QACJ,KAAK;QACL,OAAO;QACP,OAAO;QACN,UAAU;aAUpC;IAED;;OAEG;IACH,cAOC;IAED;;;;;OAKG;IACH,gBAHW,QAAQ,UAOlB;IAED;;;OAGG;IACH,kBAEC;IAED;;;OAGG;IACH,mBAsCC;IAED;;;OAGG;IACH,uBAEC;CACF"}
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/event.d.ts b/cron/scripts/node_modules/ical.js/dist/types/event.d.ts
new file mode 100644
index 0000000..7ceda59
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/event.d.ts
@@ -0,0 +1,240 @@
+export default Event;
+/**
+ * Imports the 'frequencyValues' type from the "types.js" module
+ */
+export type frequencyValues = import("./types.js").frequencyValues;
+/**
+ * Imports the 'occurrenceDetails' type from the "types.js" module
+ */
+export type occurrenceDetails = import("./types.js").occurrenceDetails;
+/**
+ * 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").frequencyValues} frequencyValues
+ * Imports the 'frequencyValues' type from the "types.js" module
+ * @typedef {import("./types.js").occurrenceDetails} occurrenceDetails
+ * Imports the 'occurrenceDetails' type from the "types.js" module
+ */
+/**
+ * ICAL.js is organized into multiple layers. The bottom layer is a raw jCal
+ * object, followed by the component/property layer. The highest level is the
+ * event representation, which this class is part of. See the
+ * {@tutorial layers} guide for more details.
+ *
+ * @memberof ICAL
+ */
+declare class Event {
+ static THISANDFUTURE: string;
+ /**
+ * Creates a new ICAL.Event instance.
+ *
+ * @param {Component=} component The ICAL.Component to base this event on
+ * @param {Object} [options] Options for this event
+ * @param {Boolean=} options.strictExceptions When true, will verify exceptions are related by
+ * their UUID
+ * @param {Array=} options.exceptions
+ * Exceptions to this event, either as components or events. If not
+ * specified exceptions will automatically be set in relation of
+ * component's parent
+ */
+ constructor(component?: Component | undefined, options?: {
+ strictExceptions?: boolean | undefined;
+ exceptions?: Array | undefined;
+ });
+ component: Component;
+ _rangeExceptionCache: any;
+ /**
+ * List of related event exceptions.
+ *
+ * @type {Event[]}
+ */
+ exceptions: Event[];
+ rangeExceptions: any[];
+ /**
+ * When true, will verify exceptions are related by their UUID.
+ *
+ * @type {Boolean}
+ */
+ strictExceptions: boolean;
+ /**
+ * Relates a given event exception to this object. If the given component
+ * does not share the UID of this event it cannot be related and will throw
+ * an exception.
+ *
+ * If this component is an exception it cannot have other exceptions
+ * related to it.
+ *
+ * @param {Component|Event} obj Component or event
+ */
+ relateException(obj: Component | Event): void;
+ /**
+ * Checks if this record is an exception and has the RANGE=THISANDFUTURE
+ * value.
+ *
+ * @return {Boolean} True, when exception is within range
+ */
+ modifiesFuture(): boolean;
+ /**
+ * Finds the range exception nearest to the given date.
+ *
+ * @param {Time} time usually an occurrence time of an event
+ * @return {?Event} the related event/exception or null
+ */
+ findRangeException(time: Time): Event | null;
+ /**
+ * Returns the occurrence details based on its start time. If the
+ * occurrence has an exception will return the details for that exception.
+ *
+ * NOTE: this method is intend to be used in conjunction
+ * with the {@link ICAL.Event#iterator iterator} method.
+ *
+ * @param {Time} occurrence time occurrence
+ * @return {occurrenceDetails} Information about the occurrence
+ */
+ getOccurrenceDetails(occurrence: Time): occurrenceDetails;
+ /**
+ * Builds a recur expansion instance for a specific point in time (defaults
+ * to startDate).
+ *
+ * @param {Time=} startTime Starting point for expansion
+ * @return {RecurExpansion} Expansion object
+ */
+ iterator(startTime?: Time | undefined): RecurExpansion;
+ /**
+ * Checks if the event is recurring
+ *
+ * @return {Boolean} True, if event is recurring
+ */
+ isRecurring(): boolean;
+ /**
+ * Checks if the event describes a recurrence exception. See
+ * {@tutorial terminology} for details.
+ *
+ * @return {Boolean} True, if the event describes a recurrence exception
+ */
+ isRecurrenceException(): boolean;
+ /**
+ * Returns the types of recurrences this event may have.
+ *
+ * Returned as an object with the following possible keys:
+ *
+ * - YEARLY
+ * - MONTHLY
+ * - WEEKLY
+ * - DAILY
+ * - MINUTELY
+ * - SECONDLY
+ *
+ * @return {Object.}
+ * Object of recurrence flags
+ */
+ getRecurrenceTypes(): any;
+ set uid(value: string);
+ /**
+ * The uid of this event
+ * @type {String}
+ */
+ get uid(): string;
+ set startDate(value: Time);
+ /**
+ * The start date
+ * @type {Time}
+ */
+ get startDate(): Time;
+ set endDate(value: Time);
+ /**
+ * The end date. This can be the result directly from the property, or the
+ * end date calculated from start date and duration. Setting the property
+ * will remove any duration properties.
+ * @type {Time}
+ */
+ get endDate(): Time;
+ set duration(value: Duration);
+ /**
+ * The duration. This can be the result directly from the property, or the
+ * duration calculated from start date and end date. Setting the property
+ * will remove any `dtend` properties.
+ * @type {Duration}
+ */
+ get duration(): Duration;
+ set location(value: string);
+ /**
+ * The location of the event.
+ * @type {String}
+ */
+ get location(): string;
+ /**
+ * The attendees in the event
+ * @type {Property[]}
+ */
+ get attendees(): Property[];
+ set summary(value: string);
+ /**
+ * The event summary
+ * @type {String}
+ */
+ get summary(): string;
+ set description(value: string);
+ /**
+ * The event description.
+ * @type {String}
+ */
+ get description(): string;
+ set color(value: string);
+ /**
+ * The event color from [rfc7986](https://datatracker.ietf.org/doc/html/rfc7986)
+ * @type {String}
+ */
+ get color(): string;
+ set organizer(value: string);
+ /**
+ * The organizer value as an uri. In most cases this is a mailto: uri, but
+ * it can also be something else, like urn:uuid:...
+ * @type {String}
+ */
+ get organizer(): string;
+ set sequence(value: number);
+ /**
+ * The sequence value for this event. Used for scheduling
+ * see {@tutorial terminology}.
+ * @type {Number}
+ */
+ get sequence(): number;
+ set recurrenceId(value: Time);
+ /**
+ * The recurrence id for this event. See {@tutorial terminology} for details.
+ * @type {Time}
+ */
+ get recurrenceId(): Time;
+ /**
+ * Set/update a time property's value.
+ * This will also update the TZID of the property.
+ *
+ * TODO: this method handles the case where we are switching
+ * from a known timezone to an implied timezone (one without TZID).
+ * This does _not_ handle the case of moving between a known
+ * (by TimezoneService) timezone to an unknown timezone...
+ *
+ * We will not add/remove/update the VTIMEZONE subcomponents
+ * leading to invalid ICAL data...
+ * @private
+ * @param {String} propName The property name
+ * @param {Time} time The time to set
+ */
+ private _setTime;
+ _setProp(name: any, value: any): void;
+ _firstProp(name: any): string | import("./binary.js").default | Duration | Time | import("./recur.js").default | import("./period.js").default | import("./utc_offset.js").default | import("./types.js").Geo;
+ /**
+ * The string representation of this event.
+ * @return {String}
+ */
+ toString(): string;
+}
+import Component from "./component.js";
+import Time from "./time.js";
+import RecurExpansion from "./recur_expansion.js";
+import Duration from "./duration.js";
+import Property from "./property.js";
+//# sourceMappingURL=event.d.ts.map
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/event.d.ts.map b/cron/scripts/node_modules/ical.js/dist/types/event.d.ts.map
new file mode 100644
index 0000000..7e309ed
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/event.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"event.d.ts","sourceRoot":"","sources":["../../lib/ical/event.js"],"names":[],"mappings":";;;;8BAqBa,OAAO,YAAY,EAAE,eAAe;;;;gCAEpC,OAAO,YAAY,EAAE,iBAAiB;AAPnD;;;;;;;;;GASG;AAEH;;;;;;;GAOG;AACH;IA6CE,6BAAuC;IA5CvC;;;;;;;;;;;OAWG;IACH,wBATW,SAAS;QAES,gBAAgB;QAED,UAAU,GAA3C,MAAM,SAAS,GAAC,KAAK,CAAC;OAkChC;IAtBG,qBAA0B;IAK5B,0BAA+C;IAsBjD;;;;OAIG;IACH,YAFU,KAAK,EAAE,CAEC;IAzBhB,uBAAyB;IA2B3B;;;;OAIG;IACH,0BAAyB;IAEzB;;;;;;;;;OASG;IACH,qBAFW,SAAS,GAAC,KAAK,QAsCzB;IAED;;;;;OAKG;IACH,0BAOC;IAED;;;;;OAKG;IACH,yBAHW,IAAI,GACF,KAAK,QA8BjB;IAED;;;;;;;;;OASG;IACH,iCAHW,IAAI,GACH,iBAAiB,CAwE5B;IAED;;;;;;OAMG;IACH,qBAHW,IAAI,eACH,cAAc,CAOzB;IAED;;;;OAIG;IACH,uBAGC;IAED;;;;;OAKG;IACH,iCAEC;IAED;;;;;;;;;;;;;;OAcG;IACH,0BAYC;IAUD,uBAEC;IAVD;;;OAGG;IACH,kBAEC;IAcD,2BAEC;IAVD;;;OAGG;IACH,sBAEC;IA0BD,yBAKC;IAzBD;;;;;OAKG;IACH,oBAYC;IAuBD,8BAMC;IApBD;;;;;OAKG;IACH,yBAMC;IAkBD,4BAEC;IAVD;;;OAGG;IACH,uBAEC;IAMD;;;OAGG;IACH,4BAIC;IAUD,2BAEC;IAVD;;;OAGG;IACH,sBAEC;IAcD,+BAEC;IAVD;;;OAGG;IACH,0BAEC;IAcD,yBAEC;IAVD;;;OAGG;IACH,oBAEC;IAeD,6BAEC;IAXD;;;;OAIG;IACH,wBAEC;IAeD,4BAEC;IAXD;;;;OAIG;IACH,uBAEC;IAcD,8BAEC;IAVD;;;OAGG;IACH,yBAEC;IAMD;;;;;;;;;;;;;;OAcG;IACH,iBAoBC;IAED,sCAEC;IAED,8MAEC;IAED;;;OAGG;IACH,mBAEC;CACF;sBA3iBqB,gBAAgB;iBAKrB,WAAW;2BAGD,sBAAsB;qBAD5B,eAAe;qBANf,eAAe"}
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/helpers.d.ts b/cron/scripts/node_modules/ical.js/dist/types/helpers.d.ts
new file mode 100644
index 0000000..4bb7305
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/helpers.d.ts
@@ -0,0 +1,123 @@
+/**
+ * Helper functions used in various places within ical.js
+ * @module ICAL.helpers
+ */
+/**
+ * Compiles a list of all referenced TZIDs in all subcomponents and
+ * removes any extra VTIMEZONE subcomponents. In addition, if any TZIDs
+ * are referenced by a component, but a VTIMEZONE does not exist,
+ * an attempt will be made to generate a VTIMEZONE using ICAL.TimezoneService.
+ *
+ * @param {Component} vcal The top-level VCALENDAR component.
+ * @return {Component} The ICAL.Component that was passed in.
+ */
+export function updateTimezones(vcal: Component): Component;
+/**
+ * Checks if the given type is of the number type and also NaN.
+ *
+ * @param {Number} number The number to check
+ * @return {Boolean} True, if the number is strictly NaN
+ */
+export function isStrictlyNaN(number: number): boolean;
+/**
+ * Parses a string value that is expected to be an integer, when the valid is
+ * not an integer throws a decoration error.
+ *
+ * @param {String} string Raw string input
+ * @return {Number} Parsed integer
+ */
+export function strictParseInt(string: string): number;
+/**
+ * Creates or returns a class instance of a given type with the initialization
+ * data if the data is not already an instance of the given type.
+ *
+ * @example
+ * var time = new ICAL.Time(...);
+ * var result = ICAL.helpers.formatClassType(time, ICAL.Time);
+ *
+ * (result instanceof ICAL.Time)
+ * // => true
+ *
+ * result = ICAL.helpers.formatClassType({}, ICAL.Time);
+ * (result isntanceof ICAL.Time)
+ * // => true
+ *
+ *
+ * @param {Object} data object initialization data
+ * @param {Object} type object type (like ICAL.Time)
+ * @return {?} An instance of the found type.
+ */
+export function formatClassType(data: any, type: any): unknown;
+/**
+ * Identical to indexOf but will only match values when they are not preceded
+ * by a backslash character.
+ *
+ * @param {String} buffer String to search
+ * @param {String} search Value to look for
+ * @param {Number} pos Start position
+ * @return {Number} The position, or -1 if not found
+ */
+export function unescapedIndexOf(buffer: string, search: string, pos: number): number;
+/**
+ * Find the index for insertion using binary search.
+ *
+ * @param {Array} list The list to search
+ * @param {?} seekVal The value to insert
+ * @param {function(?,?)} cmpfunc The comparison func, that can
+ * compare two seekVals
+ * @return {Number} The insert position
+ */
+export function binsearchInsert(list: any[], seekVal: unknown, cmpfunc: (arg0: unknown, arg1: unknown) => any): number;
+/**
+ * Clone the passed object or primitive. By default a shallow clone will be
+ * executed.
+ *
+ * @param {*} aSrc The thing to clone
+ * @param {Boolean=} aDeep If true, a deep clone will be performed
+ * @return {*} The copy of the thing
+ */
+export function clone(aSrc: any, aDeep?: boolean | undefined): any;
+/**
+ * Performs iCalendar line folding. A line ending character is inserted and
+ * the next line begins with a whitespace.
+ *
+ * @example
+ * SUMMARY:This line will be fold
+ * ed right in the middle of a word.
+ *
+ * @param {String} aLine The line to fold
+ * @return {String} The folded line
+ */
+export function foldline(aLine: string): string;
+/**
+ * Pads the given string or number with zeros so it will have at least two
+ * characters.
+ *
+ * @param {String|Number} data The string or number to pad
+ * @return {String} The number padded as a string
+ */
+export function pad2(data: string | number): string;
+/**
+ * Truncates the given number, correctly handling negative numbers.
+ *
+ * @param {Number} number The number to truncate
+ * @return {Number} The truncated number
+ */
+export function trunc(number: number): number;
+/**
+ * Poor-man's cross-browser object extension. Doesn't support all the
+ * features, but enough for our usage. Note that the target's properties are
+ * not overwritten with the source properties.
+ *
+ * @example
+ * var child = ICAL.helpers.extend(parent, {
+ * "bar": 123
+ * });
+ *
+ * @param {Object} source The object to extend
+ * @param {Object} target The object to extend with
+ * @return {Object} Returns the target.
+ */
+export function extend(source: any, target: any): any;
+import Component from "./component.js";
+//# sourceMappingURL=helpers.d.ts.map
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/helpers.d.ts.map b/cron/scripts/node_modules/ical.js/dist/types/helpers.d.ts.map
new file mode 100644
index 0000000..87c5f89
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/helpers.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../lib/ical/helpers.js"],"names":[],"mappings":"AAWA;;;GAGG;AAEH;;;;;;;;GAQG;AACH,sCAHW,SAAS,GACR,SAAS,CAgDpB;AAED;;;;;GAKG;AACH,uDAEC;AAED;;;;;;GAMG;AACH,uDAUC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,+DASC;AAED;;;;;;;;GAQG;AACH,sFASC;AAED;;;;;;;;GAQG;AACH,uHAyBC;AAED;;;;;;;GAOG;AACH,mEAwBC;AAED;;;;;;;;;;GAUG;AACH,gDAoBC;AAED;;;;;;GAMG;AACH,2BAHW,eAAa,UAsBvB;AAED;;;;;GAKG;AACH,8CAEC;AAED;;;;;;;;;;;;;GAaG;AACH,sDAQC;sBApTqB,gBAAgB"}
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/module.d.ts b/cron/scripts/node_modules/ical.js/dist/types/module.d.ts
new file mode 100644
index 0000000..243ffc4
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/module.d.ts
@@ -0,0 +1,45 @@
+declare namespace _default {
+ export let foldLength: number;
+ export let debug: boolean;
+ export let newLineChar: string;
+ export { Binary };
+ export { Component };
+ export { ComponentParser };
+ export { Duration };
+ export { Event };
+ export { Period };
+ export { Property };
+ export { Recur };
+ export { RecurExpansion };
+ export { RecurIterator };
+ export { Time };
+ export { Timezone };
+ export { TimezoneService };
+ export { UtcOffset };
+ export { VCardTime };
+ export { parse };
+ export { stringify };
+ export { design };
+ export { helpers };
+}
+export default _default;
+import Binary from "./binary.js";
+import Component from "./component.js";
+import ComponentParser from "./component_parser.js";
+import Duration from "./duration.js";
+import Event from "./event.js";
+import Period from "./period.js";
+import Property from "./property.js";
+import Recur from "./recur.js";
+import RecurExpansion from "./recur_expansion.js";
+import RecurIterator from "./recur_iterator.js";
+import Time from "./time.js";
+import Timezone from "./timezone.js";
+import TimezoneService from "./timezone_service.js";
+import UtcOffset from "./utc_offset.js";
+import VCardTime from "./vcard_time.js";
+import parse from "./parse.js";
+import stringify from "./stringify.js";
+import design from "./design.js";
+import * as helpers from "./helpers.js";
+//# sourceMappingURL=module.d.ts.map
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/module.d.ts.map b/cron/scripts/node_modules/ical.js/dist/types/module.d.ts.map
new file mode 100644
index 0000000..b70f32f
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/module.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../lib/ical/module.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;mBAKmB,aAAa;sBACV,gBAAgB;4BACV,uBAAuB;qBAE9B,eAAe;kBAClB,YAAY;mBAGX,aAAa;qBACX,eAAe;kBAClB,YAAY;2BACH,sBAAsB;0BACvB,qBAAqB;iBAE9B,WAAW;qBACP,eAAe;4BACR,uBAAuB;sBAC7B,iBAAiB;sBACjB,iBAAiB;kBAXrB,YAAY;sBAMR,gBAAgB;mBAVnB,aAAa;yBAGP,cAAc"}
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/parse.d.ts b/cron/scripts/node_modules/ical.js/dist/types/parse.d.ts
new file mode 100644
index 0000000..fe0b0d6
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/parse.d.ts
@@ -0,0 +1,124 @@
+/**
+ * Parses iCalendar or vCard data into a raw jCal object. Consult
+ * documentation on the {@tutorial layers|layers of parsing} for more
+ * details.
+ *
+ * @function ICAL.parse
+ * @memberof ICAL
+ * @variation function
+ * @todo Fix the API to be more clear on the return type
+ * @param {String} input The string data to parse
+ * @return {Object|Object[]} A single jCal object, or an array thereof
+ */
+declare function parse(input: string): any | any[];
+declare namespace parse {
+ /**
+ * Parse an iCalendar property value into the jCal for a single property
+ *
+ * @function ICAL.parse.property
+ * @param {String} str
+ * The iCalendar property string to parse
+ * @param {designSet=} designSet
+ * The design data to use for this property
+ * @return {Object}
+ * The jCal Object containing the property
+ */
+ export function property(str: string, designSet?: import("./types.js").designSet): any;
+ /**
+ * Convenience method to parse a component. You can use ICAL.parse() directly
+ * instead.
+ *
+ * @function ICAL.parse.component
+ * @see ICAL.parse(function)
+ * @param {String} str The iCalendar component string to parse
+ * @return {Object} The jCal Object containing the component
+ */
+ export function component(str: string): any;
+ export { ParserError };
+ /**
+ * Handles a single line of iCalendar/vCard, updating the state.
+ *
+ * @private
+ * @function ICAL.parse._handleContentLine
+ * @param {String} line The content line to process
+ * @param {parserState} state The current state of the line parsing
+ */
+ export function _handleContentLine(line: string, state: import("./types.js").parserState): void;
+ /**
+ * Parse a value from the raw value into the jCard/jCal value.
+ *
+ * @private
+ * @function ICAL.parse._parseValue
+ * @param {String} value Original value
+ * @param {String} type Type of value
+ * @param {Object} designSet The design data to use for this value
+ * @return {Object} varies on type
+ */
+ export function _parseValue(value: string, type: string, designSet: any, structuredValue: any): any;
+ /**
+ * Parse parameters from a string to object.
+ *
+ * @function ICAL.parse._parseParameters
+ * @private
+ * @param {String} line A single unfolded line
+ * @param {Number} start Position to start looking for properties
+ * @param {Object} designSet The design data to use for this property
+ * @return {Array} Array containing key/valye pairs of parsed parameters, the
+ * parsed value, and the position of the last parameter found
+ */
+ export function _parseParameters(line: string, start: number, designSet: any): any[];
+ /**
+ * Internal helper for rfc6868. Exposing this on ICAL.parse so that
+ * hackers can disable the rfc6868 parsing if the really need to.
+ *
+ * @function ICAL.parse._rfc6868Escape
+ * @param {String} val The value to escape
+ * @return {String} The escaped value
+ */
+ export function _rfc6868Escape(val: string): string;
+ /**
+ * Parse a multi value string. This function is used either for parsing
+ * actual multi-value property's values, or for handling parameter values. It
+ * can be used for both multi-value properties and structured value properties.
+ *
+ * @private
+ * @function ICAL.parse._parseMultiValue
+ * @param {String} buffer The buffer containing the full value
+ * @param {String} delim The multi-value delimiter
+ * @param {String} type The value type to be parsed
+ * @param {Array.>} result The array to append results to, varies on value type
+ * @param {String} innerMulti The inner delimiter to split each value with
+ * @param {designSet} designSet The design data for this value
+ * @return {?|Array.>} Either an array of results, or the first result
+ */
+ export function _parseMultiValue(buffer: string, delim: string, type: string, result: any[], innerMulti: string, designSet: import("./types.js").designSet, structuredValue: any): any;
+ /**
+ * Process a complete buffer of iCalendar/vCard data line by line, correctly
+ * unfolding content. Each line will be processed with the given callback
+ *
+ * @private
+ * @function ICAL.parse._eachLine
+ * @param {String} buffer The buffer to process
+ * @param {function(?String, String)} callback The callback for each line
+ */
+ export function _eachLine(buffer: string, callback: (arg0: string, arg1: string) => any): void;
+}
+export default parse;
+/**
+ * Imports the 'parserState' type from the "types.js" module
+ */
+export type parserState = import("./types.js").parserState;
+/**
+ * Imports the 'designSet' type from the "types.js" module
+ */
+export type designSet = import("./types.js").designSet;
+/**
+ * An error that occurred during parsing.
+ *
+ * @param {String} message The error message
+ * @memberof ICAL.parse
+ * @extends {Error}
+ */
+declare class ParserError extends Error {
+}
+//# sourceMappingURL=parse.d.ts.map
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/parse.d.ts.map b/cron/scripts/node_modules/ical.js/dist/types/parse.d.ts.map
new file mode 100644
index 0000000..abd8c0c
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/parse.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../lib/ical/parse.js"],"names":[],"mappings":"AA4BA;;;;;;;;;;;GAWG;AACH,uCAFY,MAAO,KAAQ,CAyB1B;;IAED;;;;;;;;;;OAUG;IACH,uFAOC;IAED;;;;;;;;OAQG;IACH,4CAEC;;IAkBD;;;;;;;OAOG;IACH,gGA+KC;IAED;;;;;;;;;OASG;IACH,oGAKC;IAED;;;;;;;;;;OAUG;IACH,qFAkHC;IAED;;;;;;;OAOG;IACH,oDAIC;IAED;;;;;;;;;;;;;;OAcG;IACH,uLA8BC;IAED;;;;;;;;OAQG;IACH,+FA2CC;;;;;;0BA9hBY,OAAO,YAAY,EAAE,WAAW;;;;wBAEhC,OAAO,YAAY,EAAE,SAAS;AAmF3C;;;;;;GAMG;AACH;CAEC"}
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/period.d.ts b/cron/scripts/node_modules/ical.js/dist/types/period.d.ts
new file mode 100644
index 0000000..0024b9a
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/period.d.ts
@@ -0,0 +1,146 @@
+export default Period;
+/**
+ * Imports the 'occurrenceDetails' type from the "types.js" module
+ */
+export type jCalComponent = import("./types.js").jCalComponent;
+/**
+ * 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").jCalComponent} jCalComponent
+ * Imports the 'occurrenceDetails' type from the "types.js" module
+ */
+/**
+ * This class represents the "period" value type, with various calculation and manipulation methods.
+ *
+ * @memberof ICAL
+ */
+declare class Period {
+ /**
+ * Creates a new {@link ICAL.Period} instance from the passed string.
+ *
+ * @param {String} str The string to parse
+ * @param {Property} prop The property this period will be on
+ * @return {Period} The created period instance
+ */
+ static fromString(str: string, prop: Property): Period;
+ /**
+ * Creates a new {@link ICAL.Period} instance from the given data object.
+ * The passed data object cannot contain both and end date and a duration.
+ *
+ * @param {Object} aData An object with members of the period
+ * @param {Time=} aData.start The start of the period
+ * @param {Time=} aData.end The end of the period
+ * @param {Duration=} aData.duration The duration of the period
+ * @return {Period} The period instance
+ */
+ static fromData(aData: {
+ start?: Time | undefined;
+ end?: Time | undefined;
+ duration?: Duration | undefined;
+ }): Period;
+ /**
+ * Returns a new period instance from the given jCal data array. The first
+ * member is always the start date string, the second member is either a
+ * duration or end date string.
+ *
+ * @param {jCalComponent} aData The jCal data array
+ * @param {Property} aProp The property this jCal data is on
+ * @param {Boolean} aLenient If true, data value can be both date and date-time
+ * @return {Period} The period instance
+ */
+ static fromJSON(aData: import("./types.js").jCalComponent, aProp: Property, aLenient: boolean): Period;
+ /**
+ * Creates a new ICAL.Period instance. The passed data object cannot contain both and end date and
+ * a duration.
+ *
+ * @param {Object} aData An object with members of the period
+ * @param {Time=} aData.start The start of the period
+ * @param {Time=} aData.end The end of the period
+ * @param {Duration=} aData.duration The duration of the period
+ */
+ constructor(aData: {
+ start?: Time | undefined;
+ end?: Time | undefined;
+ duration?: Duration | undefined;
+ });
+ wrappedJSObject: this;
+ /**
+ * The start of the period
+ * @type {Time}
+ */
+ start: Time;
+ /**
+ * The end of the period
+ * @type {Time}
+ */
+ end: Time;
+ /**
+ * The duration of the period
+ * @type {Duration}
+ */
+ duration: Duration;
+ /**
+ * The class identifier.
+ * @constant
+ * @type {String}
+ * @default "icalperiod"
+ */
+ icalclass: string;
+ /**
+ * The type name, to be used in the jCal object.
+ * @constant
+ * @type {String}
+ * @default "period"
+ */
+ icaltype: string;
+ /**
+ * Returns a clone of the duration object.
+ *
+ * @return {Period} The cloned object
+ */
+ clone(): Period;
+ /**
+ * Calculates the duration of the period, either directly or by subtracting
+ * start from end date.
+ *
+ * @return {Duration} The calculated duration
+ */
+ getDuration(): Duration;
+ /**
+ * Calculates the end date of the period, either directly or by adding
+ * duration to start date.
+ *
+ * @return {Time} The calculated end date
+ */
+ getEnd(): Time;
+ /**
+ * Compare this period with a date or other period. To maintain the logic where a.compare(b)
+ * returns 1 when a > b, this function will return 1 when the period is after the date, 0 when the
+ * date is within the period, and -1 when the period is before the date. When comparing two
+ * periods, as soon as they overlap in any way this will return 0.
+ *
+ * @param {Time|Period} dt The date or other period to compare with
+ */
+ compare(dt: Time | Period): 0 | 1 | -1;
+ /**
+ * The string representation of this period.
+ * @return {String}
+ */
+ toString(): string;
+ /**
+ * The jCal representation of this period type.
+ * @return {Object}
+ */
+ toJSON(): any;
+ /**
+ * The iCalendar string representation of this period.
+ * @return {String}
+ */
+ toICALString(): string;
+}
+import Time from "./time.js";
+import Duration from "./duration.js";
+import Property from "./property.js";
+//# sourceMappingURL=period.d.ts.map
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/period.d.ts.map b/cron/scripts/node_modules/ical.js/dist/types/period.d.ts.map
new file mode 100644
index 0000000..c0214bd
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/period.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"period.d.ts","sourceRoot":"","sources":["../../lib/ical/period.js"],"names":[],"mappings":";;;;4BAgBa,OAAO,YAAY,EAAE,aAAa;AAL/C;;;;;;;GAOG;AAEH;;;;GAIG;AACH;IACE;;;;;;OAMG;IACH,qCAHW,QAAQ,GACP,MAAM,CAwBjB;IAED;;;;;;;;;OASG;IACH;QALwB,KAAK,GAAlB,IAAI;QACS,GAAG,GAAhB,IAAI;QACa,QAAQ,GAAzB,QAAQ;QACP,MAAM,CAIjB;IAED;;;;;;;;;OASG;IACH,kEAJW,QAAQ,sBAEP,MAAM,CAsBjB;IAED;;;;;;;;OAQG;IACH;QAJwB,KAAK,GAAlB,IAAI;QACS,GAAG,GAAhB,IAAI;QACa,QAAQ,GAAzB,QAAQ;OA6BlB;IA1BC,sBAA2B;IA6B7B;;;OAGG;IACH,OAFU,IAAI,CAED;IAEb;;;OAGG;IACH,KAFU,IAAI,CAEH;IAEX;;;OAGG;IACH,UAFU,QAAQ,CAEF;IAEhB;;;;;OAKG;IACH,kBAAyB;IAEzB;;;;;OAKG;IACH,iBAAoB;IAEpB;;;;OAIG;IACH,SAFY,MAAM,CAQjB;IAED;;;;;OAKG;IACH,eAFY,QAAQ,CAQnB;IAED;;;;;OAKG;IACH,UAFY,IAAI,CAUf;IAED;;;;;;;OAOG;IACH,YAFW,IAAI,GAAC,MAAM,cAUrB;IAED;;;OAGG;IACH,mBAEC;IAED;;;OAGG;IACH,cAEC;IAED;;;OAGG;IACH,uBAGC;CACF;iBAhQgB,WAAW;qBACP,eAAe;qBAGf,eAAe"}
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/property.d.ts b/cron/scripts/node_modules/ical.js/dist/types/property.d.ts
new file mode 100644
index 0000000..8ec2d4c
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/property.d.ts
@@ -0,0 +1,209 @@
+export default Property;
+/**
+ * Imports the 'designSet' type from the "types.js" module
+ */
+export type designSet = import("./types.js").designSet;
+/**
+ * Imports the 'Geo' type from the "types.js" module
+ */
+export type Geo = import("./types.js").Geo;
+/**
+ * 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
+ * @typedef {import("./types.js").Geo} Geo
+ * Imports the 'Geo' type from the "types.js" module
+ */
+/**
+ * Provides a layer on top of the raw jCal object for manipulating a single property, with its
+ * parameters and value.
+ *
+ * @memberof ICAL
+ */
+declare class Property {
+ /**
+ * Create an {@link ICAL.Property} by parsing the passed iCalendar string.
+ *
+ * @param {String} str The iCalendar string to parse
+ * @param {designSet=} designSet The design data to use for this property
+ * @return {Property} The created iCalendar property
+ */
+ static fromString(str: string, designSet?: designSet | undefined): Property;
+ /**
+ * Creates a new ICAL.Property instance.
+ *
+ * It is important to note that mutations done in the wrapper directly mutate the jCal object used
+ * to initialize.
+ *
+ * Can also be used to create new properties by passing the name of the property (as a String).
+ *
+ * @param {Array|String} jCal Raw jCal representation OR the new name of the property
+ * @param {Component=} parent Parent component
+ */
+ constructor(jCal: any[] | string, parent?: Component | undefined);
+ _parent: Component;
+ jCal: any[];
+ /**
+ * The value type for this property
+ * @type {String}
+ */
+ get type(): string;
+ /**
+ * The name of this property, in lowercase.
+ * @type {String}
+ */
+ get name(): string;
+ set parent(p: Component);
+ /**
+ * The parent component for this property.
+ * @type {Component}
+ */
+ get parent(): Component;
+ /**
+ * The design set for this property, e.g. icalendar vs vcard
+ *
+ * @type {designSet}
+ * @private
+ */
+ private get _designSet();
+ /**
+ * Updates the type metadata from the current jCal type and design set.
+ *
+ * @private
+ */
+ private _updateType;
+ isDecorated: boolean;
+ isMultiValue: boolean;
+ isStructuredValue: boolean;
+ /**
+ * Hydrate a single value. The act of hydrating means turning the raw jCal
+ * value into a potentially wrapped object, for example {@link ICAL.Time}.
+ *
+ * @private
+ * @param {Number} index The index of the value to hydrate
+ * @return {?Object} The decorated value.
+ */
+ private _hydrateValue;
+ _values: any[];
+ /**
+ * Decorate a single value, returning its wrapped object. This is used by
+ * the hydrate function to actually wrap the value.
+ *
+ * @private
+ * @param {?} value The value to decorate
+ * @return {Object} The decorated value
+ */
+ private _decorate;
+ /**
+ * Undecorate a single value, returning its raw jCal data.
+ *
+ * @private
+ * @param {Object} value The value to undecorate
+ * @return {?} The undecorated value
+ */
+ private _undecorate;
+ /**
+ * Sets the value at the given index while also hydrating it. The passed
+ * value can either be a decorated or undecorated value.
+ *
+ * @private
+ * @param {?} value The value to set
+ * @param {Number} index The index to set it at
+ */
+ private _setDecoratedValue;
+ /**
+ * Gets a parameter on the property.
+ *
+ * @param {String} name Parameter name (lowercase)
+ * @return {Array|String} Parameter value
+ */
+ getParameter(name: string): any[] | string;
+ /**
+ * Gets first parameter on the property.
+ *
+ * @param {String} name Parameter name (lowercase)
+ * @return {String} Parameter value
+ */
+ getFirstParameter(name: string): string;
+ /**
+ * Sets a parameter on the property.
+ *
+ * @param {String} name The parameter name
+ * @param {Array|String} value The parameter value
+ */
+ setParameter(name: string, value: any[] | string): void;
+ /**
+ * Removes a parameter
+ *
+ * @param {String} name The parameter name
+ */
+ removeParameter(name: string): void;
+ /**
+ * Get the default type based on this property's name.
+ *
+ * @return {String} The default type for this property
+ */
+ getDefaultType(): string;
+ /**
+ * Sets type of property and clears out any existing values of the current
+ * type.
+ *
+ * @param {String} type New iCAL type (see design.*.values)
+ */
+ resetType(type: string): void;
+ /**
+ * Finds the first property value.
+ *
+ * @return {Binary | Duration | Period |
+ * Recur | Time | UtcOffset | Geo | string | null} First property value
+ */
+ getFirstValue(): Binary | Duration | Period | Recur | Time | UtcOffset | import("./types.js").Geo | string | null;
+ /**
+ * Gets all values on the property.
+ *
+ * NOTE: this creates an array during each call.
+ *
+ * @return {Array} List of values
+ */
+ getValues(): any[];
+ /**
+ * Removes all values from this property
+ */
+ removeAllValues(): void;
+ /**
+ * Sets the values of the property. Will overwrite the existing values.
+ * This can only be used for multi-value properties.
+ *
+ * @param {Array} values An array of values
+ */
+ setValues(values: any[]): void;
+ /**
+ * Sets the current value of the property. If this is a multi-value
+ * property, all other values will be removed.
+ *
+ * @param {String|Object} value New property value.
+ */
+ setValue(value: string | any): void;
+ /**
+ * Returns the Object representation of this component. The returned object
+ * is a live jCal object and should be cloned if modified.
+ * @return {Object}
+ */
+ toJSON(): any;
+ /**
+ * The string representation of this component.
+ * @return {String}
+ */
+ toICALString(): string;
+}
+import Component from "./component.js";
+import Binary from "./binary.js";
+import Duration from "./duration.js";
+import Period from "./period.js";
+import Recur from "./recur.js";
+import Time from "./time.js";
+import UtcOffset from "./utc_offset.js";
+//# sourceMappingURL=property.d.ts.map
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/property.d.ts.map b/cron/scripts/node_modules/ical.js/dist/types/property.d.ts.map
new file mode 100644
index 0000000..079606b
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/property.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"property.d.ts","sourceRoot":"","sources":["../../lib/ical/property.js"],"names":[],"mappings":";;;;wBAyCa,OAAO,YAAY,EAAE,SAAS;;;;kBAE9B,OAAO,YAAY,EAAE,GAAG;AAPrC;;;;;;;;;GASG;AAEH;;;;;GAKG;AACH;IACE;;;;;;OAMG;IACH,2CAHW,SAAS,eACR,QAAQ,CAInB;IAED;;;;;;;;;;OAUG;IACH,kBAHW,cAAY,WACZ,SAAS,cAanB;IAVC,mBAA6B;IAI3B,YAA0C;IAQ9C;;;OAGG;IACH,mBAEC;IAED;;;OAGG;IACH,mBAEC;IAUD,yBAWC;IAnBD;;;OAGG;IACH,wBAEC;IAeD;;;;;OAKG;IACH,yBAEC;IAED;;;;OAIG;IACH,oBAeC;IAVK,qBAAuB;IAMvB,sBAAmE;IACnE,2BAA6E;IAKnF;;;;;;;OAOG;IACH,sBAoBC;IARK,eAAiB;IAUvB;;;;;;;OAOG;IACH,kBAEC;IAED;;;;;;OAMG;IACH,oBAEC;IAED;;;;;;;OAOG;IACH,2BAcC;IAED;;;;;OAKG;IACH,4BAFY,cAAY,CAQvB;IAED;;;;;OAKG;IACH,wCAQC;IAED;;;;;OAKG;IACH,kCAFW,cAAY,QAUtB;IAED;;;;OAIG;IACH,oCAEC;IAED;;;;OAIG;IACH,yBAWC;IAED;;;;;OAKG;IACH,8BAIC;IAED;;;;;OAKG;IACH,iBAHY,MAAM,GAAG,QAAQ,GAAG,MAAM,GACrC,KAAO,GAAG,IAAI,GAAG,SAAS,8BAAS,MAAM,GAAG,IAAI,CAIhD;IAED;;;;;;OAMG;IACH,mBAgBC;IAED;;OAEG;IACH,wBAKC;IAED;;;;;OAKG;IACH,+BA2BC;IAED;;;;;OAKG;IACH,gBAFW,YAAa,QAavB;IAED;;;;OAIG;IACH,cAEC;IAED;;;OAGG;IACH,uBAIC;CACF;sBAraqB,gBAAgB;mBASnB,aAAa;qBANX,eAAe;mBASjB,aAAa;kBAGd,YAAY;iBAGb,WAAW;sBAZN,iBAAiB"}
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/recur.d.ts b/cron/scripts/node_modules/ical.js/dist/types/recur.d.ts
new file mode 100644
index 0000000..5b8f186
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/recur.d.ts
@@ -0,0 +1,348 @@
+export default Recur;
+/**
+ * Imports the 'weekDay' type from the "types.js" module
+ */
+export type weekDay = import("./types.js").weekDay;
+/**
+ * Imports the 'frequencyValues' type from the "types.js" module
+ */
+export type frequencyValues = import("./types.js").frequencyValues;
+/**
+ * This class represents the "recur" value type, used for example by RRULE. It provides methods to
+ * calculate occurrences among others.
+ *
+ * @memberof ICAL
+ */
+declare class Recur {
+ /**
+ * Creates a new {@link ICAL.Recur} instance from the passed string.
+ *
+ * @param {String} string The string to parse
+ * @return {Recur} The created recurrence instance
+ */
+ static fromString(string: string): Recur;
+ /**
+ * Creates a new {@link ICAL.Recur} instance using members from the passed
+ * data object.
+ *
+ * @param {Object} aData An object with members of the recurrence
+ * @param {frequencyValues=} aData.freq The frequency value
+ * @param {Number=} aData.interval The INTERVAL value
+ * @param {weekDay=} aData.wkst The week start value
+ * @param {Time=} aData.until The end of the recurrence set
+ * @param {Number=} aData.count The number of occurrences
+ * @param {Array.=} aData.bysecond The seconds for the BYSECOND part
+ * @param {Array.=} aData.byminute The minutes for the BYMINUTE part
+ * @param {Array.=} aData.byhour The hours for the BYHOUR part
+ * @param {Array.=} aData.byday The BYDAY values
+ * @param {Array.=} aData.bymonthday The days for the BYMONTHDAY part
+ * @param {Array.=} aData.byyearday The days for the BYYEARDAY part
+ * @param {Array.=} aData.byweekno The weeks for the BYWEEKNO part
+ * @param {Array.=} aData.bymonth The month for the BYMONTH part
+ * @param {Array.=} aData.bysetpos The positionals for the BYSETPOS part
+ */
+ static fromData(aData: {
+ freq?: frequencyValues | undefined;
+ interval?: number | undefined;
+ wkst?: weekDay | undefined;
+ until?: Time | undefined;
+ count?: number | undefined;
+ bysecond?: Array | undefined;
+ byminute?: Array | undefined;
+ byhour?: Array | undefined;
+ byday?: Array | undefined;
+ bymonthday?: Array | undefined;
+ byyearday?: Array | undefined;
+ byweekno?: Array | undefined;
+ bymonth?: Array | undefined;
+ bysetpos?: Array | undefined;
+ }): Recur;
+ /**
+ * Converts a recurrence string to a data object, suitable for the fromData
+ * method.
+ *
+ * @private
+ * @param {String} string The string to parse
+ * @param {Boolean} fmtIcal If true, the string is considered to be an
+ * iCalendar string
+ * @return {Recur} The recurrence instance
+ */
+ private static _stringToData;
+ /**
+ * Convert an ical representation of a day (SU, MO, etc..)
+ * into a numeric value of that day.
+ *
+ * @param {String} string The iCalendar day name
+ * @param {weekDay=} aWeekStart
+ * The week start weekday, defaults to SUNDAY
+ * @return {Number} Numeric value of given day
+ */
+ static icalDayToNumericDay(string: string, aWeekStart?: weekDay | undefined): number;
+ /**
+ * Convert a numeric day value into its ical representation (SU, MO, etc..)
+ *
+ * @param {Number} num Numeric value of given day
+ * @param {weekDay=} aWeekStart
+ * The week start weekday, defaults to SUNDAY
+ * @return {String} The ICAL day value, e.g SU,MO,...
+ */
+ static numericDayToIcalDay(num: number, aWeekStart?: weekDay | undefined): string;
+ /**
+ * Create a new instance of the Recur class.
+ *
+ * @param {Object} data An object with members of the recurrence
+ * @param {frequencyValues=} data.freq The frequency value
+ * @param {Number=} data.interval The INTERVAL value
+ * @param {weekDay=} data.wkst The week start value
+ * @param {Time=} data.until The end of the recurrence set
+ * @param {Number=} data.count The number of occurrences
+ * @param {Array.=} data.bysecond The seconds for the BYSECOND part
+ * @param {Array.=} data.byminute The minutes for the BYMINUTE part
+ * @param {Array.=} data.byhour The hours for the BYHOUR part
+ * @param {Array.=} data.byday The BYDAY values
+ * @param {Array.=} data.bymonthday The days for the BYMONTHDAY part
+ * @param {Array.=} data.byyearday The days for the BYYEARDAY part
+ * @param {Array.=} data.byweekno The weeks for the BYWEEKNO part
+ * @param {Array.=} data.bymonth The month for the BYMONTH part
+ * @param {Array.=} data.bysetpos The positionals for the BYSETPOS part
+ */
+ constructor(data: {
+ freq?: frequencyValues | undefined;
+ interval?: number | undefined;
+ wkst?: weekDay | undefined;
+ until?: Time | undefined;
+ count?: number | undefined;
+ bysecond?: Array | undefined;
+ byminute?: Array | undefined;
+ byhour?: Array | undefined;
+ byday?: Array | undefined;
+ bymonthday?: Array | undefined;
+ byyearday?: Array | undefined;
+ byweekno?: Array | undefined;
+ bymonth?: Array | undefined;
+ bysetpos?: Array | undefined;
+ });
+ wrappedJSObject: this;
+ /**
+ * An object holding the BY-parts of the recurrence rule
+ * @memberof ICAL.Recur
+ * @typedef {Object} byParts
+ * @property {Array.=} BYSECOND The seconds for the BYSECOND part
+ * @property {Array.=} BYMINUTE The minutes for the BYMINUTE part
+ * @property {Array.=} BYHOUR The hours for the BYHOUR part
+ * @property {Array.=} BYDAY The BYDAY values
+ * @property {Array.=} BYMONTHDAY The days for the BYMONTHDAY part
+ * @property {Array.=} BYYEARDAY The days for the BYYEARDAY part
+ * @property {Array.=} BYWEEKNO The weeks for the BYWEEKNO part
+ * @property {Array.=} BYMONTH The month for the BYMONTH part
+ * @property {Array.=} BYSETPOS The positionals for the BYSETPOS part
+ */
+ /**
+ * An object holding the BY-parts of the recurrence rule
+ * @type {byParts}
+ */
+ parts: {
+ /**
+ * The seconds for the BYSECOND part
+ */
+ BYSECOND?: Array | undefined;
+ /**
+ * The minutes for the BYMINUTE part
+ */
+ BYMINUTE?: Array | undefined;
+ /**
+ * The hours for the BYHOUR part
+ */
+ BYHOUR?: Array | undefined;
+ /**
+ * The BYDAY values
+ */
+ BYDAY?: Array | undefined;
+ /**
+ * The days for the BYMONTHDAY part
+ */
+ BYMONTHDAY?: Array | undefined;
+ /**
+ * The days for the BYYEARDAY part
+ */
+ BYYEARDAY?: Array | undefined;
+ /**
+ * The weeks for the BYWEEKNO part
+ */
+ BYWEEKNO?: Array | undefined;
+ /**
+ * The month for the BYMONTH part
+ */
+ BYMONTH?: Array | undefined;
+ /**
+ * The positionals for the BYSETPOS part
+ */
+ BYSETPOS?: Array | undefined;
+ };
+ /**
+ * The interval value for the recurrence rule.
+ * @type {Number}
+ */
+ interval: number;
+ /**
+ * The week start day
+ *
+ * @type {weekDay}
+ * @default ICAL.Time.MONDAY
+ */
+ wkst: weekDay;
+ /**
+ * The end of the recurrence
+ * @type {?Time}
+ */
+ until: Time | null;
+ /**
+ * The maximum number of occurrences
+ * @type {?Number}
+ */
+ count: number | null;
+ /**
+ * The frequency value.
+ * @type {frequencyValues}
+ */
+ freq: frequencyValues;
+ /**
+ * The class identifier.
+ * @constant
+ * @type {String}
+ * @default "icalrecur"
+ */
+ icalclass: string;
+ /**
+ * The type name, to be used in the jCal object.
+ * @constant
+ * @type {String}
+ * @default "recur"
+ */
+ icaltype: string;
+ /**
+ * Create a new iterator for this recurrence rule. The passed start date
+ * must be the start date of the event, not the start of the range to
+ * search in.
+ *
+ * @example
+ * let recur = comp.getFirstPropertyValue('rrule');
+ * let dtstart = comp.getFirstPropertyValue('dtstart');
+ * let iter = recur.iterator(dtstart);
+ * for (let next = iter.next(); next; next = iter.next()) {
+ * if (next.compare(rangeStart) < 0) {
+ * continue;
+ * }
+ * console.log(next.toString());
+ * }
+ *
+ * @param {Time} aStart The item's start date
+ * @return {RecurIterator} The recurrence iterator
+ */
+ iterator(aStart: Time): RecurIterator;
+ /**
+ * Returns a clone of the recurrence object.
+ *
+ * @return {Recur} The cloned object
+ */
+ clone(): Recur;
+ /**
+ * Checks if the current rule is finite, i.e. has a count or until part.
+ *
+ * @return {Boolean} True, if the rule is finite
+ */
+ isFinite(): boolean;
+ /**
+ * Checks if the current rule has a count part, and not limited by an until
+ * part.
+ *
+ * @return {Boolean} True, if the rule is by count
+ */
+ isByCount(): boolean;
+ /**
+ * Adds a component (part) to the recurrence rule. This is not a component
+ * in the sense of {@link ICAL.Component}, but a part of the recurrence
+ * rule, i.e. BYMONTH.
+ *
+ * @param {String} aType The name of the component part
+ * @param {Array|String} aValue The component value
+ */
+ addComponent(aType: string, aValue: any[] | string): void;
+ /**
+ * Sets the component value for the given by-part.
+ *
+ * @param {String} aType The component part name
+ * @param {Array} aValues The component values
+ */
+ setComponent(aType: string, aValues: any[]): void;
+ /**
+ * Gets (a copy) of the requested component value.
+ *
+ * @param {String} aType The component part name
+ * @return {Array} The component part value
+ */
+ getComponent(aType: string): any[];
+ /**
+ * Retrieves the next occurrence after the given recurrence id. See the
+ * guide on {@tutorial terminology} for more details.
+ *
+ * NOTE: Currently, this method iterates all occurrences from the start
+ * date. It should not be called in a loop for performance reasons. If you
+ * would like to get more than one occurrence, you can iterate the
+ * occurrences manually, see the example on the
+ * {@link ICAL.Recur#iterator iterator} method.
+ *
+ * @param {Time} aStartTime The start of the event series
+ * @param {Time} aRecurrenceId The date of the last occurrence
+ * @return {Time} The next occurrence after
+ */
+ getNextOccurrence(aStartTime: Time, aRecurrenceId: Time): Time;
+ /**
+ * Sets up the current instance using members from the passed data object.
+ *
+ * @param {Object} data An object with members of the recurrence
+ * @param {frequencyValues=} data.freq The frequency value
+ * @param {Number=} data.interval The INTERVAL value
+ * @param {weekDay=} data.wkst The week start value
+ * @param {Time=} data.until The end of the recurrence set
+ * @param {Number=} data.count The number of occurrences
+ * @param {Array.=} data.bysecond The seconds for the BYSECOND part
+ * @param {Array.=} data.byminute The minutes for the BYMINUTE part
+ * @param {Array.=} data.byhour The hours for the BYHOUR part
+ * @param {Array.=} data.byday The BYDAY values
+ * @param {Array.=} data.bymonthday The days for the BYMONTHDAY part
+ * @param {Array.=} data.byyearday The days for the BYYEARDAY part
+ * @param {Array.=} data.byweekno The weeks for the BYWEEKNO part
+ * @param {Array.=} data.bymonth The month for the BYMONTH part
+ * @param {Array.=} data.bysetpos The positionals for the BYSETPOS part
+ */
+ fromData(data: {
+ freq?: frequencyValues | undefined;
+ interval?: number | undefined;
+ wkst?: weekDay | undefined;
+ until?: Time | undefined;
+ count?: number | undefined;
+ bysecond?: Array | undefined;
+ byminute?: Array | undefined;
+ byhour?: Array | undefined;
+ byday?: Array | undefined;
+ bymonthday?: Array | undefined;
+ byyearday?: Array | undefined;
+ byweekno?: Array | undefined;
+ bymonth?: Array | undefined;
+ bysetpos?: Array | undefined;
+ }): void;
+ /**
+ * The jCal representation of this recurrence type.
+ * @return {Object}
+ */
+ toJSON(): any;
+ /**
+ * The string representation of this recurrence rule.
+ * @return {String}
+ */
+ toString(): string;
+}
+import Time from "./time.js";
+import RecurIterator from "./recur_iterator.js";
+//# sourceMappingURL=recur.d.ts.map
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/recur.d.ts.map b/cron/scripts/node_modules/ical.js/dist/types/recur.d.ts.map
new file mode 100644
index 0000000..b761581
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/recur.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"recur.d.ts","sourceRoot":"","sources":["../../lib/ical/recur.js"],"names":[],"mappings":";;;;sBAgBa,OAAO,YAAY,EAAE,OAAO;;;;8BAE5B,OAAO,YAAY,EAAE,eAAe;AAqBjD;;;;;GAKG;AACH;IACE;;;;;OAKG;IACH,mCAFY,KAAK,CAKhB;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH;QAfmC,IAAI,GAA5B,eAAe;QACA,QAAQ;QACP,IAAI,GAApB,OAAO;QACM,KAAK,GAAlB,IAAI;QACW,KAAK;QACG,QAAQ,GAA/B,aAAc;QACS,QAAQ,GAA/B,aAAc;QACS,MAAM,GAA7B,aAAc;QACS,KAAK,GAA5B,aAAc;QACS,UAAU,GAAjC,aAAc;QACS,SAAS,GAAhC,aAAc;QACS,QAAQ,GAA/B,aAAc;QACS,OAAO,GAA9B,aAAc;QACS,QAAQ,GAA/B,aAAc;cAIxB;IAED;;;;;;;;;OASG;IACH,6BAkCC;IAED;;;;;;;;OAQG;IACH,wDAJW,OAAO,sBASjB;IAED;;;;;;;OAOG;IACH,qDAJW,OAAO,sBAcjB;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACH;QAfkC,IAAI,GAA3B,eAAe;QACD,QAAQ;QACP,IAAI,GAAnB,OAAO;QACK,KAAK,GAAjB,IAAI;QACU,KAAK;QACG,QAAQ,GAA9B,aAAc;QACQ,QAAQ,GAA9B,aAAc;QACQ,MAAM,GAA5B,aAAc;QACQ,KAAK,GAA3B,aAAc;QACQ,UAAU,GAAhC,aAAc;QACQ,SAAS,GAA/B,aAAc;QACQ,QAAQ,GAA9B,aAAc;QACQ,OAAO,GAA7B,aAAc;QACQ,QAAQ,GAA9B,aAAc;OASxB;IANC,sBAA2B;IAQ7B;;;;;;;;;;;;;OAaG;IAEH;;;OAGG;IACH;;;;mBAfc,aAAc;;;;mBACd,aAAc;;;;iBACd,aAAc;;;;gBACd,aAAc;;;;qBACd,aAAc;;;;oBACd,aAAc;;;;mBACd,aAAc;;;;kBACd,aAAc;;;;mBACd,aAAc;MAOf;IAEb;;;OAGG;IACH,iBAAa;IAEb;;;;;OAKG;IACH,MAHU,OAAO,CAGE;IAEnB;;;OAGG;IACH,OAFW,IAAI,QAEF;IAEb;;;OAGG;IACH,qBAAa;IAEb;;;OAGG;IACH,MAFU,eAAe,CAEb;IAEZ;;;;;OAKG;IACH,kBAAwB;IAExB;;;;;OAKG;IACH,iBAAmB;IAEnB;;;;;;;;;;;;;;;;;;OAkBG;IACH,iBAHW,IAAI,GACH,aAAa,CAOxB;IAED;;;;OAIG;IACH,SAFY,KAAK,CAIhB;IAED;;;;OAIG;IACH,oBAEC;IAED;;;;;OAKG;IACH,qBAEC;IAED;;;;;;;OAOG;IACH,oCAFW,cAAY,QAStB;IAED;;;;;OAKG;IACH,kDAEC;IAED;;;;;OAKG;IACH,mCAGC;IAED;;;;;;;;;;;;;OAaG;IACH,8BAJW,IAAI,iBACJ,IAAI,GACH,IAAI,CAef;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACH;QAfkC,IAAI,GAA3B,eAAe;QACD,QAAQ;QACP,IAAI,GAAnB,OAAO;QACK,KAAK,GAAjB,IAAI;QACU,KAAK;QACG,QAAQ,GAA9B,aAAc;QACQ,QAAQ,GAA9B,aAAc;QACQ,MAAM,GAA5B,aAAc;QACQ,KAAK,GAA3B,aAAc;QACQ,UAAU,GAAhC,aAAc;QACQ,SAAS,GAA/B,aAAc;QACQ,QAAQ,GAA9B,aAAc;QACQ,OAAO,GAA7B,aAAc;QACQ,QAAQ,GAA9B,aAAc;aA4BxB;IAED;;;OAGG;IACH,cA2BC;IAED;;;OAGG;IACH,mBAmBC;CACF;iBA/dgB,WAAW;0BADF,qBAAqB"}
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/recur_expansion.d.ts b/cron/scripts/node_modules/ical.js/dist/types/recur_expansion.d.ts
new file mode 100644
index 0000000..bd9965f
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/recur_expansion.d.ts
@@ -0,0 +1,199 @@
+export default RecurExpansion;
+/**
+ * Primary class for expanding recurring rules. Can take multiple rrules, rdates, exdate(s) and
+ * iterate (in order) over each next occurrence.
+ *
+ * Once initialized this class can also be serialized saved and continue iteration from the last
+ * point.
+ *
+ * NOTE: it is intended that this class is to be used with {@link ICAL.Event} which handles recurrence
+ * exceptions.
+ *
+ * @example
+ * // assuming event is a parsed ical component
+ * var event;
+ *
+ * var expand = new ICAL.RecurExpansion({
+ * component: event,
+ * dtstart: event.getFirstPropertyValue('dtstart')
+ * });
+ *
+ * // remember there are infinite rules so it is a good idea to limit the scope of the iterations
+ * // then resume later on.
+ *
+ * // next is always an ICAL.Time or null
+ * var next;
+ *
+ * while (someCondition && (next = expand.next())) {
+ * // do something with next
+ * }
+ *
+ * // save instance for later
+ * var json = JSON.stringify(expand);
+ *
+ * //...
+ *
+ * // NOTE: if the component's properties have changed you will need to rebuild the class and start
+ * // over. This only works when the component's recurrence info is the same.
+ * var expand = new ICAL.RecurExpansion(JSON.parse(json));
+ *
+ * @memberof ICAL
+ */
+declare class RecurExpansion {
+ /**
+ * Creates a new ICAL.RecurExpansion instance.
+ *
+ * The options object can be filled with the specified initial values. It can also contain
+ * additional members, as a result of serializing a previous expansion state, as shown in the
+ * example.
+ *
+ * @param {Object} options
+ * Recurrence expansion options
+ * @param {Time} options.dtstart
+ * Start time of the event
+ * @param {Component=} options.component
+ * Component for expansion, required if not resuming.
+ */
+ constructor(options: {
+ dtstart: Time;
+ component?: Component | undefined;
+ });
+ /**
+ * Array of rdate instances.
+ *
+ * @type {Time[]}
+ * @private
+ */
+ private ruleDates;
+ /**
+ * Array of exdate instances.
+ *
+ * @type {Time[]}
+ * @private
+ */
+ private exDates;
+ /**
+ * True when iteration is fully completed.
+ * @type {Boolean}
+ */
+ complete: boolean;
+ /**
+ * Array of rrule iterators.
+ *
+ * @type {RecurIterator[]}
+ * @private
+ */
+ private ruleIterators;
+ /**
+ * Current position in ruleDates array.
+ * @type {Number}
+ * @private
+ */
+ private ruleDateInc;
+ /**
+ * Current position in exDates array
+ * @type {Number}
+ * @private
+ */
+ private exDateInc;
+ /**
+ * Current negative date.
+ *
+ * @type {Time}
+ * @private
+ */
+ private exDate;
+ /**
+ * Current additional date.
+ *
+ * @type {Time}
+ * @private
+ */
+ private ruleDate;
+ /**
+ * Start date of recurring rules.
+ *
+ * @type {Time}
+ */
+ dtstart: Time;
+ /**
+ * Last expanded time
+ *
+ * @type {Time}
+ */
+ last: Time;
+ /**
+ * Initialize the recurrence expansion from the data object. The options
+ * object may also contain additional members, see the
+ * {@link ICAL.RecurExpansion constructor} for more details.
+ *
+ * @param {Object} options
+ * Recurrence expansion options
+ * @param {Time} options.dtstart
+ * Start time of the event
+ * @param {Component=} options.component
+ * Component for expansion, required if not resuming.
+ */
+ fromData(options: {
+ dtstart: Time;
+ component?: Component | undefined;
+ }): void;
+ /**
+ * Compare two ICAL.Time objects. When the second parameter is a DATE and the first parameter is
+ * DATE-TIME, strip the time and compare only the days.
+ *
+ * @private
+ * @param {Time} a The one object to compare
+ * @param {Time} b The other object to compare
+ */
+ private _compare_special;
+ /**
+ * Retrieve the next occurrence in the series.
+ * @return {Time}
+ */
+ next(): Time;
+ /**
+ * Converts object into a serialize-able format. This format can be passed
+ * back into the expansion to resume iteration.
+ * @return {Object}
+ */
+ toJSON(): any;
+ /**
+ * Extract all dates from the properties in the given component. The
+ * properties will be filtered by the property name.
+ *
+ * @private
+ * @param {Component} component The component to search in
+ * @param {String} propertyName The property name to search for
+ * @return {Time[]} The extracted dates.
+ */
+ private _extractDates;
+ /**
+ * Initialize the recurrence expansion.
+ *
+ * @private
+ * @param {Component} component The component to initialize from.
+ */
+ private _init;
+ /**
+ * Advance to the next exdate
+ * @private
+ */
+ private _nextExDay;
+ /**
+ * Advance to the next rule date
+ * @private
+ */
+ private _nextRuleDay;
+ /**
+ * Find and return the recurrence rule with the most recent event and
+ * return it.
+ *
+ * @private
+ * @return {?RecurIterator} Found iterator.
+ */
+ private _nextRecurrenceIter;
+}
+import Time from "./time.js";
+import Component from "./component.js";
+//# sourceMappingURL=recur_expansion.d.ts.map
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/recur_expansion.d.ts.map b/cron/scripts/node_modules/ical.js/dist/types/recur_expansion.d.ts.map
new file mode 100644
index 0000000..9b0704d
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/recur_expansion.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"recur_expansion.d.ts","sourceRoot":"","sources":["../../lib/ical/recur_expansion.js"],"names":[],"mappings":";AAYA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH;IACE;;;;;;;;;;;;;OAaG;IACH;QALyB,OAAO,EAArB,IAAI;QAEgB,SAAS,GAA7B,SAAS;OAOnB;IAgBD;;;;;OAKG;IACH,kBAAiB;IAEjB;;;;;OAKG;IACH,gBAAe;IA5Bf;;;OAGG;IACH,kBAAiB;IAEjB;;;;;OAKG;IACH,sBAAqB;IAkBrB;;;;OAIG;IACH,oBAAgB;IAEhB;;;;OAIG;IACH,kBAAc;IAEd;;;;;OAKG;IACH,eAAc;IAEd;;;;;OAKG;IACH,iBAAgB;IAEhB;;;;OAIG;IACH,SAFU,IAAI,CAEC;IAEf;;;;OAIG;IACH,MAFU,IAAI,CAEF;IAEZ;;;;;;;;;;;OAWG;IACH;QALyB,OAAO,EAArB,IAAI;QAEgB,SAAS,GAA7B,SAAS;aA0CnB;IAED;;;;;;;OAOG;IACH,yBAIC;IAED;;;OAGG;IACH,QAFY,IAAI,CAqEf;IAED;;;;OAIG;IACH,cAuBC;IAED;;;;;;;;OAQG;IACH,sBAkBC;IAED;;;;;OAKG;IACH,cAwEC;IAED;;;OAGG;IACH,mBAEC;IAED;;;OAGG;IACH,qBAEC;IAED;;;;;;OAMG;IACH,4BAwCC;CACF;iBAregB,WAAW;sBAIN,gBAAgB"}
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/recur_iterator.d.ts b/cron/scripts/node_modules/ical.js/dist/types/recur_iterator.d.ts
new file mode 100644
index 0000000..4fb5453
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/recur_iterator.d.ts
@@ -0,0 +1,220 @@
+export default RecurIterator;
+/**
+ * Imports the 'weekDay' type from the "types.js" module
+ */
+export type weekDay = import("./types.js").weekDay;
+/**
+ * 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").weekDay} weekDay
+ * Imports the 'weekDay' type from the "types.js" module
+ */
+/**
+ * An iterator for a single recurrence rule. This class usually doesn't have to be instanciated
+ * directly, the convenience method {@link ICAL.Recur#iterator} can be used.
+ *
+ * @memberof ICAL
+ */
+declare class RecurIterator {
+ static _indexMap: {
+ BYSECOND: number;
+ BYMINUTE: number;
+ BYHOUR: number;
+ BYDAY: number;
+ BYMONTHDAY: number;
+ BYYEARDAY: number;
+ BYWEEKNO: number;
+ BYMONTH: number;
+ BYSETPOS: number;
+ };
+ static _expandMap: {
+ SECONDLY: number[];
+ MINUTELY: number[];
+ HOURLY: number[];
+ DAILY: number[];
+ WEEKLY: number[];
+ MONTHLY: number[];
+ YEARLY: number[];
+ };
+ static UNKNOWN: number;
+ static CONTRACT: number;
+ static EXPAND: number;
+ static ILLEGAL: number;
+ /**
+ * Creates a new ICAL.RecurIterator instance. The options object may contain additional members
+ * when resuming iteration from a previous run.
+ *
+ * @param {Object} options The iterator options
+ * @param {Recur} options.rule The rule to iterate.
+ * @param {Time} options.dtstart The start date of the event.
+ * @param {Boolean=} options.initialized When true, assume that options are
+ * from a previously constructed iterator. Initialization will not be
+ * repeated.
+ */
+ constructor(options: {
+ rule: Recur;
+ dtstart: Time;
+ initialized?: boolean | undefined;
+ });
+ /**
+ * True when iteration is finished.
+ * @type {Boolean}
+ */
+ completed: boolean;
+ /**
+ * The rule that is being iterated
+ * @type {Recur}
+ */
+ rule: Recur;
+ /**
+ * The start date of the event being iterated.
+ * @type {Time}
+ */
+ dtstart: Time;
+ /**
+ * The last occurrence that was returned from the
+ * {@link RecurIterator#next} method.
+ * @type {Time}
+ */
+ last: Time;
+ /**
+ * The sequence number from the occurrence
+ * @type {Number}
+ */
+ occurrence_number: number;
+ /**
+ * The indices used for the {@link ICAL.RecurIterator#by_data} object.
+ * @type {Object}
+ * @private
+ */
+ private by_indices;
+ /**
+ * If true, the iterator has already been initialized
+ * @type {Boolean}
+ * @private
+ */
+ private initialized;
+ /**
+ * The initializd by-data.
+ * @type {Object}
+ * @private
+ */
+ private by_data;
+ /**
+ * The expanded yeardays
+ * @type {Array}
+ * @private
+ */
+ private days;
+ /**
+ * The index in the {@link ICAL.RecurIterator#days} array.
+ * @type {Number}
+ * @private
+ */
+ private days_index;
+ /**
+ * Initialize the recurrence iterator from the passed data object. This
+ * method is usually not called directly, you can initialize the iterator
+ * through the constructor.
+ *
+ * @param {Object} options The iterator options
+ * @param {Recur} options.rule The rule to iterate.
+ * @param {Time} options.dtstart The start date of the event.
+ * @param {Boolean=} options.initialized When true, assume that options are
+ * from a previously constructed iterator. Initialization will not be
+ * repeated.
+ */
+ fromData(options: {
+ rule: Recur;
+ dtstart: Time;
+ initialized?: boolean | undefined;
+ }): void;
+ /**
+ * Initialize the iterator
+ * @private
+ */
+ private init;
+ /**
+ * Retrieve the next occurrence from the iterator.
+ * @return {Time}
+ */
+ next(again?: boolean): Time;
+ next_second(): number;
+ increment_second(inc: any): void;
+ next_minute(): number;
+ increment_minute(inc: any): void;
+ next_hour(): number;
+ increment_hour(inc: any): void;
+ next_day(): number;
+ next_week(): number;
+ /**
+ * Normalize each by day rule for a given year/month.
+ * Takes into account ordering and negative rules
+ *
+ * @private
+ * @param {Number} year Current year.
+ * @param {Number} month Current month.
+ * @param {Array} rules Array of rules.
+ *
+ * @return {Array} sorted and normalized rules.
+ * Negative rules will be expanded to their
+ * correct positive values for easier processing.
+ */
+ private normalizeByMonthDayRules;
+ /**
+ * NOTES:
+ * We are given a list of dates in the month (BYMONTHDAY) (23, etc..)
+ * Also we are given a list of days (BYDAY) (MO, 2SU, etc..) when
+ * both conditions match a given date (this.last.day) iteration stops.
+ *
+ * @private
+ * @param {Boolean=} isInit When given true will not increment the
+ * current day (this.last).
+ */
+ private _byDayAndMonthDay;
+ next_month(): number;
+ next_weekday_by_week(): number;
+ next_year(): 0 | 1;
+ _nextByYearDay(): 0 | 1;
+ /**
+ * @param dow (eg: '1TU', '-1MO')
+ * @param {weekDay=} aWeekStart The week start weekday
+ * @return [pos, numericDow] (eg: [1, 3]) numericDow is relative to aWeekStart
+ */
+ ruleDayOfWeek(dow: any, aWeekStart?: weekDay | undefined): any[];
+ next_generic(aRuleType: any, aInterval: any, aDateAttr: any, aFollowingAttr: any, aPreviousIncr: any): number;
+ increment_monthday(inc: any): void;
+ increment_month(): void;
+ increment_year(inc: any): void;
+ increment_generic(inc: any, aDateAttr: any, aFactor: any, aNextIncrement: any): void;
+ has_by_data(aRuleType: any): boolean;
+ expand_year_days(aYear: any): number;
+ expand_by_day(aYear: any): number[];
+ is_day_in_byday(tt: any): 0 | 1;
+ /**
+ * Checks if given value is in BYSETPOS.
+ *
+ * @private
+ * @param {Numeric} aPos position to check for.
+ * @return {Boolean} false unless BYSETPOS rules exist
+ * and the given value is present in rules.
+ */
+ private check_set_position;
+ sort_byday_rules(aRules: any): void;
+ check_contract_restriction(aRuleType: any, v: any): boolean;
+ check_contracting_rules(): boolean;
+ setup_defaults(aRuleType: any, req: any, deftime: any): any;
+ /**
+ * Convert iterator into a serialize-able object. Will preserve current
+ * iteration sequence to ensure the seamless continuation of the recurrence
+ * rule.
+ * @return {Object}
+ */
+ toJSON(): any;
+}
+import Recur from "./recur.js";
+import Time from "./time.js";
+//# sourceMappingURL=recur_iterator.d.ts.map
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/recur_iterator.d.ts.map b/cron/scripts/node_modules/ical.js/dist/types/recur_iterator.d.ts.map
new file mode 100644
index 0000000..b5aa5f9
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/recur_iterator.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"recur_iterator.d.ts","sourceRoot":"","sources":["../../lib/ical/recur_iterator.js"],"names":[],"mappings":";;;;sBAea,OAAO,YAAY,EAAE,OAAO;AANzC;;;;;;;;GAQG;AAEH;;;;;GAKG;AACH;IACE;;;;;;;;;;MAUE;IAEF;;;;;;;;MAQE;IAEF,uBAAmB;IACnB,wBAAoB;IACpB,sBAAkB;IAClB,uBAAmB;IAEnB;;;;;;;;;;OAUG;IACH;QAN0B,IAAI,EAAnB,KAAK;QACS,OAAO,EAArB,IAAI;QACc,WAAW;OAMvC;IAED;;;OAGG;IACH,mBAAkB;IAElB;;;OAGG;IACH,MAFU,KAAK,CAEH;IAEZ;;;OAGG;IACH,SAFU,IAAI,CAEC;IAEf;;;;OAIG;IACH,MAFU,IAAI,CAEF;IAEZ;;;OAGG;IACH,0BAAsB;IAEtB;;;;OAIG;IACH,mBAAkB;IAElB;;;;OAIG;IACH,oBAAoB;IAEpB;;;;OAIG;IACH,gBAAe;IAEf;;;;OAIG;IACH,aAAY;IAEZ;;;;OAIG;IACH,mBAAe;IAEf;;;;;;;;;;;OAWG;IACH;QAN0B,IAAI,EAAnB,KAAK;QACS,OAAO,EAArB,IAAI;QACc,WAAW;aA8DvC;IAED;;;OAGG;IACH,aAyKC;IAED;;;OAGG;IACH,uBAFY,IAAI,CAqFf;IAED,sBAEC;IAED,iCAEC;IAED,sBAGC;IAED,iCAEC;IAED,oBAGC;IAED,+BAEC;IAED,mBAcC;IAED,oBAgCC;IAED;;;;;;;;;;;;OAYG;IACH,iCA2CC;IAED;;;;;;;;;OASG;IACH,0BAmIC;IAED,qBA2FC;IAED,+BAwDC;IAED,mBAsBC;IAED,wBAqBC;IAED;;;;OAIG;IACH,qCAHW,OAAO,qBAYjB;IAED,8GA2BC;IAED,mCAUC;IAED,wBAmCC;IAED,+BAIC;IAED,qFAOC;IAED,qCAEC;IAED,qCAmOC;IAED,oCAsDC;IAED,gCAgBC;IAED;;;;;;;OAOG;IACH,2BAOC;IAED,oCAaC;IAED,4DAqBC;IAED,mCAaC;IAED,4DAaC;IAED;;;;;OAKG;IACH,cAaC;CACF;kBAp7CiB,YAAY;iBACb,WAAW"}
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/stringify.d.ts b/cron/scripts/node_modules/ical.js/dist/types/stringify.d.ts
new file mode 100644
index 0000000..8deae82
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/stringify.d.ts
@@ -0,0 +1,95 @@
+/**
+ * Convert a full jCal/jCard array into a iCalendar/vCard string.
+ *
+ * @function ICAL.stringify
+ * @variation function
+ * @param {Array} jCal The jCal/jCard document
+ * @return {String} The stringified iCalendar/vCard document
+ */
+declare function stringify(jCal: any[]): string;
+declare namespace stringify {
+ /**
+ * Converts an jCal component array into a ICAL string.
+ * Recursive will resolve sub-components.
+ *
+ * Exact component/property order is not saved all
+ * properties will come before subcomponents.
+ *
+ * @function ICAL.stringify.component
+ * @param {Array} component
+ * jCal/jCard fragment of a component
+ * @param {designSet} designSet
+ * The design data to use for this component
+ * @return {String} The iCalendar/vCard string
+ */
+ function component(component: any[], designSet: import("./types.js").designSet): string;
+ /**
+ * Converts a single jCal/jCard property to a iCalendar/vCard string.
+ *
+ * @function ICAL.stringify.property
+ * @param {Array} property
+ * jCal/jCard property array
+ * @param {designSet} designSet
+ * The design data to use for this property
+ * @param {Boolean} noFold
+ * If true, the line is not folded
+ * @return {String} The iCalendar/vCard string
+ */
+ function property(property: any[], designSet: import("./types.js").designSet, noFold: boolean): string;
+ /**
+ * Handles escaping of property values that may contain:
+ *
+ * COLON (:), SEMICOLON (;), or COMMA (,)
+ *
+ * If any of the above are present the result is wrapped
+ * in double quotes.
+ *
+ * @function ICAL.stringify.paramPropertyValue
+ * @param {String} value Raw property value
+ * @param {boolean} force If value should be escaped even when unnecessary
+ * @return {String} Given or escaped value when needed
+ */
+ function paramPropertyValue(value: string, force: boolean): string;
+ /**
+ * Converts an array of ical values into a single
+ * string based on a type and a delimiter value (like ",").
+ *
+ * @function ICAL.stringify.multiValue
+ * @param {Array} values List of values to convert
+ * @param {String} delim Used to join the values (",", ";", ":")
+ * @param {String} type Lowecase ical value type
+ * (like boolean, date-time, etc..)
+ * @param {?String} innerMulti If set, each value will again be processed
+ * Used for structured values
+ * @param {designSet} designSet
+ * The design data to use for this property
+ *
+ * @return {String} iCalendar/vCard string for value
+ */
+ function multiValue(values: any[], delim: string, type: string, innerMulti: string, designSet: import("./types.js").designSet, structuredValue: any): string;
+ /**
+ * Processes a single ical value runs the associated "toICAL" method from the
+ * design value type if available to convert the value.
+ *
+ * @function ICAL.stringify.value
+ * @param {String|Number} value A formatted value
+ * @param {String} type Lowercase iCalendar/vCard value type
+ * (like boolean, date-time, etc..)
+ * @return {String} iCalendar/vCard value for single value
+ */
+ function value(value: string | number, type: string, designSet: any, structuredValue: any): string;
+ /**
+ * Internal helper for rfc6868. Exposing this on ICAL.stringify so that
+ * hackers can disable the rfc6868 parsing if the really need to.
+ *
+ * @param {String} val The value to unescape
+ * @return {String} The escaped value
+ */
+ function _rfc6868Unescape(val: string): string;
+}
+export default stringify;
+/**
+ * Imports the 'designSet' type from the "types.js" module
+ */
+export type designSet = import("./types.js").designSet;
+//# sourceMappingURL=stringify.d.ts.map
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/stringify.d.ts.map b/cron/scripts/node_modules/ical.js/dist/types/stringify.d.ts.map
new file mode 100644
index 0000000..9105c72
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/stringify.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"stringify.d.ts","sourceRoot":"","sources":["../../lib/ical/stringify.js"],"names":[],"mappings":"AAsBA;;;;;;;GAOG;AACH,gDAeC;;IAED;;;;;;;;;;;;;OAaG;IACH,wFAgCC;IAED;;;;;;;;;;;OAWG;IACH,uGAuGC;IAED;;;;;;;;;;;;OAYG;IACH,mEAUC;IAED;;;;;;;;;;;;;;;OAeG;IACH,6JAkBC;IAED;;;;;;;;;OASG;IACH,mGAKC;IAED;;;;;;OAMG;IACH,+CAIC;;;;;;wBA/RY,OAAO,YAAY,EAAE,SAAS"}
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/time.d.ts b/cron/scripts/node_modules/ical.js/dist/types/time.d.ts
new file mode 100644
index 0000000..dc083a7
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/time.d.ts
@@ -0,0 +1,542 @@
+export default Time;
+/**
+ * Imports the 'weekDay' type from the "types.js" module
+ */
+export type weekDay = import("./types.js").weekDay;
+/**
+ * Imports the 'timeInit' type from the "types.js" module
+ */
+export type timeInit = import("./types.js").timeInit;
+/**
+ * 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").weekDay} weekDay
+ * Imports the 'weekDay' type from the "types.js" module
+ *
+ * @ignore
+ * @typedef {import("./types.js").timeInit} timeInit
+ * Imports the 'timeInit' type from the "types.js" module
+ */
+/**
+ * @classdesc
+ * iCalendar Time representation (similar to JS Date object). Fully
+ * independent of system (OS) timezone / time. Unlike JS Date, the month
+ * January is 1, not zero.
+ *
+ * @example
+ * var time = new ICAL.Time({
+ * year: 2012,
+ * month: 10,
+ * day: 11
+ * minute: 0,
+ * second: 0,
+ * isDate: false
+ * });
+ *
+ *
+ * @memberof ICAL
+*/
+declare class Time {
+ static _dowCache: {};
+ static _wnCache: {};
+ /**
+ * Returns the days in the given month
+ *
+ * @param {Number} month The month to check
+ * @param {Number} year The year to check
+ * @return {Number} The number of days in the month
+ */
+ static daysInMonth(month: number, year: number): number;
+ /**
+ * Checks if the year is a leap year
+ *
+ * @param {Number} year The year to check
+ * @return {Boolean} True, if the year is a leap year
+ */
+ static isLeapYear(year: number): boolean;
+ /**
+ * Create a new ICAL.Time from the day of year and year. The date is returned
+ * in floating timezone.
+ *
+ * @param {Number} aDayOfYear The day of year
+ * @param {Number} aYear The year to create the instance in
+ * @return {Time} The created instance with the calculated date
+ */
+ static fromDayOfYear(aDayOfYear: number, aYear: number): Time;
+ /**
+ * Returns a new ICAL.Time instance from a date string, e.g 2015-01-02.
+ *
+ * @deprecated Use {@link ICAL.Time.fromDateString} instead
+ * @param {String} str The string to create from
+ * @return {Time} The date/time instance
+ */
+ static fromStringv2(str: string): Time;
+ /**
+ * Returns a new ICAL.Time instance from a date string, e.g 2015-01-02.
+ *
+ * @param {String} aValue The string to create from
+ * @return {Time} The date/time instance
+ */
+ static fromDateString(aValue: string): Time;
+ /**
+ * Returns a new ICAL.Time instance from a date-time string, e.g
+ * 2015-01-02T03:04:05. If a property is specified, the timezone is set up
+ * from the property's TZID parameter.
+ *
+ * @param {String} aValue The string to create from
+ * @param {Property=} prop The property the date belongs to
+ * @return {Time} The date/time instance
+ */
+ static fromDateTimeString(aValue: string, prop?: Property | undefined): Time;
+ /**
+ * Returns a new ICAL.Time instance from a date or date-time string,
+ *
+ * @param {String} aValue The string to create from
+ * @param {Property=} prop The property the date belongs to
+ * @return {Time} The date/time instance
+ */
+ static fromString(aValue: string, aProperty: any): Time;
+ /**
+ * Creates a new ICAL.Time instance from the given Javascript Date.
+ *
+ * @param {?Date} aDate The Javascript Date to read, or null to reset
+ * @param {Boolean} [useUTC=false] If true, the UTC values of the date will be used
+ */
+ static fromJSDate(aDate: Date | null, useUTC?: boolean): Time;
+ /**
+ * Creates a new ICAL.Time instance from the the passed data object.
+ *
+ * @param {timeInit} aData Time initialization
+ * @param {Timezone=} aZone Timezone this position occurs in
+ */
+ static fromData: (aData: timeInit, aZone?: Timezone | undefined) => Time;
+ /**
+ * Creates a new ICAL.Time instance from the current moment.
+ * The instance is “floating” - has no timezone relation.
+ * To create an instance considering the time zone, call
+ * ICAL.Time.fromJSDate(new Date(), true)
+ * @return {Time}
+ */
+ static now(): Time;
+ /**
+ * Returns the date on which ISO week number 1 starts.
+ *
+ * @see Time#weekNumber
+ * @param {Number} aYear The year to search in
+ * @param {weekDay=} aWeekStart The week start weekday, used for calculation.
+ * @return {Time} The date on which week number 1 starts
+ */
+ static weekOneStarts(aYear: number, aWeekStart?: weekDay | undefined): Time;
+ /**
+ * Get the dominical letter for the given year. Letters range from A - G for
+ * common years, and AG to GF for leap years.
+ *
+ * @param {Number} yr The year to retrieve the letter for
+ * @return {String} The dominical letter.
+ */
+ static getDominicalLetter(yr: number): string;
+ static "__#1@#epochTime": any;
+ /**
+ * January 1st, 1970 as an ICAL.Time.
+ * @type {Time}
+ * @constant
+ * @instance
+ */
+ static get epochTime(): Time;
+ static _cmp_attr(a: any, b: any, attr: any): 0 | 1 | -1;
+ /**
+ * The days that have passed in the year after a given month. The array has
+ * two members, one being an array of passed days for non-leap years, the
+ * other analog for leap years.
+ * @example
+ * var isLeapYear = ICAL.Time.isLeapYear(year);
+ * var passedDays = ICAL.Time.daysInYearPassedMonth[isLeapYear][month];
+ * @type {Array.>}
+ */
+ static daysInYearPassedMonth: Array>;
+ static SUNDAY: number;
+ static MONDAY: number;
+ static TUESDAY: number;
+ static WEDNESDAY: number;
+ static THURSDAY: number;
+ static FRIDAY: number;
+ static SATURDAY: number;
+ /**
+ * The default weekday for the WKST part.
+ * @constant
+ * @default ICAL.Time.MONDAY
+ */
+ static DEFAULT_WEEK_START: number;
+ /**
+ * Creates a new ICAL.Time instance.
+ *
+ * @param {timeInit} data Time initialization
+ * @param {Timezone} zone timezone this position occurs in
+ */
+ constructor(data: timeInit, zone: Timezone);
+ wrappedJSObject: this;
+ /**
+ * @type {timeInit}
+ * @private
+ */
+ private _time;
+ /**
+ * The class identifier.
+ * @constant
+ * @type {String}
+ * @default "icaltime"
+ */
+ icalclass: string;
+ _cachedUnixTime: any;
+ /**
+ * The type name, to be used in the jCal object. This value may change and
+ * is strictly defined by the {@link ICAL.Time#isDate isDate} member.
+ * @type {String}
+ * @default "date-time"
+ */
+ get icaltype(): string;
+ /**
+ * The timezone for this time.
+ * @type {Timezone}
+ */
+ zone: Timezone;
+ /**
+ * Internal uses to indicate that a change has been made and the next read
+ * operation must attempt to normalize the value (for example changing the
+ * day to 33).
+ *
+ * @type {Boolean}
+ * @private
+ */
+ private _pendingNormalization;
+ set year(val: number);
+ /**
+ * The year of this date.
+ * @type {Number}
+ */
+ get year(): number;
+ set month(val: number);
+ /**
+ * The month of this date.
+ * @type {Number}
+ */
+ get month(): number;
+ set day(val: number);
+ /**
+ * The day of this date.
+ * @type {Number}
+ */
+ get day(): number;
+ set hour(val: number);
+ /**
+ * The hour of this date-time.
+ * @type {Number}
+ */
+ get hour(): number;
+ set minute(val: number);
+ /**
+ * The minute of this date-time.
+ * @type {Number}
+ */
+ get minute(): number;
+ set second(val: number);
+ /**
+ * The second of this date-time.
+ * @type {Number}
+ */
+ get second(): number;
+ set isDate(val: boolean);
+ /**
+ * If true, the instance represents a date (as opposed to a date-time)
+ * @type {Boolean}
+ */
+ get isDate(): boolean;
+ /**
+ * @private
+ * @param {String} attr Attribute to get (one of: year, month,
+ * day, hour, minute, second, isDate)
+ * @return {Number|Boolean} Current value for the attribute
+ */
+ private _getTimeAttr;
+ /**
+ * @private
+ * @param {String} attr Attribute to set (one of: year, month,
+ * day, hour, minute, second, isDate)
+ * @param {Number|Boolean} val New value for the attribute
+ */
+ private _setTimeAttr;
+ /**
+ * Returns a clone of the time object.
+ *
+ * @return {Time} The cloned object
+ */
+ clone(): Time;
+ /**
+ * Reset the time instance to epoch time
+ */
+ reset(): void;
+ /**
+ * Reset the time instance to the given date/time values.
+ *
+ * @param {Number} year The year to set
+ * @param {Number} month The month to set
+ * @param {Number} day The day to set
+ * @param {Number} hour The hour to set
+ * @param {Number} minute The minute to set
+ * @param {Number} second The second to set
+ * @param {Timezone} timezone The timezone to set
+ */
+ resetTo(year: number, month: number, day: number, hour: number, minute: number, second: number, timezone: Timezone): void;
+ /**
+ * Set up the current instance from the Javascript date value.
+ *
+ * @param {?Date} aDate The Javascript Date to read, or null to reset
+ * @param {Boolean} [useUTC=false] If true, the UTC values of the date will be used
+ */
+ fromJSDate(aDate: Date | null, useUTC?: boolean): this;
+ /**
+ * Sets up the current instance using members from the passed data object.
+ *
+ * @param {timeInit} aData Time initialization
+ * @param {Timezone=} aZone Timezone this position occurs in
+ */
+ fromData(aData: timeInit, aZone?: Timezone | undefined): this;
+ /**
+ * Calculate the day of week.
+ * @param {weekDay=} aWeekStart
+ * The week start weekday, defaults to SUNDAY
+ * @return {weekDay}
+ */
+ dayOfWeek(aWeekStart?: weekDay | undefined): weekDay;
+ /**
+ * Calculate the day of year.
+ * @return {Number}
+ */
+ dayOfYear(): number;
+ /**
+ * Returns a copy of the current date/time, rewound to the start of the
+ * week. The resulting ICAL.Time instance is of icaltype date, even if this
+ * is a date-time.
+ *
+ * @param {weekDay=} aWeekStart
+ * The week start weekday, defaults to SUNDAY
+ * @return {Time} The start of the week (cloned)
+ */
+ startOfWeek(aWeekStart?: weekDay | undefined): Time;
+ /**
+ * Returns a copy of the current date/time, shifted to the end of the week.
+ * The resulting ICAL.Time instance is of icaltype date, even if this is a
+ * date-time.
+ *
+ * @param {weekDay=} aWeekStart
+ * The week start weekday, defaults to SUNDAY
+ * @return {Time} The end of the week (cloned)
+ */
+ endOfWeek(aWeekStart?: weekDay | undefined): Time;
+ /**
+ * Returns a copy of the current date/time, rewound to the start of the
+ * month. The resulting ICAL.Time instance is of icaltype date, even if
+ * this is a date-time.
+ *
+ * @return {Time} The start of the month (cloned)
+ */
+ startOfMonth(): Time;
+ /**
+ * Returns a copy of the current date/time, shifted to the end of the
+ * month. The resulting ICAL.Time instance is of icaltype date, even if
+ * this is a date-time.
+ *
+ * @return {Time} The end of the month (cloned)
+ */
+ endOfMonth(): Time;
+ /**
+ * Returns a copy of the current date/time, rewound to the start of the
+ * year. The resulting ICAL.Time instance is of icaltype date, even if
+ * this is a date-time.
+ *
+ * @return {Time} The start of the year (cloned)
+ */
+ startOfYear(): Time;
+ /**
+ * Returns a copy of the current date/time, shifted to the end of the
+ * year. The resulting ICAL.Time instance is of icaltype date, even if
+ * this is a date-time.
+ *
+ * @return {Time} The end of the year (cloned)
+ */
+ endOfYear(): Time;
+ /**
+ * First calculates the start of the week, then returns the day of year for
+ * this date. If the day falls into the previous year, the day is zero or negative.
+ *
+ * @param {weekDay=} aFirstDayOfWeek
+ * The week start weekday, defaults to SUNDAY
+ * @return {Number} The calculated day of year
+ */
+ startDoyWeek(aFirstDayOfWeek?: weekDay | undefined): number;
+ /**
+ * Get the dominical letter for the current year. Letters range from A - G
+ * for common years, and AG to GF for leap years.
+ *
+ * @param {Number} yr The year to retrieve the letter for
+ * @return {String} The dominical letter.
+ */
+ getDominicalLetter(): string;
+ /**
+ * Finds the nthWeekDay relative to the current month (not day). The
+ * returned value is a day relative the month that this month belongs to so
+ * 1 would indicate the first of the month and 40 would indicate a day in
+ * the following month.
+ *
+ * @param {Number} aDayOfWeek Day of the week see the day name constants
+ * @param {Number} aPos Nth occurrence of a given week day values
+ * of 1 and 0 both indicate the first weekday of that type. aPos may
+ * be either positive or negative
+ *
+ * @return {Number} numeric value indicating a day relative
+ * to the current month of this time object
+ */
+ nthWeekDay(aDayOfWeek: number, aPos: number): number;
+ /**
+ * Checks if current time is the nth weekday, relative to the current
+ * month. Will always return false when rule resolves outside of current
+ * month.
+ *
+ * @param {weekDay} aDayOfWeek Day of week to check
+ * @param {Number} aPos Relative position
+ * @return {Boolean} True, if it is the nth weekday
+ */
+ isNthWeekDay(aDayOfWeek: weekDay, aPos: number): boolean;
+ /**
+ * Calculates the ISO 8601 week number. The first week of a year is the
+ * week that contains the first Thursday. The year can have 53 weeks, if
+ * January 1st is a Friday.
+ *
+ * Note there are regions where the first week of the year is the one that
+ * starts on January 1st, which may offset the week number. Also, if a
+ * different week start is specified, this will also affect the week
+ * number.
+ *
+ * @see Time.weekOneStarts
+ * @param {weekDay} aWeekStart The weekday the week starts with
+ * @return {Number} The ISO week number
+ */
+ weekNumber(aWeekStart: weekDay): number;
+ /**
+ * Adds the duration to the current time. The instance is modified in
+ * place.
+ *
+ * @param {Duration} aDuration The duration to add
+ */
+ addDuration(aDuration: Duration): void;
+ /**
+ * Subtract the date details (_excluding_ timezone). Useful for finding
+ * the relative difference between two time objects excluding their
+ * timezone differences.
+ *
+ * @param {Time} aDate The date to subtract
+ * @return {Duration} The difference as a duration
+ */
+ subtractDate(aDate: Time): Duration;
+ /**
+ * Subtract the date details, taking timezones into account.
+ *
+ * @param {Time} aDate The date to subtract
+ * @return {Duration} The difference in duration
+ */
+ subtractDateTz(aDate: Time): Duration;
+ /**
+ * Compares the ICAL.Time instance with another one, or a period.
+ *
+ * @param {Time|Period} aOther The instance to compare with
+ * @return {Number} -1, 0 or 1 for less/equal/greater
+ */
+ compare(other: any): number;
+ /**
+ * Compares only the date part of this instance with another one.
+ *
+ * @param {Time} other The instance to compare with
+ * @param {Timezone} tz The timezone to compare in
+ * @return {Number} -1, 0 or 1 for less/equal/greater
+ */
+ compareDateOnlyTz(other: Time, tz: Timezone): number;
+ /**
+ * Convert the instance into another timezone. The returned ICAL.Time
+ * instance is always a copy.
+ *
+ * @param {Timezone} zone The zone to convert to
+ * @return {Time} The copy, converted to the zone
+ */
+ convertToZone(zone: Timezone): Time;
+ /**
+ * Calculates the UTC offset of the current date/time in the timezone it is
+ * in.
+ *
+ * @return {Number} UTC offset in seconds
+ */
+ utcOffset(): number;
+ /**
+ * Returns an RFC 5545 compliant ical representation of this object.
+ *
+ * @return {String} ical date/date-time
+ */
+ toICALString(): string;
+ /**
+ * The string representation of this date/time, in jCal form
+ * (including : and - separators).
+ * @return {String}
+ */
+ toString(): string;
+ /**
+ * Converts the current instance to a Javascript date
+ * @return {Date}
+ */
+ toJSDate(): Date;
+ _normalize(): this;
+ /**
+ * Adjust the date/time by the given offset
+ *
+ * @param {Number} aExtraDays The extra amount of days
+ * @param {Number} aExtraHours The extra amount of hours
+ * @param {Number} aExtraMinutes The extra amount of minutes
+ * @param {Number} aExtraSeconds The extra amount of seconds
+ * @param {Number=} aTime The time to adjust, defaults to the
+ * current instance.
+ */
+ adjust(aExtraDays: number, aExtraHours: number, aExtraMinutes: number, aExtraSeconds: number, aTime?: number | undefined): this;
+ /**
+ * Sets up the current instance from unix time, the number of seconds since
+ * January 1st, 1970.
+ *
+ * @param {Number} seconds The seconds to set up with
+ */
+ fromUnixTime(seconds: number): void;
+ /**
+ * Converts the current instance to seconds since January 1st 1970.
+ *
+ * @return {Number} Seconds since 1970
+ */
+ toUnixTime(): number;
+ /**
+ * Converts time to into Object which can be serialized then re-created
+ * using the constructor.
+ *
+ * @example
+ * // toJSON will automatically be called
+ * var json = JSON.stringify(mytime);
+ *
+ * var deserialized = JSON.parse(json);
+ *
+ * var time = new ICAL.Time(deserialized);
+ *
+ * @return {Object}
+ */
+ toJSON(): any;
+}
+import Timezone from "./timezone.js";
+import Duration from "./duration.js";
+import Property from "./property.js";
+//# sourceMappingURL=time.d.ts.map
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/time.d.ts.map b/cron/scripts/node_modules/ical.js/dist/types/time.d.ts.map
new file mode 100644
index 0000000..e2d38e8
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/time.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../../lib/ical/time.js"],"names":[],"mappings":";;;;sBAqBa,OAAO,YAAY,EAAE,OAAO;;;;uBAI5B,OAAO,YAAY,EAAE,QAAQ;AAV1C;;;;;;;;;;;;GAYG;AAEH;;;;;;;;;;;;;;;;;;EAkBE;AACF;IACE,qBAAsB;IACtB,oBAAqB;IAErB;;;;;;OAMG;IACH,wDAaC;IAED;;;;;OAKG;IACH,yCAMC;IAED;;;;;;;OAOG;IACH,yDAFY,IAAI,CAkCf;IAED;;;;;;OAMG;IACH,kCAFY,IAAI,CASf;IAED;;;;;OAKG;IACH,uCAFY,IAAI,CAef;IAED;;;;;;;;OAQG;IACH,iDAHW,QAAQ,eACP,IAAI,CAiDf;IAED;;;;;;OAMG;IACH,mDAFY,IAAI,CAQf;IAED;;;;;OAKG;IACH,yBAHY,IAAI,iCAMf;IAED;;;;;OAKG;IACH,yBAHW,QAAQ,UACR,QAAQ,sBAKjB;IAEF;;;;;;OAMG;IACH,cAFY,IAAI,CAIf;IAED;;;;;;;OAOG;IACH,iDAHW,OAAO,eACN,IAAI,CAsBf;IAED;;;;;;OAMG;IACH,8CASC;IAED,8BAAyB;IACzB;;;;;OAKG;IACH,6BAcC;IAED,wDAIC;IAED;;;;;;;;OAQG;IACH,8BAFU,MAAO,aAAc,CAAC,CAK9B;IAEF,sBAAkB;IAClB,sBAAkB;IAClB,uBAAmB;IACnB,yBAAqB;IACrB,wBAAoB;IACpB,sBAAkB;IAClB,wBAAoB;IAEpB;;;;OAIG;IACH,kCAA8B;IAE9B;;;;;OAKG;IACH,kBAHW,QAAQ,QACR,QAAQ,EAqBlB;IAlBC,sBAA2B;IAE3B;;;OAGG;IACH,cAAgC;IAclC;;;;;OAKG;IACH,kBAAuB;IACvB,qBAAuB;IAEvB;;;;;OAKG;IACH,uBAEC;IAED;;;OAGG;IACH,MAFU,QAAQ,CAEN;IAEZ;;;;;;;OAOG;IACH,8BAA8B;IAU9B,sBAEC;IAVD;;;OAGG;IACH,mBAEC;IAcD,uBAEC;IAVD;;;OAGG;IACH,oBAEC;IAcD,qBAEC;IAVD;;;OAGG;IACH,kBAEC;IAcD,sBAEC;IAVD;;;OAGG;IACH,mBAEC;IAcD,wBAEC;IAVD;;;OAGG;IACH,qBAEC;IAcD,wBAEC;IAVD;;;OAGG;IACH,qBAEC;IAcD,yBAEC;IAVD;;;OAGG;IACH,sBAEC;IAMD;;;;;OAKG;IACH,qBAOC;IAED;;;;;OAKG;IACH,qBAUC;IAED;;;;OAIG;IACH,SAFY,IAAI,CAIf;IAED;;OAEG;IACH,cAGC;IAED;;;;;;;;;;OAUG;IACH,0GAFW,QAAQ,QAYlB;IAED;;;;;OAKG;IACH,kBAHY,IAAI,iCA2Bf;IAED;;;;;OAKG;IACH,gBAHW,QAAQ,UACR,QAAQ,oBAuClB;IAED;;;;;OAKG;IACH,uBAJW,OAAO,eAEN,OAAO,CA0BlB;IAED;;;OAGG;IACH,oBAIC;IAED;;;;;;;;OAQG;IACH,yBAJW,OAAO,eAEN,IAAI,CAWf;IAED;;;;;;;;OAQG;IACH,uBAJW,OAAO,eAEN,IAAI,CAWf;IAED;;;;;;OAMG;IACH,gBAFY,IAAI,CAUf;IAED;;;;;;OAMG;IACH,cAFY,IAAI,CAUf;IAED;;;;;;OAMG;IACH,eAFY,IAAI,CAWf;IAED;;;;;;OAMG;IACH,aAFY,IAAI,CAWf;IAED;;;;;;;OAOG;IACH,+BAJW,OAAO,sBASjB;IAED;;;;;;OAMG;IACH,6BAEC;IAED;;;;;;;;;;;;;OAaG;IACH,qDAuEC;IAED;;;;;;;;OAQG;IACH,yBAJW,OAAO,yBAmBjB;IAED;;;;;;;;;;;;;OAaG;IACH,uBAHW,OAAO,UAmCjB;IAED;;;;;OAKG;IACH,uBAFW,QAAQ,QA0BlB;IAED;;;;;;;OAOG;IACH,oBAHW,IAAI,GACH,QAAQ,CAMnB;IAED;;;;;OAKG;IACH,sBAHW,IAAI,GACH,QAAQ,CAMnB;IAED;;;;;OAKG;IACH,4BAWC;IAED;;;;;;OAMG;IACH,yBAJW,IAAI,MACJ,QAAQ,UAalB;IAED;;;;;;OAMG;IACH,oBAHW,QAAQ,GACP,IAAI,CAYf;IAED;;;;;OAKG;IACH,oBAOC;IAED;;;;OAIG;IACH,uBAQC;IAED;;;;OAIG;IACH,mBAgBC;IAED;;;OAGG;IACH,YAFY,IAAI,CAaf;IAED,mBASC;IAED;;;;;;;;;OASG;IACH,gIAoFC;IAED;;;;;OAKG;IACH,oCAoBC;IAED;;;;OAIG;IACH,qBAoBC;IAED;;;;;;;;;;;;;OAaG;IACH,cA2BC;CACF;qBAt3CoB,eAAe;qBACf,eAAe;qBAKf,eAAe"}
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/timezone.d.ts b/cron/scripts/node_modules/ical.js/dist/types/timezone.d.ts
new file mode 100644
index 0000000..3cc28ae
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/timezone.d.ts
@@ -0,0 +1,169 @@
+export default Timezone;
+/**
+ * Timezone representation.
+ *
+ * @example
+ * var vcalendar;
+ * var timezoneComp = vcalendar.getFirstSubcomponent('vtimezone');
+ * var tzid = timezoneComp.getFirstPropertyValue('tzid');
+ *
+ * var timezone = new ICAL.Timezone({
+ * component: timezoneComp,
+ * tzid
+ * });
+ *
+ * @memberof ICAL
+ */
+declare class Timezone {
+ static _compare_change_fn(a: any, b: any): 0 | 1 | -1;
+ /**
+ * Convert the date/time from one zone to the next.
+ *
+ * @param {Time} tt The time to convert
+ * @param {Timezone} from_zone The source zone to convert from
+ * @param {Timezone} to_zone The target zone to convert to
+ * @return {Time} The converted date/time object
+ */
+ static convert_time(tt: Time, from_zone: Timezone, to_zone: Timezone): Time;
+ /**
+ * Creates a new ICAL.Timezone instance from the passed data object.
+ *
+ * @param {Component|Object} aData options for class
+ * @param {String|Component} aData.component
+ * If aData is a simple object, then this member can be set to either a
+ * string containing the component data, or an already parsed
+ * ICAL.Component
+ * @param {String} aData.tzid The timezone identifier
+ * @param {String} aData.location The timezone locationw
+ * @param {String} aData.tznames An alternative string representation of the
+ * timezone
+ * @param {Number} aData.latitude The latitude of the timezone
+ * @param {Number} aData.longitude The longitude of the timezone
+ */
+ static fromData(aData: Component | any): Timezone;
+ /**
+ * The instance describing the UTC timezone
+ * @type {Timezone}
+ * @constant
+ * @instance
+ */
+ static "__#2@#utcTimezone": Timezone;
+ static get utcTimezone(): Timezone;
+ /**
+ * The instance describing the local timezone
+ * @type {Timezone}
+ * @constant
+ * @instance
+ */
+ static "__#2@#localTimezone": Timezone;
+ static get localTimezone(): Timezone;
+ /**
+ * Adjust a timezone change object.
+ * @private
+ * @param {Object} change The timezone change object
+ * @param {Number} days The extra amount of days
+ * @param {Number} hours The extra amount of hours
+ * @param {Number} minutes The extra amount of minutes
+ * @param {Number} seconds The extra amount of seconds
+ */
+ private static adjust_change;
+ static _minimumExpansionYear: number;
+ static EXTRA_COVERAGE: number;
+ /**
+ * Creates a new ICAL.Timezone instance, by passing in a tzid and component.
+ *
+ * @param {Component|Object} data options for class
+ * @param {String|Component} data.component
+ * If data is a simple object, then this member can be set to either a
+ * string containing the component data, or an already parsed
+ * ICAL.Component
+ * @param {String} data.tzid The timezone identifier
+ * @param {String} data.location The timezone locationw
+ * @param {String} data.tznames An alternative string representation of the
+ * timezone
+ * @param {Number} data.latitude The latitude of the timezone
+ * @param {Number} data.longitude The longitude of the timezone
+ */
+ constructor(data: Component | any);
+ wrappedJSObject: this;
+ /**
+ * Timezone identifier
+ * @type {String}
+ */
+ tzid: string;
+ /**
+ * Timezone location
+ * @type {String}
+ */
+ location: string;
+ /**
+ * Alternative timezone name, for the string representation
+ * @type {String}
+ */
+ tznames: string;
+ /**
+ * The primary latitude for the timezone.
+ * @type {Number}
+ */
+ latitude: number;
+ /**
+ * The primary longitude for the timezone.
+ * @type {Number}
+ */
+ longitude: number;
+ /**
+ * The vtimezone component for this timezone.
+ * @type {Component}
+ */
+ component: Component;
+ /**
+ * The year this timezone has been expanded to. All timezone transition
+ * dates until this year are known and can be used for calculation
+ *
+ * @private
+ * @type {Number}
+ */
+ private expandedUntilYear;
+ /**
+ * The class identifier.
+ * @constant
+ * @type {String}
+ * @default "icaltimezone"
+ */
+ icalclass: string;
+ /**
+ * Sets up the current instance using members from the passed data object.
+ *
+ * @param {Component|Object} aData options for class
+ * @param {String|Component} aData.component
+ * If aData is a simple object, then this member can be set to either a
+ * string containing the component data, or an already parsed
+ * ICAL.Component
+ * @param {String} aData.tzid The timezone identifier
+ * @param {String} aData.location The timezone locationw
+ * @param {String} aData.tznames An alternative string representation of the
+ * timezone
+ * @param {Number} aData.latitude The latitude of the timezone
+ * @param {Number} aData.longitude The longitude of the timezone
+ */
+ fromData(aData: Component | any): this;
+ changes: any[];
+ /**
+ * Finds the utcOffset the given time would occur in this timezone.
+ *
+ * @param {Time} tt The time to check for
+ * @return {Number} utc offset in seconds
+ */
+ utcOffset(tt: Time): number;
+ _findNearbyChange(change: any): number;
+ _ensureCoverage(aYear: any): void;
+ _expandComponent(aComponent: any, aYear: any, changes: any): any;
+ /**
+ * The string representation of this timezone.
+ * @return {String}
+ */
+ toString(): string;
+}
+import Component from "./component.js";
+import Time from "./time.js";
+//# sourceMappingURL=timezone.d.ts.map
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/timezone.d.ts.map b/cron/scripts/node_modules/ical.js/dist/types/timezone.d.ts.map
new file mode 100644
index 0000000..27da2e8
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/timezone.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"timezone.d.ts","sourceRoot":"","sources":["../../lib/ical/timezone.js"],"names":[],"mappings":";AAYA;;;;;;;;;;;;;;GAcG;AACH;IACE,sDAoBC;IAED;;;;;;;OAOG;IACH,wBALW,IAAI,aACJ,QAAQ,WACR,QAAQ,GACP,IAAI,CAkBf;IAED;;;;;;;;;;;;;;OAcG;IACH,uBAZW,SAAS,MAAO,YAe1B;IAED;;;;;OAKG;IACH,4BAJU,QAAQ,CAIS;IAC3B,mCAOC;IAED;;;;;OAKG;IACH,8BAJU,QAAQ,CAIW;IAC7B,qCAOC;IAED;;;;;;;;OAQG;IACH,6BASC;IAED,qCAAkC;IAClC,8BAA0B;IAE1B;;;;;;;;;;;;;;OAcG;IACH,kBAZW,SAAS,MAAO,EAe1B;IAFC,sBAA2B;IAK7B;;;OAGG;IACH,aAAU;IAEV;;;OAGG;IACH,iBAAc;IAEd;;;OAGG;IACH,gBAAa;IAEb;;;OAGG;IACH,iBAAe;IAEf;;;OAGG;IACH,kBAAgB;IAEhB;;;OAGG;IACH,WAFU,SAAS,CAEF;IAEjB;;;;;;OAMG;IACH,0BAAsB;IAEtB;;;;;OAKG;IACH,kBAA2B;IAE3B;;;;;;;;;;;;;;OAcG;IACH,gBAZW,SAAS,MAAO,QAkD1B;IApCC,eAAiB;IAsCnB;;;;;OAKG;IACH,cAHW,IAAI,UAiFd;IAED,uCAaC;IAED,kCA2BC;IAED,iEAyGC;IAED;;;OAGG;IACH,mBAEC;CACF;sBAtgBqB,gBAAgB;iBADrB,WAAW"}
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/timezone_service.d.ts b/cron/scripts/node_modules/ical.js/dist/types/timezone_service.d.ts
new file mode 100644
index 0000000..871a6ad
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/timezone_service.d.ts
@@ -0,0 +1,13 @@
+export default TimezoneService;
+declare namespace TimezoneService {
+ const count: number;
+ function reset(): void;
+ function _hard_reset(): void;
+ function has(tzid: string): boolean;
+ function get(tzid: string): Timezone;
+ function register(timezone: Timezone | Component, name?: string): void;
+ function remove(tzid: string): Timezone;
+}
+import Timezone from "./timezone.js";
+import Component from "./component.js";
+//# sourceMappingURL=timezone_service.d.ts.map
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/timezone_service.d.ts.map b/cron/scripts/node_modules/ical.js/dist/types/timezone_service.d.ts.map
new file mode 100644
index 0000000..d078cee
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/timezone_service.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"timezone_service.d.ts","sourceRoot":"","sources":["../../lib/ical/timezone_service.js"],"names":[],"mappings":";;;IA8BS,uBAON;IACY,6BAEZ;IAQI,oCAMJ;IAQI,qCAMJ;IAYS,uEA+BT;IAQO,wCAMP;;qBAxHkB,eAAe;sBAGd,gBAAgB"}
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/types.d.ts b/cron/scripts/node_modules/ical.js/dist/types/types.d.ts
new file mode 100644
index 0000000..497c478
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/types.d.ts
@@ -0,0 +1,217 @@
+/**
+ * The weekday, 1 = SUNDAY, 7 = SATURDAY. Access via
+ * ICAL.Time.MONDAY, ICAL.Time.TUESDAY, ...
+ *
+ * @typedef {Number} weekDay
+ * @memberof ICAL.Time
+ */
+/**
+ * @typedef {Object} timeInit Time initialization
+ * @property {Number=} year The year for this date
+ * @property {Number=} month The month for this date
+ * @property {Number=} day The day for this date
+ * @property {Number=} hour The hour for this date
+ * @property {Number=} minute The minute for this date
+ * @property {Number=} second The second for this date
+ * @property {Boolean=} isDate If true, the instance represents a date
+ * (as opposed to a date-time)
+ * @property {String=} timezone Timezone id if zone is unknown [internal]
+ * @property {Timezone=} zone Resolved timezone object [internal]
+ * @memberof ICAL.Time
+ */
+/**
+ * Possible frequency values for the FREQ part
+ * (YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, SECONDLY)
+ *
+ * @typedef {String} frequencyValues
+ * @memberof ICAL.Recur
+ */
+/**
+ * This object is returned by {@link ICAL.Event#getOccurrenceDetails getOccurrenceDetails}
+ * @memberof ICAL.Event
+ * @typedef {Object} occurrenceDetails
+ * @property {Time} recurrenceId The passed in recurrence id
+ * @property {Event} item The occurrence
+ * @property {Time} startDate The start of the occurrence
+ * @property {Time} endDate The end of the occurrence
+ */
+/**
+ * The state for parsing content lines from an iCalendar/vCard string.
+ *
+ * @private
+ * @memberof ICAL.parse
+ * @typedef {Object} parserState
+ * @property {designSet} designSet The design set to use for parsing
+ * @property {Component[]} stack The stack of components being processed
+ * @property {Component} component The currently active component
+ */
+/**
+ * A jCal component.
+ *
+ * TODO: Properly typedef this when https://github.com/hegemonic/catharsis/pull/70
+ * is merged. Documentation is ignored until this can be documented properly.
+ *
+ * @example
+ * ["vevent", [...properties here...], [...components here...] ]
+ *
+ * @ignore
+ * @typedef {Array} jCalComponent
+ * @property {String} 0 The component name
+ * @property {jCalProperty[]} 1 The properties of this component
+ * @property {jCalComponent[]} 2 The subcomponents of this component
+ */
+/**
+ * A designSet describes value, parameter and property data. It is used by
+ * ther parser and stringifier in components and properties to determine they
+ * should be represented.
+ *
+ * @memberof ICAL.design
+ * @typedef {Object} designSet
+ * @property {Object} value Definitions for value types, keys are type names
+ * @property {Object} param Definitions for params, keys are param names
+ * @property {Object} property Definitions for properties, keys are property names
+ * @property {boolean} propertyGroups If content lines may include a group name
+ */
+/**
+ * The jCal Geo type. This is a tuple representing a geographical location.
+ * The first element is the Latitude and the second element is the Longitude.
+ *
+ * TODO: Properly typedef this when https://github.com/hegemonic/catharsis/pull/70
+ *
+ * @typedef {Array} Geo
+ * @property {Number} 0 Latitude
+ * @property {Number} 1 Longitude
+ */
+export const _: {};
+/**
+ * The weekday, 1 = SUNDAY, 7 = SATURDAY. Access via
+ * ICAL.Time.MONDAY, ICAL.Time.TUESDAY, ...
+ */
+export type weekDay = number;
+/**
+ * Time initialization
+ */
+export type timeInit = {
+ /**
+ * The year for this date
+ */
+ year?: number | undefined;
+ /**
+ * The month for this date
+ */
+ month?: number | undefined;
+ /**
+ * The day for this date
+ */
+ day?: number | undefined;
+ /**
+ * The hour for this date
+ */
+ hour?: number | undefined;
+ /**
+ * The minute for this date
+ */
+ minute?: number | undefined;
+ /**
+ * The second for this date
+ */
+ second?: number | undefined;
+ /**
+ * If true, the instance represents a date
+ * (as opposed to a date-time)
+ */
+ isDate?: boolean | undefined;
+ /**
+ * Timezone id if zone is unknown [internal]
+ */
+ timezone?: string | undefined;
+ /**
+ * Resolved timezone object [internal]
+ */
+ zone?: Timezone | undefined;
+};
+/**
+ * Possible frequency values for the FREQ part
+ * (YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, SECONDLY)
+ */
+export type frequencyValues = string;
+/**
+ * This object is returned by {@link ICAL.EventgetOccurrenceDetails getOccurrenceDetails}
+ */
+export type occurrenceDetails = {
+ /**
+ * The passed in recurrence id
+ */
+ recurrenceId: Time;
+ /**
+ * The occurrence
+ */
+ item: Event;
+ /**
+ * The start of the occurrence
+ */
+ startDate: Time;
+ /**
+ * The end of the occurrence
+ */
+ endDate: Time;
+};
+/**
+ * The state for parsing content lines from an iCalendar/vCard string.
+ */
+export type parserState = {
+ /**
+ * The design set to use for parsing
+ */
+ designSet: designSet;
+ /**
+ * The stack of components being processed
+ */
+ stack: Component[];
+ /**
+ * The currently active component
+ */
+ component: Component;
+};
+/**
+ * A jCal component.
+ *
+ * TODO: Properly typedef this when https://github.com/hegemonic/catharsis/pull/70
+ * is merged. Documentation is ignored until this can be documented properly.
+ */
+export type jCalComponent = any[];
+/**
+ * A designSet describes value, parameter and property data. It is used by
+ * ther parser and stringifier in components and properties to determine they
+ * should be represented.
+ */
+export type designSet = {
+ /**
+ * Definitions for value types, keys are type names
+ */
+ value: any;
+ /**
+ * Definitions for params, keys are param names
+ */
+ param: any;
+ /**
+ * Definitions for properties, keys are property names
+ */
+ property: any;
+ /**
+ * If content lines may include a group name
+ */
+ propertyGroups: boolean;
+};
+/**
+ * The jCal Geo type. This is a tuple representing a geographical location.
+ * The first element is the Latitude and the second element is the Longitude.
+ *
+ * TODO: Properly typedef this when https://github.com/hegemonic/catharsis/pull/70
+ */
+export type Geo = any[];
+import Timezone from "./timezone";
+import Time from "./time";
+import Event from "./event";
+import Component from "./component";
+//# sourceMappingURL=types.d.ts.map
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/types.d.ts.map b/cron/scripts/node_modules/ical.js/dist/types/types.d.ts.map
new file mode 100644
index 0000000..ebeaa60
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/types.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../lib/ical/types.js"],"names":[],"mappings":"AAgBA;;;;;;GAMG;AAEH;;;;;;;;;;;;;GAaG;AAEH;;;;;;GAMG;AAEH;;;;;;;;GAQG;AAEH;;;;;;;;;GASG;AAEH;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;;;;;GAWG;AAEH;;;;;;;;;GASG;AAEH,mBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAzEN,QAAQ;;;;;;;;;;;;;;kBAgBR,IAAI;;;;UACJ,KAAK;;;;eACL,IAAI;;;;aACJ,IAAI;;;;;;;;;eASJ,SAAS;;;;WACT,SAAS,EAAE;;;;eACX,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBA6BT,OAAO;;;;;;;;;qBAhFA,YAAY;iBADhB,QAAQ;kBADP,SAAS;sBADL,aAAa"}
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/utc_offset.d.ts b/cron/scripts/node_modules/ical.js/dist/types/utc_offset.d.ts
new file mode 100644
index 0000000..9338293
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/utc_offset.d.ts
@@ -0,0 +1,112 @@
+export default UtcOffset;
+/**
+ * This class represents the "utc-offset" value type, with various calculation and manipulation
+ * methods.
+ *
+ * @memberof ICAL
+ */
+declare class UtcOffset {
+ /**
+ * Creates a new {@link ICAL.UtcOffset} instance from the passed string.
+ *
+ * @param {String} aString The string to parse
+ * @return {Duration} The created utc-offset instance
+ */
+ static fromString(aString: string): Duration;
+ /**
+ * Creates a new {@link ICAL.UtcOffset} instance from the passed seconds
+ * value.
+ *
+ * @param {Number} aSeconds The number of seconds to convert
+ */
+ static fromSeconds(aSeconds: number): UtcOffset;
+ /**
+ * Creates a new ICAL.UtcOffset instance.
+ *
+ * @param {Object} aData An object with members of the utc offset
+ * @param {Number=} aData.hours The hours for the utc offset
+ * @param {Number=} aData.minutes The minutes in the utc offset
+ * @param {Number=} aData.factor The factor for the utc-offset, either -1 or 1
+ */
+ constructor(aData: {
+ hours?: number | undefined;
+ minutes?: number | undefined;
+ factor?: number | undefined;
+ });
+ /**
+ * The hours in the utc-offset
+ * @type {Number}
+ */
+ hours: number;
+ /**
+ * The minutes in the utc-offset
+ * @type {Number}
+ */
+ minutes: number;
+ /**
+ * The sign of the utc offset, 1 for positive offset, -1 for negative
+ * offsets.
+ * @type {Number}
+ */
+ factor: number;
+ /**
+ * The type name, to be used in the jCal object.
+ * @constant
+ * @type {String}
+ * @default "utc-offset"
+ */
+ icaltype: string;
+ /**
+ * Returns a clone of the utc offset object.
+ *
+ * @return {UtcOffset} The cloned object
+ */
+ clone(): UtcOffset;
+ /**
+ * Sets up the current instance using members from the passed data object.
+ *
+ * @param {Object} aData An object with members of the utc offset
+ * @param {Number=} aData.hours The hours for the utc offset
+ * @param {Number=} aData.minutes The minutes in the utc offset
+ * @param {Number=} aData.factor The factor for the utc-offset, either -1 or 1
+ */
+ fromData(aData: {
+ hours?: number | undefined;
+ minutes?: number | undefined;
+ factor?: number | undefined;
+ }): void;
+ /**
+ * Sets up the current instance from the given seconds value. The seconds
+ * value is truncated to the minute. Offsets are wrapped when the world
+ * ends, the hour after UTC+14:00 is UTC-12:00.
+ *
+ * @param {Number} aSeconds The seconds to convert into an offset
+ */
+ fromSeconds(aSeconds: number): this;
+ /**
+ * Convert the current offset to a value in seconds
+ *
+ * @return {Number} The offset in seconds
+ */
+ toSeconds(): number;
+ /**
+ * Compare this utc offset with another one.
+ *
+ * @param {UtcOffset} other The other offset to compare with
+ * @return {Number} -1, 0 or 1 for less/equal/greater
+ */
+ compare(other: UtcOffset): number;
+ _normalize(): void;
+ /**
+ * The iCalendar string representation of this utc-offset.
+ * @return {String}
+ */
+ toICALString(): string;
+ /**
+ * The string representation of this utc-offset.
+ * @return {String}
+ */
+ toString(): string;
+}
+import Duration from "./duration.js";
+//# sourceMappingURL=utc_offset.d.ts.map
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/utc_offset.d.ts.map b/cron/scripts/node_modules/ical.js/dist/types/utc_offset.d.ts.map
new file mode 100644
index 0000000..fe68236
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/utc_offset.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"utc_offset.d.ts","sourceRoot":"","sources":["../../lib/ical/utc_offset.js"],"names":[],"mappings":";AAWA;;;;;GAKG;AACH;IACE;;;;;OAKG;IACH,oCAFY,QAAQ,CAWnB;IAED;;;;;OAKG;IACH,gDAIC;IAED;;;;;;;OAOG;IACH;QAJ0B,KAAK;QACL,OAAO;QACP,MAAM;OAI/B;IAED;;;OAGG;IACH,cAAU;IAEV;;;OAGG;IACH,gBAAY;IAEZ;;;;OAIG;IACH,eAAW;IAEX;;;;;OAKG;IACH,iBAAwB;IAExB;;;;OAIG;IACH,SAFY,SAAS,CAIpB;IAED;;;;;;;OAOG;IACH;QAJ0B,KAAK;QACL,OAAO;QACP,MAAM;aAS/B;IAED;;;;;;OAMG;IACH,oCASC;IAED;;;;OAIG;IACH,oBAEC;IAED;;;;;OAKG;IACH,eAHW,SAAS,UAOnB;IAED,mBAiBC;IAED;;;OAGG;IACH,uBAEC;IAED;;;OAGG;IACH,mBAEC;CACF;qBAjLoB,eAAe"}
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/vcard_time.d.ts b/cron/scripts/node_modules/ical.js/dist/types/vcard_time.d.ts
new file mode 100644
index 0000000..660cd3a
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/vcard_time.d.ts
@@ -0,0 +1,67 @@
+export default VCardTime;
+/**
+ * Describes a vCard time, which has slight differences to the ICAL.Time.
+ * Properties can be null if not specified, for example for dates with
+ * reduced accuracy or truncation.
+ *
+ * Note that currently not all methods are correctly re-implemented for
+ * VCardTime. For example, comparison will have undefined results when some
+ * members are null.
+ *
+ * Also, normalization is not yet implemented for this class!
+ *
+ * @memberof ICAL
+ * @extends {Time}
+ */
+declare class VCardTime extends Time {
+ /**
+ * Returns a new ICAL.VCardTime instance from a date and/or time string.
+ *
+ * @param {String} aValue The string to create from
+ * @param {String} aIcalType The type for this instance, e.g. date-and-or-time
+ * @return {VCardTime} The date/time instance
+ */
+ static fromDateAndOrTimeString(aValue: string, aIcalType: string): VCardTime;
+ /**
+ * Creates a new ICAL.VCardTime instance.
+ *
+ * @param {Object} data The data for the time instance
+ * @param {Number=} data.year The year for this date
+ * @param {Number=} data.month The month for this date
+ * @param {Number=} data.day The day for this date
+ * @param {Number=} data.hour The hour for this date
+ * @param {Number=} data.minute The minute for this date
+ * @param {Number=} data.second The second for this date
+ * @param {Timezone|UtcOffset} zone The timezone to use
+ * @param {String} icaltype The type for this date/time object
+ */
+ constructor(data: {
+ year?: number | undefined;
+ month?: number | undefined;
+ day?: number | undefined;
+ hour?: number | undefined;
+ minute?: number | undefined;
+ second?: number | undefined;
+ }, zone: Timezone | UtcOffset, icaltype: string);
+ /**
+ * The type name, to be used in the jCal object.
+ * @type {String}
+ * @default "date-and-or-time"
+ */
+ icaltype: string;
+ /**
+ * Returns a clone of the vcard date/time object.
+ *
+ * @return {VCardTime} The cloned object
+ */
+ clone(): VCardTime;
+ _normalize(): this;
+ /**
+ * @inheritdoc
+ */
+ utcOffset(...args: any[]): any;
+}
+import Time from "./time.js";
+import Timezone from "./timezone.js";
+import UtcOffset from "./utc_offset.js";
+//# sourceMappingURL=vcard_time.d.ts.map
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/dist/types/vcard_time.d.ts.map b/cron/scripts/node_modules/ical.js/dist/types/vcard_time.d.ts.map
new file mode 100644
index 0000000..571abb2
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/dist/types/vcard_time.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"vcard_time.d.ts","sourceRoot":"","sources":["../../lib/ical/vcard_time.js"],"names":[],"mappings":";AAWA;;;;;;;;;;;;;GAaG;AACH;IACE;;;;;;OAMG;IACH,mEAFY,SAAS,CAoCpB;IAGD;;;;;;;;;;;;OAYG;IACH;QATyB,IAAI;QACJ,KAAK;QACL,GAAG;QACH,IAAI;QACJ,MAAM;QACN,MAAM;aACpB,QAAQ,GAAC,SAAS,oBAM5B;IAUD;;;;OAIG;IACH,iBAA8B;IAE9B;;;;OAIG;IACH,SAFY,SAAS,CAIpB;IAED,mBAEC;IAED;;OAEG;IACH,+BAMC;CAwDF;iBA/KgB,WAAW;qBACP,eAAe;sBAFd,iBAAiB"}
\ No newline at end of file
diff --git a/cron/scripts/node_modules/ical.js/lib/ical/binary.js b/cron/scripts/node_modules/ical.js/lib/ical/binary.js
new file mode 100644
index 0000000..57c9dee
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/lib/ical/binary.js
@@ -0,0 +1,173 @@
+/* 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 */
+
+/**
+ * Represents the BINARY value type, which contains extra methods for encoding and decoding.
+ *
+ * @memberof ICAL
+ */
+class Binary {
+ /**
+ * Creates a binary value from the given string.
+ *
+ * @param {String} aString The binary value string
+ * @return {Binary} The binary value instance
+ */
+ static fromString(aString) {
+ return new Binary(aString);
+ }
+
+ /**
+ * Creates a new ICAL.Binary instance
+ *
+ * @param {String} aValue The binary data for this value
+ */
+ constructor(aValue) {
+ this.value = aValue;
+ }
+
+ /**
+ * The type name, to be used in the jCal object.
+ * @default "binary"
+ * @constant
+ */
+ icaltype = "binary";
+
+ /**
+ * Base64 decode the current value
+ *
+ * @return {String} The base64-decoded value
+ */
+ decodeValue() {
+ return this._b64_decode(this.value);
+ }
+
+ /**
+ * Encodes the passed parameter with base64 and sets the internal
+ * value to the result.
+ *
+ * @param {String} aValue The raw binary value to encode
+ */
+ setEncodedValue(aValue) {
+ this.value = this._b64_encode(aValue);
+ }
+
+ _b64_encode(data) {
+ // http://kevin.vanzonneveld.net
+ // + original by: Tyler Akins (http://rumkin.com)
+ // + improved by: Bayron Guevara
+ // + improved by: Thunder.m
+ // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ // + bugfixed by: Pellentesque Malesuada
+ // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ // + improved by: Rafał Kukawski (http://kukawski.pl)
+ // * example 1: base64_encode('Kevin van Zonneveld');
+ // * returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA=='
+ // mozilla has this native
+ // - but breaks in 2.0.0.12!
+ //if (typeof this.window['atob'] == 'function') {
+ // return atob(data);
+ //}
+ let b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
+ "abcdefghijklmnopqrstuvwxyz0123456789+/=";
+ let o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
+ ac = 0,
+ enc = "",
+ tmp_arr = [];
+
+ if (!data) {
+ return data;
+ }
+
+ do { // pack three octets into four hexets
+ o1 = data.charCodeAt(i++);
+ o2 = data.charCodeAt(i++);
+ o3 = data.charCodeAt(i++);
+
+ bits = o1 << 16 | o2 << 8 | o3;
+
+ h1 = bits >> 18 & 0x3f;
+ h2 = bits >> 12 & 0x3f;
+ h3 = bits >> 6 & 0x3f;
+ h4 = bits & 0x3f;
+
+ // use hexets to index into b64, and append result to encoded string
+ tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
+ } while (i < data.length);
+
+ enc = tmp_arr.join('');
+
+ let r = data.length % 3;
+
+ return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3);
+
+ }
+
+ _b64_decode(data) {
+ // http://kevin.vanzonneveld.net
+ // + original by: Tyler Akins (http://rumkin.com)
+ // + improved by: Thunder.m
+ // + input by: Aman Gupta
+ // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ // + bugfixed by: Onno Marsman
+ // + bugfixed by: Pellentesque Malesuada
+ // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ // + input by: Brett Zamir (http://brett-zamir.me)
+ // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ // * example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA==');
+ // * returns 1: 'Kevin van Zonneveld'
+ // mozilla has this native
+ // - but breaks in 2.0.0.12!
+ //if (typeof this.window['btoa'] == 'function') {
+ // return btoa(data);
+ //}
+ let b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
+ "abcdefghijklmnopqrstuvwxyz0123456789+/=";
+ let o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
+ ac = 0,
+ dec = "",
+ tmp_arr = [];
+
+ if (!data) {
+ return data;
+ }
+
+ data += '';
+
+ do { // unpack four hexets into three octets using index points in b64
+ h1 = b64.indexOf(data.charAt(i++));
+ h2 = b64.indexOf(data.charAt(i++));
+ h3 = b64.indexOf(data.charAt(i++));
+ h4 = b64.indexOf(data.charAt(i++));
+
+ bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
+
+ o1 = bits >> 16 & 0xff;
+ o2 = bits >> 8 & 0xff;
+ o3 = bits & 0xff;
+
+ if (h3 == 64) {
+ tmp_arr[ac++] = String.fromCharCode(o1);
+ } else if (h4 == 64) {
+ tmp_arr[ac++] = String.fromCharCode(o1, o2);
+ } else {
+ tmp_arr[ac++] = String.fromCharCode(o1, o2, o3);
+ }
+ } while (i < data.length);
+
+ dec = tmp_arr.join('');
+
+ return dec;
+ }
+
+ /**
+ * The string representation of this value
+ * @return {String}
+ */
+ toString() {
+ return this.value;
+ }
+}
+export default Binary;
diff --git a/cron/scripts/node_modules/ical.js/lib/ical/component.js b/cron/scripts/node_modules/ical.js/lib/ical/component.js
new file mode 100644
index 0000000..54e8e7e
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/lib/ical/component.js
@@ -0,0 +1,635 @@
+/* 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 Property from "./property.js";
+import Timezone from "./timezone.js";
+import ICALParse from "./parse.js";
+import stringify from "./stringify.js";
+import design from "./design.js";
+// needed for typescript type resolution
+// eslint-disable-next-line no-unused-vars
+import Duration from "./duration.js";
+// needed for typescript type resolution
+// eslint-disable-next-line no-unused-vars
+import UtcOffset from "./utc_offset.js";
+// needed for typescript type resolution
+// eslint-disable-next-line no-unused-vars
+import Binary from "./binary.js";
+// needed for typescript type resolution
+// eslint-disable-next-line no-unused-vars
+import Period from "./period.js";
+// needed for typescript type resolution
+// eslint-disable-next-line no-unused-vars
+import Recur from "./recur.js";
+// needed for typescript type resolution
+// eslint-disable-next-line no-unused-vars
+import Time from "./time.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
+ * @typedef {import("./types.js").Geo} Geo
+ * Imports the 'Geo' type from the "types.js" module
+ */
+
+const NAME_INDEX = 0;
+const PROPERTY_INDEX = 1;
+const COMPONENT_INDEX = 2;
+
+const PROPERTY_NAME_INDEX = 0;
+const PROPERTY_VALUE_INDEX = 3;
+
+/**
+ * Wraps a jCal component, adding convenience methods to add, remove and update subcomponents and
+ * properties.
+ *
+ * @memberof ICAL
+ */
+class Component {
+ /**
+ * Create an {@link ICAL.Component} by parsing the passed iCalendar string.
+ *
+ * @param {String} str The iCalendar string to parse
+ */
+ static fromString(str) {
+ return new Component(ICALParse.component(str));
+ }
+
+ /**
+ * Creates a new Component instance.
+ *
+ * @param {Array|String} jCal Raw jCal component data OR name of new
+ * component
+ * @param {Component=} parent Parent component to associate
+ */
+ constructor(jCal, parent) {
+ if (typeof(jCal) === 'string') {
+ // jCal spec (name, properties, components)
+ jCal = [jCal, [], []];
+ }
+
+ // mostly for legacy reasons.
+ this.jCal = jCal;
+
+ this.parent = parent || null;
+
+ if (!this.parent && this.name === 'vcalendar') {
+ this._timezoneCache = new Map();
+ }
+ }
+
+ /**
+ * Hydrated properties are inserted into the _properties array at the same
+ * position as in the jCal array, so it is possible that the array contains
+ * undefined values for unhydrdated properties. To avoid iterating the
+ * array when checking if all properties have been hydrated, we save the
+ * count here.
+ *
+ * @type {Number}
+ * @private
+ */
+ _hydratedPropertyCount = 0;
+
+ /**
+ * The same count as for _hydratedPropertyCount, but for subcomponents
+ *
+ * @type {Number}
+ * @private
+ */
+ _hydratedComponentCount = 0;
+
+ /**
+ * A cache of hydrated time zone objects which may be used by consumers, keyed
+ * by time zone ID.
+ *
+ * @type {Map}
+ * @private
+ */
+ _timezoneCache = null;
+
+ /**
+ * @private
+ */
+ _components = null;
+
+ /**
+ * @private
+ */
+ _properties = null;
+
+ /**
+ * The name of this component
+ *
+ * @type {String}
+ */
+ get name() {
+ return this.jCal[NAME_INDEX];
+ }
+
+ /**
+ * The design set for this component, e.g. icalendar vs vcard
+ *
+ * @type {designSet}
+ * @private
+ */
+ get _designSet() {
+ let parentDesign = this.parent && this.parent._designSet;
+ if (!parentDesign && this.name == "vcard") {
+ // We can't decide on vcard3 vs vcard4 just based on the component name, the version number is
+ // in the version property. We also can't use hydrated properties here because it would lead
+ // to recursion, but the spec says that the version property needs to be the very first one.
+ let versionProp = this.jCal[PROPERTY_INDEX]?.[0];
+
+ if (versionProp && versionProp[PROPERTY_NAME_INDEX] == "version" && versionProp[PROPERTY_VALUE_INDEX] == "3.0") {
+ return design.getDesignSet("vcard3");
+ }
+ }
+
+ return parentDesign || design.getDesignSet(this.name);
+ }
+
+ /**
+ * @private
+ */
+ _hydrateComponent(index) {
+ if (!this._components) {
+ this._components = [];
+ this._hydratedComponentCount = 0;
+ }
+
+ if (this._components[index]) {
+ return this._components[index];
+ }
+
+ let comp = new Component(
+ this.jCal[COMPONENT_INDEX][index],
+ this
+ );
+
+ this._hydratedComponentCount++;
+ return (this._components[index] = comp);
+ }
+
+ /**
+ * @private
+ */
+ _hydrateProperty(index) {
+ if (!this._properties) {
+ this._properties = [];
+ this._hydratedPropertyCount = 0;
+ }
+
+ if (this._properties[index]) {
+ return this._properties[index];
+ }
+
+ let prop = new Property(
+ this.jCal[PROPERTY_INDEX][index],
+ this
+ );
+
+ this._hydratedPropertyCount++;
+ return (this._properties[index] = prop);
+ }
+
+ /**
+ * Finds first sub component, optionally filtered by name.
+ *
+ * @param {String=} name Optional name to filter by
+ * @return {?Component} The found subcomponent
+ */
+ getFirstSubcomponent(name) {
+ if (name) {
+ let i = 0;
+ let comps = this.jCal[COMPONENT_INDEX];
+ let len = comps.length;
+
+ for (; i < len; i++) {
+ if (comps[i][NAME_INDEX] === name) {
+ let result = this._hydrateComponent(i);
+ return result;
+ }
+ }
+ } else {
+ if (this.jCal[COMPONENT_INDEX].length) {
+ return this._hydrateComponent(0);
+ }
+ }
+
+ // ensure we return a value (strict mode)
+ return null;
+ }
+
+ /**
+ * Finds all sub components, optionally filtering by name.
+ *
+ * @param {String=} name Optional name to filter by
+ * @return {Component[]} The found sub components
+ */
+ getAllSubcomponents(name) {
+ let jCalLen = this.jCal[COMPONENT_INDEX].length;
+ let i = 0;
+
+ if (name) {
+ let comps = this.jCal[COMPONENT_INDEX];
+ let result = [];
+
+ for (; i < jCalLen; i++) {
+ if (name === comps[i][NAME_INDEX]) {
+ result.push(
+ this._hydrateComponent(i)
+ );
+ }
+ }
+ return result;
+ } else {
+ if (!this._components ||
+ (this._hydratedComponentCount !== jCalLen)) {
+ for (; i < jCalLen; i++) {
+ this._hydrateComponent(i);
+ }
+ }
+
+ return this._components || [];
+ }
+ }
+
+ /**
+ * Returns true when a named property exists.
+ *
+ * @param {String} name The property name
+ * @return {Boolean} True, when property is found
+ */
+ hasProperty(name) {
+ let props = this.jCal[PROPERTY_INDEX];
+ let len = props.length;
+
+ let i = 0;
+ for (; i < len; i++) {
+ // 0 is property name
+ if (props[i][NAME_INDEX] === name) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Finds the first property, optionally with the given name.
+ *
+ * @param {String=} name Lowercase property name
+ * @return {?Property} The found property
+ */
+ getFirstProperty(name) {
+ if (name) {
+ let i = 0;
+ let props = this.jCal[PROPERTY_INDEX];
+ let len = props.length;
+
+ for (; i < len; i++) {
+ if (props[i][NAME_INDEX] === name) {
+ let result = this._hydrateProperty(i);
+ return result;
+ }
+ }
+ } else {
+ if (this.jCal[PROPERTY_INDEX].length) {
+ return this._hydrateProperty(0);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns first property's value, if available.
+ *
+ * @param {String=} name Lowercase property name
+ * @return {Binary | Duration | Period |
+ * Recur | Time | UtcOffset | Geo | string | null} The found property value.
+ */
+ getFirstPropertyValue(name) {
+ let prop = this.getFirstProperty(name);
+ if (prop) {
+ return prop.getFirstValue();
+ }
+
+ return null;
+ }
+
+ /**
+ * Get all properties in the component, optionally filtered by name.
+ *
+ * @param {String=} name Lowercase property name
+ * @return {Property[]} List of properties
+ */
+ getAllProperties(name) {
+ let jCalLen = this.jCal[PROPERTY_INDEX].length;
+ let i = 0;
+
+ if (name) {
+ let props = this.jCal[PROPERTY_INDEX];
+ let result = [];
+
+ for (; i < jCalLen; i++) {
+ if (name === props[i][NAME_INDEX]) {
+ result.push(
+ this._hydrateProperty(i)
+ );
+ }
+ }
+ return result;
+ } else {
+ if (!this._properties ||
+ (this._hydratedPropertyCount !== jCalLen)) {
+ for (; i < jCalLen; i++) {
+ this._hydrateProperty(i);
+ }
+ }
+
+ return this._properties || [];
+ }
+ }
+
+ /**
+ * @private
+ */
+ _removeObjectByIndex(jCalIndex, cache, index) {
+ cache = cache || [];
+ // remove cached version
+ if (cache[index]) {
+ let obj = cache[index];
+ if ("parent" in obj) {
+ obj.parent = null;
+ }
+ }
+
+ cache.splice(index, 1);
+
+ // remove it from the jCal
+ this.jCal[jCalIndex].splice(index, 1);
+ }
+
+ /**
+ * @private
+ */
+ _removeObject(jCalIndex, cache, nameOrObject) {
+ let i = 0;
+ let objects = this.jCal[jCalIndex];
+ let len = objects.length;
+ let cached = this[cache];
+
+ if (typeof(nameOrObject) === 'string') {
+ for (; i < len; i++) {
+ if (objects[i][NAME_INDEX] === nameOrObject) {
+ this._removeObjectByIndex(jCalIndex, cached, i);
+ return true;
+ }
+ }
+ } else if (cached) {
+ for (; i < len; i++) {
+ if (cached[i] && cached[i] === nameOrObject) {
+ this._removeObjectByIndex(jCalIndex, cached, i);
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @private
+ */
+ _removeAllObjects(jCalIndex, cache, name) {
+ let cached = this[cache];
+
+ // Unfortunately we have to run through all children to reset their
+ // parent property.
+ let objects = this.jCal[jCalIndex];
+ let i = objects.length - 1;
+
+ // descending search required because splice
+ // is used and will effect the indices.
+ for (; i >= 0; i--) {
+ if (!name || objects[i][NAME_INDEX] === name) {
+ this._removeObjectByIndex(jCalIndex, cached, i);
+ }
+ }
+ }
+
+ /**
+ * Adds a single sub component.
+ *
+ * @param {Component} component The component to add
+ * @return {Component} The passed in component
+ */
+ addSubcomponent(component) {
+ if (!this._components) {
+ this._components = [];
+ this._hydratedComponentCount = 0;
+ }
+
+ if (component.parent) {
+ component.parent.removeSubcomponent(component);
+ }
+
+ let idx = this.jCal[COMPONENT_INDEX].push(component.jCal);
+ this._components[idx - 1] = component;
+ this._hydratedComponentCount++;
+ component.parent = this;
+ return component;
+ }
+
+ /**
+ * Removes a single component by name or the instance of a specific
+ * component.
+ *
+ * @param {Component|String} nameOrComp Name of component, or component
+ * @return {Boolean} True when comp is removed
+ */
+ removeSubcomponent(nameOrComp) {
+ let removed = this._removeObject(COMPONENT_INDEX, '_components', nameOrComp);
+ if (removed) {
+ this._hydratedComponentCount--;
+ }
+ return removed;
+ }
+
+ /**
+ * Removes all components or (if given) all components by a particular
+ * name.
+ *
+ * @param {String=} name Lowercase component name
+ */
+ removeAllSubcomponents(name) {
+ let removed = this._removeAllObjects(COMPONENT_INDEX, '_components', name);
+ this._hydratedComponentCount = 0;
+ return removed;
+ }
+
+ /**
+ * Adds an {@link ICAL.Property} to the component.
+ *
+ * @param {Property} property The property to add
+ * @return {Property} The passed in property
+ */
+ addProperty(property) {
+ if (!(property instanceof Property)) {
+ throw new TypeError('must be instance of ICAL.Property');
+ }
+
+ if (!this._properties) {
+ this._properties = [];
+ this._hydratedPropertyCount = 0;
+ }
+
+ if (property.parent) {
+ property.parent.removeProperty(property);
+ }
+
+ let idx = this.jCal[PROPERTY_INDEX].push(property.jCal);
+ this._properties[idx - 1] = property;
+ this._hydratedPropertyCount++;
+ property.parent = this;
+ return property;
+ }
+
+ /**
+ * Helper method to add a property with a value to the component.
+ *
+ * @param {String} name Property name to add
+ * @param {String|Number|Object} value Property value
+ * @return {Property} The created property
+ */
+ addPropertyWithValue(name, value) {
+ let prop = new Property(name);
+ prop.setValue(value);
+
+ this.addProperty(prop);
+
+ return prop;
+ }
+
+ /**
+ * Helper method that will update or create a property of the given name
+ * and sets its value. If multiple properties with the given name exist,
+ * only the first is updated.
+ *
+ * @param {String} name Property name to update
+ * @param {String|Number|Object} value Property value
+ * @return {Property} The created property
+ */
+ updatePropertyWithValue(name, value) {
+ let prop = this.getFirstProperty(name);
+
+ if (prop) {
+ prop.setValue(value);
+ } else {
+ prop = this.addPropertyWithValue(name, value);
+ }
+
+ return prop;
+ }
+
+ /**
+ * Removes a single property by name or the instance of the specific
+ * property.
+ *
+ * @param {String|Property} nameOrProp Property name or instance to remove
+ * @return {Boolean} True, when deleted
+ */
+ removeProperty(nameOrProp) {
+ let removed = this._removeObject(PROPERTY_INDEX, '_properties', nameOrProp);
+ if (removed) {
+ this._hydratedPropertyCount--;
+ }
+ return removed;
+ }
+
+ /**
+ * Removes all properties associated with this component, optionally
+ * filtered by name.
+ *
+ * @param {String=} name Lowercase property name
+ * @return {Boolean} True, when deleted
+ */
+ removeAllProperties(name) {
+ let removed = this._removeAllObjects(PROPERTY_INDEX, '_properties', name);
+ this._hydratedPropertyCount = 0;
+ return removed;
+ }
+
+ /**
+ * Returns the Object representation of this component. The returned object
+ * is a live jCal object and should be cloned if modified.
+ * @return {Object}
+ */
+ toJSON() {
+ return this.jCal;
+ }
+
+ /**
+ * The string representation of this component.
+ * @return {String}
+ */
+ toString() {
+ return stringify.component(
+ this.jCal, this._designSet
+ );
+ }
+
+ /**
+ * Retrieve a time zone definition from the component tree, if any is present.
+ * If the tree contains no time zone definitions or the TZID cannot be
+ * matched, returns null.
+ *
+ * @param {String} tzid The ID of the time zone to retrieve
+ * @return {Timezone} The time zone corresponding to the ID, or null
+ */
+ getTimeZoneByID(tzid) {
+ // VTIMEZONE components can only appear as a child of the VCALENDAR
+ // component; walk the tree if we're not the root.
+ if (this.parent) {
+ return this.parent.getTimeZoneByID(tzid);
+ }
+
+ // If there is no time zone cache, we are probably parsing an incomplete
+ // file and will have no time zone definitions.
+ if (!this._timezoneCache) {
+ return null;
+ }
+
+ if (this._timezoneCache.has(tzid)) {
+ return this._timezoneCache.get(tzid);
+ }
+
+ // If the time zone is not already cached, hydrate it from the
+ // subcomponents.
+ const zones = this.getAllSubcomponents('vtimezone');
+ for (const zone of zones) {
+ if (zone.getFirstProperty('tzid').getFirstValue() === tzid) {
+ const hydratedZone = new Timezone({
+ component: zone,
+ tzid: tzid,
+ });
+
+ this._timezoneCache.set(tzid, hydratedZone);
+
+ return hydratedZone;
+ }
+ }
+
+ // Per the standard, we should always have a time zone defined in a file
+ // for any referenced TZID, but don't blow up if the file is invalid.
+ return null;
+ }
+}
+export default Component;
diff --git a/cron/scripts/node_modules/ical.js/lib/ical/component_parser.js b/cron/scripts/node_modules/ical.js/lib/ical/component_parser.js
new file mode 100644
index 0000000..71f1575
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/lib/ical/component_parser.js
@@ -0,0 +1,157 @@
+/* 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 ICALParse from "./parse.js";
+import Component from "./component.js";
+import Event from "./event.js";
+import Timezone from "./timezone.js";
+
+/**
+ * The ComponentParser is used to process a String or jCal Object,
+ * firing callbacks for various found components, as well as completion.
+ *
+ * @example
+ * var options = {
+ * // when false no events will be emitted for type
+ * parseEvent: true,
+ * parseTimezone: true
+ * };
+ *
+ * var parser = new ICAL.ComponentParser(options);
+ *
+ * parser.onevent(eventComponent) {
+ * //...
+ * }
+ *
+ * // ontimezone, etc...
+ *
+ * parser.oncomplete = function() {
+ *
+ * };
+ *
+ * parser.process(stringOrComponent);
+ *
+ * @memberof ICAL
+ */
+class ComponentParser {
+ /**
+ * Creates a new ICAL.ComponentParser instance.
+ *
+ * @param {Object=} options Component parser options
+ * @param {Boolean} options.parseEvent Whether events should be parsed
+ * @param {Boolean} options.parseTimezeone Whether timezones should be parsed
+ */
+ constructor(options) {
+ if (typeof(options) === 'undefined') {
+ options = {};
+ }
+
+ for (let [key, value] of Object.entries(options)) {
+ this[key] = value;
+ }
+ }
+
+ /**
+ * When true, parse events
+ *
+ * @type {Boolean}
+ */
+ parseEvent = true;
+
+ /**
+ * When true, parse timezones
+ *
+ * @type {Boolean}
+ */
+ parseTimezone = true;
+
+
+ /* SAX like events here for reference */
+
+ /**
+ * Fired when parsing is complete
+ * @callback
+ */
+ oncomplete = /* c8 ignore next */ function() {};
+
+ /**
+ * Fired if an error occurs during parsing.
+ *
+ * @callback
+ * @param {Error} err details of error
+ */
+ onerror = /* c8 ignore next */ function(err) {};
+
+ /**
+ * Fired when a top level component (VTIMEZONE) is found
+ *
+ * @callback
+ * @param {Timezone} component Timezone object
+ */
+ ontimezone = /* c8 ignore next */ function(component) {};
+
+ /**
+ * Fired when a top level component (VEVENT) is found.
+ *
+ * @callback
+ * @param {Event} component Top level component
+ */
+ onevent = /* c8 ignore next */ function(component) {};
+
+ /**
+ * Process a string or parse ical object. This function itself will return
+ * nothing but will start the parsing process.
+ *
+ * Events must be registered prior to calling this method.
+ *
+ * @param {Component|String|Object} ical The component to process,
+ * either in its final form, as a jCal Object, or string representation
+ */
+ process(ical) {
+ //TODO: this is sync now in the future we will have a incremental parser.
+ if (typeof(ical) === 'string') {
+ ical = ICALParse(ical);
+ }
+
+ if (!(ical instanceof Component)) {
+ ical = new Component(ical);
+ }
+
+ let components = ical.getAllSubcomponents();
+ let i = 0;
+ let len = components.length;
+ let component;
+
+ for (; i < len; i++) {
+ component = components[i];
+
+ switch (component.name) {
+ case 'vtimezone':
+ if (this.parseTimezone) {
+ let tzid = component.getFirstPropertyValue('tzid');
+ if (tzid) {
+ this.ontimezone(new Timezone({
+ tzid: tzid,
+ component: component
+ }));
+ }
+ }
+ break;
+ case 'vevent':
+ if (this.parseEvent) {
+ this.onevent(new Event(component));
+ }
+ break;
+ default:
+ continue;
+ }
+ }
+
+ //XXX: ideally we should do a "nextTick" here
+ // so in all cases this is actually async.
+ this.oncomplete();
+ }
+}
+export default ComponentParser;
diff --git a/cron/scripts/node_modules/ical.js/lib/ical/design.js b/cron/scripts/node_modules/ical.js/lib/ical/design.js
new file mode 100644
index 0000000..b1698a1
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/lib/ical/design.js
@@ -0,0 +1,1029 @@
+/* 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;
diff --git a/cron/scripts/node_modules/ical.js/lib/ical/duration.js b/cron/scripts/node_modules/ical.js/lib/ical/duration.js
new file mode 100644
index 0000000..957a335
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/lib/ical/duration.js
@@ -0,0 +1,374 @@
+/* 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, trunc } from "./helpers.js";
+
+const DURATION_LETTERS = /([PDWHMTS]{1,1})/;
+const DATA_PROPS_TO_COPY = ["weeks", "days", "hours", "minutes", "seconds", "isNegative"];
+
+/**
+ * This class represents the "duration" value type, with various calculation
+ * and manipulation methods.
+ *
+ * @memberof ICAL
+ */
+class Duration {
+ /**
+ * Returns a new ICAL.Duration instance from the passed seconds value.
+ *
+ * @param {Number} aSeconds The seconds to create the instance from
+ * @return {Duration} The newly created duration instance
+ */
+ static fromSeconds(aSeconds) {
+ return (new Duration()).fromSeconds(aSeconds);
+ }
+
+ /**
+ * Checks if the given string is an iCalendar duration value.
+ *
+ * @param {String} value The raw ical value
+ * @return {Boolean} True, if the given value is of the
+ * duration ical type
+ */
+ static isValueString(string) {
+ return (string[0] === 'P' || string[1] === 'P');
+ }
+
+ /**
+ * Creates a new {@link ICAL.Duration} instance from the passed string.
+ *
+ * @param {String} aStr The string to parse
+ * @return {Duration} The created duration instance
+ */
+ static fromString(aStr) {
+ let pos = 0;
+ let dict = Object.create(null);
+ let chunks = 0;
+
+ while ((pos = aStr.search(DURATION_LETTERS)) !== -1) {
+ let type = aStr[pos];
+ let numeric = aStr.slice(0, Math.max(0, pos));
+ aStr = aStr.slice(pos + 1);
+
+ chunks += parseDurationChunk(type, numeric, dict);
+ }
+
+ if (chunks < 2) {
+ // There must be at least a chunk with "P" and some unit chunk
+ throw new Error(
+ 'invalid duration value: Not enough duration components in "' + aStr + '"'
+ );
+ }
+
+ return new Duration(dict);
+ }
+
+ /**
+ * Creates a new ICAL.Duration instance from the given data object.
+ *
+ * @param {Object} aData An object with members of the duration
+ * @param {Number=} aData.weeks Duration in weeks
+ * @param {Number=} aData.days Duration in days
+ * @param {Number=} aData.hours Duration in hours
+ * @param {Number=} aData.minutes Duration in minutes
+ * @param {Number=} aData.seconds Duration in seconds
+ * @param {Boolean=} aData.isNegative If true, the duration is negative
+ * @return {Duration} The createad duration instance
+ */
+ static fromData(aData) {
+ return new Duration(aData);
+ }
+
+ /**
+ * Creates a new ICAL.Duration instance.
+ *
+ * @param {Object} data An object with members of the duration
+ * @param {Number=} data.weeks Duration in weeks
+ * @param {Number=} data.days Duration in days
+ * @param {Number=} data.hours Duration in hours
+ * @param {Number=} data.minutes Duration in minutes
+ * @param {Number=} data.seconds Duration in seconds
+ * @param {Boolean=} data.isNegative If true, the duration is negative
+ */
+ constructor(data) {
+ this.wrappedJSObject = this;
+ this.fromData(data);
+ }
+
+ /**
+ * The weeks in this duration
+ * @type {Number}
+ * @default 0
+ */
+ weeks = 0;
+
+ /**
+ * The days in this duration
+ * @type {Number}
+ * @default 0
+ */
+ days = 0;
+
+ /**
+ * The days in this duration
+ * @type {Number}
+ * @default 0
+ */
+ hours = 0;
+
+ /**
+ * The minutes in this duration
+ * @type {Number}
+ * @default 0
+ */
+ minutes = 0;
+
+ /**
+ * The seconds in this duration
+ * @type {Number}
+ * @default 0
+ */
+ seconds = 0;
+
+ /**
+ * The seconds in this duration
+ * @type {Boolean}
+ * @default false
+ */
+ isNegative = false;
+
+ /**
+ * The class identifier.
+ * @constant
+ * @type {String}
+ * @default "icalduration"
+ */
+ icalclass = "icalduration";
+
+ /**
+ * The type name, to be used in the jCal object.
+ * @constant
+ * @type {String}
+ * @default "duration"
+ */
+ icaltype = "duration";
+
+ /**
+ * Returns a clone of the duration object.
+ *
+ * @return {Duration} The cloned object
+ */
+ clone() {
+ return Duration.fromData(this);
+ }
+
+ /**
+ * The duration value expressed as a number of seconds.
+ *
+ * @return {Number} The duration value in seconds
+ */
+ toSeconds() {
+ let seconds = this.seconds + 60 * this.minutes + 3600 * this.hours +
+ 86400 * this.days + 7 * 86400 * this.weeks;
+ return (this.isNegative ? -seconds : seconds);
+ }
+
+ /**
+ * Reads the passed seconds value into this duration object. Afterwards,
+ * members like {@link ICAL.Duration#days days} and {@link ICAL.Duration#weeks weeks} will be set up
+ * accordingly.
+ *
+ * @param {Number} aSeconds The duration value in seconds
+ * @return {Duration} Returns this instance
+ */
+ fromSeconds(aSeconds) {
+ let secs = Math.abs(aSeconds);
+
+ this.isNegative = (aSeconds < 0);
+ this.days = trunc(secs / 86400);
+
+ // If we have a flat number of weeks, use them.
+ if (this.days % 7 == 0) {
+ this.weeks = this.days / 7;
+ this.days = 0;
+ } else {
+ this.weeks = 0;
+ }
+
+ secs -= (this.days + 7 * this.weeks) * 86400;
+
+ this.hours = trunc(secs / 3600);
+ secs -= this.hours * 3600;
+
+ this.minutes = trunc(secs / 60);
+ secs -= this.minutes * 60;
+
+ this.seconds = secs;
+ return this;
+ }
+
+ /**
+ * Sets up the current instance using members from the passed data object.
+ *
+ * @param {Object} aData An object with members of the duration
+ * @param {Number=} aData.weeks Duration in weeks
+ * @param {Number=} aData.days Duration in days
+ * @param {Number=} aData.hours Duration in hours
+ * @param {Number=} aData.minutes Duration in minutes
+ * @param {Number=} aData.seconds Duration in seconds
+ * @param {Boolean=} aData.isNegative If true, the duration is negative
+ */
+ fromData(aData) {
+ for (let prop of DATA_PROPS_TO_COPY) {
+ if (aData && prop in aData) {
+ this[prop] = aData[prop];
+ } else {
+ this[prop] = 0;
+ }
+ }
+ }
+
+ /**
+ * Resets the duration instance to the default values, i.e. PT0S
+ */
+ reset() {
+ this.isNegative = false;
+ this.weeks = 0;
+ this.days = 0;
+ this.hours = 0;
+ this.minutes = 0;
+ this.seconds = 0;
+ }
+
+ /**
+ * Compares the duration instance with another one.
+ *
+ * @param {Duration} aOther The instance to compare with
+ * @return {Number} -1, 0 or 1 for less/equal/greater
+ */
+ compare(aOther) {
+ let thisSeconds = this.toSeconds();
+ let otherSeconds = aOther.toSeconds();
+ return (thisSeconds > otherSeconds) - (thisSeconds < otherSeconds);
+ }
+
+ /**
+ * Normalizes the duration instance. For example, a duration with a value
+ * of 61 seconds will be normalized to 1 minute and 1 second.
+ */
+ normalize() {
+ this.fromSeconds(this.toSeconds());
+ }
+
+ /**
+ * The string representation of this duration.
+ * @return {String}
+ */
+ toString() {
+ if (this.toSeconds() == 0) {
+ return "PT0S";
+ } else {
+ let str = "";
+ if (this.isNegative) str += "-";
+ str += "P";
+ let hasWeeks = false;
+ if (this.weeks) {
+ if (this.days || this.hours || this.minutes || this.seconds) {
+ str += (this.weeks * 7 + this.days) + "D";
+ } else {
+ str += (this.weeks + "W");
+ hasWeeks = true;
+ }
+ } else if (this.days) {
+ str += (this.days + "D");
+ }
+
+ if (!hasWeeks) {
+ if (this.hours || this.minutes || this.seconds) {
+ str += "T";
+ if (this.hours) {
+ str += this.hours + "H";
+ }
+
+ if (this.minutes) {
+ str += this.minutes + "M";
+ }
+
+ if (this.seconds) {
+ str += this.seconds + "S";
+ }
+ }
+ }
+
+ return str;
+ }
+ }
+
+ /**
+ * The iCalendar string representation of this duration.
+ * @return {String}
+ */
+ toICALString() {
+ return this.toString();
+ }
+}
+export default Duration;
+
+/**
+ * Internal helper function to handle a chunk of a duration.
+ *
+ * @private
+ * @param {String} letter type of duration chunk
+ * @param {String} number numeric value or -/+
+ * @param {Object} dict target to assign values to
+ */
+function parseDurationChunk(letter, number, object) {
+ let type;
+ switch (letter) {
+ case 'P':
+ if (number && number === '-') {
+ object.isNegative = true;
+ } else {
+ object.isNegative = false;
+ }
+ // period
+ break;
+ case 'D':
+ type = 'days';
+ break;
+ case 'W':
+ type = 'weeks';
+ break;
+ case 'H':
+ type = 'hours';
+ break;
+ case 'M':
+ type = 'minutes';
+ break;
+ case 'S':
+ type = 'seconds';
+ break;
+ default:
+ // Not a valid chunk
+ return 0;
+ }
+
+ if (type) {
+ if (!number && number !== 0) {
+ throw new Error(
+ 'invalid duration value: Missing number before "' + letter + '"'
+ );
+ }
+ let num = parseInt(number, 10);
+ if (isStrictlyNaN(num)) {
+ throw new Error(
+ 'invalid duration value: Invalid number "' + number + '" before "' + letter + '"'
+ );
+ }
+ object[type] = num;
+ }
+
+ return 1;
+}
diff --git a/cron/scripts/node_modules/ical.js/lib/ical/event.js b/cron/scripts/node_modules/ical.js/lib/ical/event.js
new file mode 100644
index 0000000..f8268c0
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/lib/ical/event.js
@@ -0,0 +1,569 @@
+/* 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 { binsearchInsert } from "./helpers.js";
+import Component from "./component.js";
+import Property from "./property.js";
+import Timezone from "./timezone.js";
+// needed for typescript type resolution
+// eslint-disable-next-line no-unused-vars
+import Time from "./time.js";
+// eslint-disable-next-line no-unused-vars
+import Duration from "./duration.js";
+import RecurExpansion from "./recur_expansion.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").frequencyValues} frequencyValues
+ * Imports the 'frequencyValues' type from the "types.js" module
+ * @typedef {import("./types.js").occurrenceDetails} occurrenceDetails
+ * Imports the 'occurrenceDetails' type from the "types.js" module
+ */
+
+/**
+ * ICAL.js is organized into multiple layers. The bottom layer is a raw jCal
+ * object, followed by the component/property layer. The highest level is the
+ * event representation, which this class is part of. See the
+ * {@tutorial layers} guide for more details.
+ *
+ * @memberof ICAL
+ */
+class Event {
+ /**
+ * Creates a new ICAL.Event instance.
+ *
+ * @param {Component=} component The ICAL.Component to base this event on
+ * @param {Object} [options] Options for this event
+ * @param {Boolean=} options.strictExceptions When true, will verify exceptions are related by
+ * their UUID
+ * @param {Array=} options.exceptions
+ * Exceptions to this event, either as components or events. If not
+ * specified exceptions will automatically be set in relation of
+ * component's parent
+ */
+ constructor(component, options) {
+ if (!(component instanceof Component)) {
+ options = component;
+ component = null;
+ }
+
+ if (component) {
+ this.component = component;
+ } else {
+ this.component = new Component('vevent');
+ }
+
+ this._rangeExceptionCache = Object.create(null);
+ this.exceptions = Object.create(null);
+ this.rangeExceptions = [];
+
+ if (options && options.strictExceptions) {
+ this.strictExceptions = options.strictExceptions;
+ }
+
+ if (options && options.exceptions) {
+ options.exceptions.forEach(this.relateException, this);
+ } else if (this.component.parent && !this.isRecurrenceException()) {
+ this.component.parent.getAllSubcomponents('vevent').forEach(function(event) {
+ if (event.hasProperty('recurrence-id')) {
+ this.relateException(event);
+ }
+ }, this);
+ }
+ }
+
+
+ static THISANDFUTURE = 'THISANDFUTURE';
+
+ /**
+ * List of related event exceptions.
+ *
+ * @type {Event[]}
+ */
+ exceptions = null;
+
+ /**
+ * When true, will verify exceptions are related by their UUID.
+ *
+ * @type {Boolean}
+ */
+ strictExceptions = false;
+
+ /**
+ * Relates a given event exception to this object. If the given component
+ * does not share the UID of this event it cannot be related and will throw
+ * an exception.
+ *
+ * If this component is an exception it cannot have other exceptions
+ * related to it.
+ *
+ * @param {Component|Event} obj Component or event
+ */
+ relateException(obj) {
+ if (this.isRecurrenceException()) {
+ throw new Error('cannot relate exception to exceptions');
+ }
+
+ if (obj instanceof Component) {
+ obj = new Event(obj);
+ }
+
+ if (this.strictExceptions && obj.uid !== this.uid) {
+ throw new Error('attempted to relate unrelated exception');
+ }
+
+ let id = obj.recurrenceId.toString();
+
+ // we don't sort or manage exceptions directly
+ // here the recurrence expander handles that.
+ this.exceptions[id] = obj;
+
+ // index RANGE=THISANDFUTURE exceptions so we can
+ // look them up later in getOccurrenceDetails.
+ if (obj.modifiesFuture()) {
+ let item = [
+ obj.recurrenceId.toUnixTime(), id
+ ];
+
+ // we keep them sorted so we can find the nearest
+ // value later on...
+ let idx = binsearchInsert(
+ this.rangeExceptions,
+ item,
+ compareRangeException
+ );
+
+ this.rangeExceptions.splice(idx, 0, item);
+ }
+ }
+
+ /**
+ * Checks if this record is an exception and has the RANGE=THISANDFUTURE
+ * value.
+ *
+ * @return {Boolean} True, when exception is within range
+ */
+ modifiesFuture() {
+ if (!this.component.hasProperty('recurrence-id')) {
+ return false;
+ }
+
+ let range = this.component.getFirstProperty('recurrence-id').getParameter('range');
+ return range === Event.THISANDFUTURE;
+ }
+
+ /**
+ * Finds the range exception nearest to the given date.
+ *
+ * @param {Time} time usually an occurrence time of an event
+ * @return {?Event} the related event/exception or null
+ */
+ findRangeException(time) {
+ if (!this.rangeExceptions.length) {
+ return null;
+ }
+
+ let utc = time.toUnixTime();
+ let idx = binsearchInsert(
+ this.rangeExceptions,
+ [utc],
+ compareRangeException
+ );
+
+ idx -= 1;
+
+ // occurs before
+ if (idx < 0) {
+ return null;
+ }
+
+ let rangeItem = this.rangeExceptions[idx];
+
+ /* c8 ignore next 4 */
+ if (utc < rangeItem[0]) {
+ // sanity check only
+ return null;
+ }
+
+ return rangeItem[1];
+ }
+
+ /**
+ * Returns the occurrence details based on its start time. If the
+ * occurrence has an exception will return the details for that exception.
+ *
+ * NOTE: this method is intend to be used in conjunction
+ * with the {@link ICAL.Event#iterator iterator} method.
+ *
+ * @param {Time} occurrence time occurrence
+ * @return {occurrenceDetails} Information about the occurrence
+ */
+ getOccurrenceDetails(occurrence) {
+ let id = occurrence.toString();
+ let utcId = occurrence.convertToZone(Timezone.utcTimezone).toString();
+ let item;
+ let result = {
+ //XXX: Clone?
+ recurrenceId: occurrence
+ };
+
+ if (id in this.exceptions) {
+ item = result.item = this.exceptions[id];
+ result.startDate = item.startDate;
+ result.endDate = item.endDate;
+ result.item = item;
+ } else if (utcId in this.exceptions) {
+ item = this.exceptions[utcId];
+ result.startDate = item.startDate;
+ result.endDate = item.endDate;
+ result.item = item;
+ } else {
+ // range exceptions (RANGE=THISANDFUTURE) have a
+ // lower priority then direct exceptions but
+ // must be accounted for first. Their item is
+ // always the first exception with the range prop.
+ let rangeExceptionId = this.findRangeException(
+ occurrence
+ );
+ let end;
+
+ if (rangeExceptionId) {
+ let exception = this.exceptions[rangeExceptionId];
+
+ // range exception must modify standard time
+ // by the difference (if any) in start/end times.
+ result.item = exception;
+
+ let startDiff = this._rangeExceptionCache[rangeExceptionId];
+
+ if (!startDiff) {
+ let original = exception.recurrenceId.clone();
+ let newStart = exception.startDate.clone();
+
+ // zones must be same otherwise subtract may be incorrect.
+ original.zone = newStart.zone;
+ startDiff = newStart.subtractDate(original);
+
+ this._rangeExceptionCache[rangeExceptionId] = startDiff;
+ }
+
+ let start = occurrence.clone();
+ start.zone = exception.startDate.zone;
+ start.addDuration(startDiff);
+
+ end = start.clone();
+ end.addDuration(exception.duration);
+
+ result.startDate = start;
+ result.endDate = end;
+ } else {
+ // no range exception standard expansion
+ end = occurrence.clone();
+ end.addDuration(this.duration);
+
+ result.endDate = end;
+ result.startDate = occurrence;
+ result.item = this;
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Builds a recur expansion instance for a specific point in time (defaults
+ * to startDate).
+ *
+ * @param {Time=} startTime Starting point for expansion
+ * @return {RecurExpansion} Expansion object
+ */
+ iterator(startTime) {
+ return new RecurExpansion({
+ component: this.component,
+ dtstart: startTime || this.startDate
+ });
+ }
+
+ /**
+ * Checks if the event is recurring
+ *
+ * @return {Boolean} True, if event is recurring
+ */
+ isRecurring() {
+ let comp = this.component;
+ return comp.hasProperty('rrule') || comp.hasProperty('rdate');
+ }
+
+ /**
+ * Checks if the event describes a recurrence exception. See
+ * {@tutorial terminology} for details.
+ *
+ * @return {Boolean} True, if the event describes a recurrence exception
+ */
+ isRecurrenceException() {
+ return this.component.hasProperty('recurrence-id');
+ }
+
+ /**
+ * Returns the types of recurrences this event may have.
+ *
+ * Returned as an object with the following possible keys:
+ *
+ * - YEARLY
+ * - MONTHLY
+ * - WEEKLY
+ * - DAILY
+ * - MINUTELY
+ * - SECONDLY
+ *
+ * @return {Object.}
+ * Object of recurrence flags
+ */
+ getRecurrenceTypes() {
+ let rules = this.component.getAllProperties('rrule');
+ let i = 0;
+ let len = rules.length;
+ let result = Object.create(null);
+
+ for (; i < len; i++) {
+ let value = rules[i].getFirstValue();
+ result[value.freq] = true;
+ }
+
+ return result;
+ }
+
+ /**
+ * The uid of this event
+ * @type {String}
+ */
+ get uid() {
+ return this._firstProp('uid');
+ }
+
+ set uid(value) {
+ this._setProp('uid', value);
+ }
+
+ /**
+ * The start date
+ * @type {Time}
+ */
+ get startDate() {
+ return this._firstProp('dtstart');
+ }
+
+ set startDate(value) {
+ this._setTime('dtstart', value);
+ }
+
+ /**
+ * The end date. This can be the result directly from the property, or the
+ * end date calculated from start date and duration. Setting the property
+ * will remove any duration properties.
+ * @type {Time}
+ */
+ get endDate() {
+ let endDate = this._firstProp('dtend');
+ if (!endDate) {
+ let duration = this._firstProp('duration');
+ endDate = this.startDate.clone();
+ if (duration) {
+ endDate.addDuration(duration);
+ } else if (endDate.isDate) {
+ endDate.day += 1;
+ }
+ }
+ return endDate;
+ }
+
+ set endDate(value) {
+ if (this.component.hasProperty('duration')) {
+ this.component.removeProperty('duration');
+ }
+ this._setTime('dtend', value);
+ }
+
+ /**
+ * The duration. This can be the result directly from the property, or the
+ * duration calculated from start date and end date. Setting the property
+ * will remove any `dtend` properties.
+ * @type {Duration}
+ */
+ get duration() {
+ let duration = this._firstProp('duration');
+ if (!duration) {
+ return this.endDate.subtractDateTz(this.startDate);
+ }
+ return duration;
+ }
+
+ set duration(value) {
+ if (this.component.hasProperty('dtend')) {
+ this.component.removeProperty('dtend');
+ }
+
+ this._setProp('duration', value);
+ }
+
+ /**
+ * The location of the event.
+ * @type {String}
+ */
+ get location() {
+ return this._firstProp('location');
+ }
+
+ set location(value) {
+ this._setProp('location', value);
+ }
+
+ /**
+ * The attendees in the event
+ * @type {Property[]}
+ */
+ get attendees() {
+ //XXX: This is way lame we should have a better
+ // data structure for this later.
+ return this.component.getAllProperties('attendee');
+ }
+
+ /**
+ * The event summary
+ * @type {String}
+ */
+ get summary() {
+ return this._firstProp('summary');
+ }
+
+ set summary(value) {
+ this._setProp('summary', value);
+ }
+
+ /**
+ * The event description.
+ * @type {String}
+ */
+ get description() {
+ return this._firstProp('description');
+ }
+
+ set description(value) {
+ this._setProp('description', value);
+ }
+
+ /**
+ * The event color from [rfc7986](https://datatracker.ietf.org/doc/html/rfc7986)
+ * @type {String}
+ */
+ get color() {
+ return this._firstProp('color');
+ }
+
+ set color(value) {
+ this._setProp('color', value);
+ }
+
+ /**
+ * The organizer value as an uri. In most cases this is a mailto: uri, but
+ * it can also be something else, like urn:uuid:...
+ * @type {String}
+ */
+ get organizer() {
+ return this._firstProp('organizer');
+ }
+
+ set organizer(value) {
+ this._setProp('organizer', value);
+ }
+
+ /**
+ * The sequence value for this event. Used for scheduling
+ * see {@tutorial terminology}.
+ * @type {Number}
+ */
+ get sequence() {
+ return this._firstProp('sequence');
+ }
+
+ set sequence(value) {
+ this._setProp('sequence', value);
+ }
+
+ /**
+ * The recurrence id for this event. See {@tutorial terminology} for details.
+ * @type {Time}
+ */
+ get recurrenceId() {
+ return this._firstProp('recurrence-id');
+ }
+
+ set recurrenceId(value) {
+ this._setTime('recurrence-id', value);
+ }
+
+ /**
+ * Set/update a time property's value.
+ * This will also update the TZID of the property.
+ *
+ * TODO: this method handles the case where we are switching
+ * from a known timezone to an implied timezone (one without TZID).
+ * This does _not_ handle the case of moving between a known
+ * (by TimezoneService) timezone to an unknown timezone...
+ *
+ * We will not add/remove/update the VTIMEZONE subcomponents
+ * leading to invalid ICAL data...
+ * @private
+ * @param {String} propName The property name
+ * @param {Time} time The time to set
+ */
+ _setTime(propName, time) {
+ let prop = this.component.getFirstProperty(propName);
+
+ if (!prop) {
+ prop = new Property(propName);
+ this.component.addProperty(prop);
+ }
+
+ // utc and local don't get a tzid
+ if (
+ time.zone === Timezone.localTimezone ||
+ time.zone === Timezone.utcTimezone
+ ) {
+ // remove the tzid
+ prop.removeParameter('tzid');
+ } else {
+ prop.setParameter('tzid', time.zone.tzid);
+ }
+
+ prop.setValue(time);
+ }
+
+ _setProp(name, value) {
+ this.component.updatePropertyWithValue(name, value);
+ }
+
+ _firstProp(name) {
+ return this.component.getFirstPropertyValue(name);
+ }
+
+ /**
+ * The string representation of this event.
+ * @return {String}
+ */
+ toString() {
+ return this.component.toString();
+ }
+}
+export default Event;
+
+function compareRangeException(a, b) {
+ if (a[0] > b[0]) return 1;
+ if (b[0] > a[0]) return -1;
+ return 0;
+}
diff --git a/cron/scripts/node_modules/ical.js/lib/ical/helpers.js b/cron/scripts/node_modules/ical.js/lib/ical/helpers.js
new file mode 100644
index 0000000..ba3cf6c
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/lib/ical/helpers.js
@@ -0,0 +1,317 @@
+/* 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 TimezoneService from "./timezone_service.js";
+// needed for typescript type resolution
+// eslint-disable-next-line no-unused-vars
+import Component from "./component.js";
+import ICALmodule from "./module.js";
+
+/**
+ * Helper functions used in various places within ical.js
+ * @module ICAL.helpers
+ */
+
+/**
+ * Compiles a list of all referenced TZIDs in all subcomponents and
+ * removes any extra VTIMEZONE subcomponents. In addition, if any TZIDs
+ * are referenced by a component, but a VTIMEZONE does not exist,
+ * an attempt will be made to generate a VTIMEZONE using ICAL.TimezoneService.
+ *
+ * @param {Component} vcal The top-level VCALENDAR component.
+ * @return {Component} The ICAL.Component that was passed in.
+ */
+export function updateTimezones(vcal) {
+ let allsubs, properties, vtimezones, reqTzid, i;
+
+ if (!vcal || vcal.name !== "vcalendar") {
+ //not a top-level vcalendar component
+ return vcal;
+ }
+
+ //Store vtimezone subcomponents in an object reference by tzid.
+ //Store properties from everything else in another array
+ allsubs = vcal.getAllSubcomponents();
+ properties = [];
+ vtimezones = {};
+ for (i = 0; i < allsubs.length; i++) {
+ if (allsubs[i].name === "vtimezone") {
+ let tzid = allsubs[i].getFirstProperty("tzid").getFirstValue();
+ vtimezones[tzid] = allsubs[i];
+ } else {
+ properties = properties.concat(allsubs[i].getAllProperties());
+ }
+ }
+
+ //create an object with one entry for each required tz
+ reqTzid = {};
+ for (i = 0; i < properties.length; i++) {
+ let tzid = properties[i].getParameter("tzid");
+ if (tzid) {
+ reqTzid[tzid] = true;
+ }
+ }
+
+ //delete any vtimezones that are not on the reqTzid list.
+ for (let [tzid, comp] of Object.entries(vtimezones)) {
+ if (!reqTzid[tzid]) {
+ vcal.removeSubcomponent(comp);
+ }
+ }
+
+ //create any missing, but registered timezones
+ for (let tzid of Object.keys(reqTzid)) {
+ if (!vtimezones[tzid] && TimezoneService.has(tzid)) {
+ vcal.addSubcomponent(TimezoneService.get(tzid).component);
+ }
+ }
+
+ return vcal;
+}
+
+/**
+ * Checks if the given type is of the number type and also NaN.
+ *
+ * @param {Number} number The number to check
+ * @return {Boolean} True, if the number is strictly NaN
+ */
+export function isStrictlyNaN(number) {
+ return typeof(number) === 'number' && isNaN(number);
+}
+
+/**
+ * Parses a string value that is expected to be an integer, when the valid is
+ * not an integer throws a decoration error.
+ *
+ * @param {String} string Raw string input
+ * @return {Number} Parsed integer
+ */
+export function strictParseInt(string) {
+ let result = parseInt(string, 10);
+
+ if (isStrictlyNaN(result)) {
+ throw new Error(
+ 'Could not extract integer from "' + string + '"'
+ );
+ }
+
+ return result;
+}
+
+/**
+ * Creates or returns a class instance of a given type with the initialization
+ * data if the data is not already an instance of the given type.
+ *
+ * @example
+ * var time = new ICAL.Time(...);
+ * var result = ICAL.helpers.formatClassType(time, ICAL.Time);
+ *
+ * (result instanceof ICAL.Time)
+ * // => true
+ *
+ * result = ICAL.helpers.formatClassType({}, ICAL.Time);
+ * (result isntanceof ICAL.Time)
+ * // => true
+ *
+ *
+ * @param {Object} data object initialization data
+ * @param {Object} type object type (like ICAL.Time)
+ * @return {?} An instance of the found type.
+ */
+export function formatClassType(data, type) {
+ if (typeof(data) === 'undefined') {
+ return undefined;
+ }
+
+ if (data instanceof type) {
+ return data;
+ }
+ return new type(data);
+}
+
+/**
+ * Identical to indexOf but will only match values when they are not preceded
+ * by a backslash character.
+ *
+ * @param {String} buffer String to search
+ * @param {String} search Value to look for
+ * @param {Number} pos Start position
+ * @return {Number} The position, or -1 if not found
+ */
+export function unescapedIndexOf(buffer, search, pos) {
+ while ((pos = buffer.indexOf(search, pos)) !== -1) {
+ if (pos > 0 && buffer[pos - 1] === '\\') {
+ pos += 1;
+ } else {
+ return pos;
+ }
+ }
+ return -1;
+}
+
+/**
+ * Find the index for insertion using binary search.
+ *
+ * @param {Array} list The list to search
+ * @param {?} seekVal The value to insert
+ * @param {function(?,?)} cmpfunc The comparison func, that can
+ * compare two seekVals
+ * @return {Number} The insert position
+ */
+export function binsearchInsert(list, seekVal, cmpfunc) {
+ if (!list.length)
+ return 0;
+
+ let low = 0, high = list.length - 1,
+ mid, cmpval;
+
+ while (low <= high) {
+ mid = low + Math.floor((high - low) / 2);
+ cmpval = cmpfunc(seekVal, list[mid]);
+
+ if (cmpval < 0)
+ high = mid - 1;
+ else if (cmpval > 0)
+ low = mid + 1;
+ else
+ break;
+ }
+
+ if (cmpval < 0)
+ return mid; // insertion is displacing, so use mid outright.
+ else if (cmpval > 0)
+ return mid + 1;
+ else
+ return mid;
+}
+
+/**
+ * Clone the passed object or primitive. By default a shallow clone will be
+ * executed.
+ *
+ * @param {*} aSrc The thing to clone
+ * @param {Boolean=} aDeep If true, a deep clone will be performed
+ * @return {*} The copy of the thing
+ */
+export function clone(aSrc, aDeep) {
+ if (!aSrc || typeof aSrc != "object") {
+ return aSrc;
+ } else if (aSrc instanceof Date) {
+ return new Date(aSrc.getTime());
+ } else if ("clone" in aSrc) {
+ return aSrc.clone();
+ } else if (Array.isArray(aSrc)) {
+ let arr = [];
+ for (let i = 0; i < aSrc.length; i++) {
+ arr.push(aDeep ? clone(aSrc[i], true) : aSrc[i]);
+ }
+ return arr;
+ } else {
+ let obj = {};
+ for (let [name, value] of Object.entries(aSrc)) {
+ if (aDeep) {
+ obj[name] = clone(value, true);
+ } else {
+ obj[name] = value;
+ }
+ }
+ return obj;
+ }
+}
+
+/**
+ * Performs iCalendar line folding. A line ending character is inserted and
+ * the next line begins with a whitespace.
+ *
+ * @example
+ * SUMMARY:This line will be fold
+ * ed right in the middle of a word.
+ *
+ * @param {String} aLine The line to fold
+ * @return {String} The folded line
+ */
+export function foldline(aLine) {
+ let result = "";
+ let line = aLine || "", pos = 0, line_length = 0;
+ //pos counts position in line for the UTF-16 presentation
+ //line_length counts the bytes for the UTF-8 presentation
+ while (line.length) {
+ let cp = line.codePointAt(pos);
+ if (cp < 128) ++line_length;
+ else if (cp < 2048) line_length += 2;//needs 2 UTF-8 bytes
+ else if (cp < 65536) line_length += 3;
+ else line_length += 4; //cp is less than 1114112
+ if (line_length < ICALmodule.foldLength + 1)
+ pos += cp > 65535 ? 2 : 1;
+ else {
+ result += ICALmodule.newLineChar + " " + line.slice(0, Math.max(0, pos));
+ line = line.slice(Math.max(0, pos));
+ pos = line_length = 0;
+ }
+ }
+ return result.slice(ICALmodule.newLineChar.length + 1);
+}
+
+/**
+ * Pads the given string or number with zeros so it will have at least two
+ * characters.
+ *
+ * @param {String|Number} data The string or number to pad
+ * @return {String} The number padded as a string
+ */
+export function pad2(data) {
+ if (typeof(data) !== 'string') {
+ // handle fractions.
+ if (typeof(data) === 'number') {
+ data = parseInt(data);
+ }
+ data = String(data);
+ }
+
+ let len = data.length;
+
+ switch (len) {
+ case 0:
+ return '00';
+ case 1:
+ return '0' + data;
+ default:
+ return data;
+ }
+}
+
+/**
+ * Truncates the given number, correctly handling negative numbers.
+ *
+ * @param {Number} number The number to truncate
+ * @return {Number} The truncated number
+ */
+export function trunc(number) {
+ return (number < 0 ? Math.ceil(number) : Math.floor(number));
+}
+
+/**
+ * Poor-man's cross-browser object extension. Doesn't support all the
+ * features, but enough for our usage. Note that the target's properties are
+ * not overwritten with the source properties.
+ *
+ * @example
+ * var child = ICAL.helpers.extend(parent, {
+ * "bar": 123
+ * });
+ *
+ * @param {Object} source The object to extend
+ * @param {Object} target The object to extend with
+ * @return {Object} Returns the target.
+ */
+export function extend(source, target) {
+ for (let key in source) {
+ let descr = Object.getOwnPropertyDescriptor(source, key);
+ if (descr && !Object.getOwnPropertyDescriptor(target, key)) {
+ Object.defineProperty(target, key, descr);
+ }
+ }
+ return target;
+}
diff --git a/cron/scripts/node_modules/ical.js/lib/ical/module.js b/cron/scripts/node_modules/ical.js/lib/ical/module.js
new file mode 100644
index 0000000..85858d7
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/lib/ical/module.js
@@ -0,0 +1,73 @@
+/* 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 Binary from "./binary.js";
+import Component from "./component.js";
+import ComponentParser from "./component_parser.js";
+import design from "./design.js";
+import Duration from "./duration.js";
+import Event from "./event.js";
+import * as helpers from "./helpers.js";
+import parse from "./parse.js";
+import Period from "./period.js";
+import Property from "./property.js";
+import Recur from "./recur.js";
+import RecurExpansion from "./recur_expansion.js";
+import RecurIterator from "./recur_iterator.js";
+import stringify from "./stringify.js";
+import Time from "./time.js";
+import Timezone from "./timezone.js";
+import TimezoneService from "./timezone_service.js";
+import UtcOffset from "./utc_offset.js";
+import VCardTime from "./vcard_time.js";
+
+/**
+ * The main ICAL module. Provides access to everything else.
+ *
+ * @alias ICAL
+ * @namespace ICAL
+ * @property {ICAL.design} design
+ * @property {ICAL.helpers} helpers
+ */
+export default {
+ /**
+ * The number of characters before iCalendar line folding should occur
+ * @type {Number}
+ * @default 75
+ */
+ foldLength: 75,
+
+ debug: false,
+
+ /**
+ * The character(s) to be used for a newline. The default value is provided by
+ * rfc5545.
+ * @type {String}
+ * @default "\r\n"
+ */
+ newLineChar: '\r\n',
+
+ Binary,
+ Component,
+ ComponentParser,
+ Duration,
+ Event,
+ Period,
+ Property,
+ Recur,
+ RecurExpansion,
+ RecurIterator,
+ Time,
+ Timezone,
+ TimezoneService,
+ UtcOffset,
+ VCardTime,
+
+ parse,
+ stringify,
+
+ design,
+ helpers
+};
diff --git a/cron/scripts/node_modules/ical.js/lib/ical/parse.js b/cron/scripts/node_modules/ical.js/lib/ical/parse.js
new file mode 100644
index 0000000..7585fd9
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/lib/ical/parse.js
@@ -0,0 +1,557 @@
+/* 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 design from "./design.js";
+import { unescapedIndexOf } from "./helpers.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").parserState} parserState
+ * Imports the 'parserState' type from the "types.js" module
+ * @typedef {import("./types.js").designSet} designSet
+ * Imports the 'designSet' type from the "types.js" module
+ */
+
+const CHAR = /[^ \t]/;
+const VALUE_DELIMITER = ':';
+const PARAM_DELIMITER = ';';
+const PARAM_NAME_DELIMITER = '=';
+const DEFAULT_VALUE_TYPE = 'unknown';
+const DEFAULT_PARAM_TYPE = 'text';
+const RFC6868_REPLACE_MAP = { "^'": '"', "^n": "\n", "^^": "^" };
+
+/**
+ * Parses iCalendar or vCard data into a raw jCal object. Consult
+ * documentation on the {@tutorial layers|layers of parsing} for more
+ * details.
+ *
+ * @function ICAL.parse
+ * @memberof ICAL
+ * @variation function
+ * @todo Fix the API to be more clear on the return type
+ * @param {String} input The string data to parse
+ * @return {Object|Object[]} A single jCal object, or an array thereof
+ */
+export default function parse(input) {
+ let state = {};
+ let root = state.component = [];
+
+ state.stack = [root];
+
+ parse._eachLine(input, function(err, line) {
+ parse._handleContentLine(line, state);
+ });
+
+
+ // when there are still items on the stack
+ // throw a fatal error, a component was not closed
+ // correctly in that case.
+ if (state.stack.length > 1) {
+ throw new ParserError(
+ 'invalid ical body. component began but did not end'
+ );
+ }
+
+ state = null;
+
+ return (root.length == 1 ? root[0] : root);
+}
+
+/**
+ * Parse an iCalendar property value into the jCal for a single property
+ *
+ * @function ICAL.parse.property
+ * @param {String} str
+ * The iCalendar property string to parse
+ * @param {designSet=} designSet
+ * The design data to use for this property
+ * @return {Object}
+ * The jCal Object containing the property
+ */
+parse.property = function(str, designSet) {
+ let state = {
+ component: [[], []],
+ designSet: designSet || design.defaultSet
+ };
+ parse._handleContentLine(str, state);
+ return state.component[1][0];
+};
+
+/**
+ * Convenience method to parse a component. You can use ICAL.parse() directly
+ * instead.
+ *
+ * @function ICAL.parse.component
+ * @see ICAL.parse(function)
+ * @param {String} str The iCalendar component string to parse
+ * @return {Object} The jCal Object containing the component
+ */
+parse.component = function(str) {
+ return parse(str);
+};
+
+
+/**
+ * An error that occurred during parsing.
+ *
+ * @param {String} message The error message
+ * @memberof ICAL.parse
+ * @extends {Error}
+ */
+class ParserError extends Error {
+ name = this.constructor.name;
+}
+
+// classes & constants
+parse.ParserError = ParserError;
+
+
+/**
+ * Handles a single line of iCalendar/vCard, updating the state.
+ *
+ * @private
+ * @function ICAL.parse._handleContentLine
+ * @param {String} line The content line to process
+ * @param {parserState} state The current state of the line parsing
+ */
+parse._handleContentLine = function(line, state) {
+ // break up the parts of the line
+ let valuePos = line.indexOf(VALUE_DELIMITER);
+ let paramPos = line.indexOf(PARAM_DELIMITER);
+
+ let lastParamIndex;
+ let lastValuePos;
+
+ // name of property or begin/end
+ let name;
+ let value;
+ // params is only overridden if paramPos !== -1.
+ // we can't do params = params || {} later on
+ // because it sacrifices ops.
+ let params = {};
+
+ /**
+ * Different property cases
+ *
+ *
+ * 1. RRULE:FREQ=foo
+ * // FREQ= is not a param but the value
+ *
+ * 2. ATTENDEE;ROLE=REQ-PARTICIPANT;
+ * // ROLE= is a param because : has not happened yet
+ */
+ // when the parameter delimiter is after the
+ // value delimiter then it is not a parameter.
+
+ if ((paramPos !== -1 && valuePos !== -1)) {
+ // when the parameter delimiter is after the
+ // value delimiter then it is not a parameter.
+ if (paramPos > valuePos) {
+ paramPos = -1;
+ }
+ }
+
+ let parsedParams;
+ if (paramPos !== -1) {
+ name = line.slice(0, Math.max(0, paramPos)).toLowerCase();
+ parsedParams = parse._parseParameters(line.slice(Math.max(0, paramPos)), 0, state.designSet);
+ if (parsedParams[2] == -1) {
+ throw new ParserError("Invalid parameters in '" + line + "'");
+ }
+ params = parsedParams[0];
+ // Handle parameter values with multiple entries
+ let parsedParamLength;
+ if (typeof parsedParams[1] === 'string') {
+ parsedParamLength = parsedParams[1].length;
+ } else {
+ parsedParamLength = parsedParams[1].reduce((accumulator, currentValue) => {
+ return accumulator + currentValue.length;
+ }, 0);
+ }
+ lastParamIndex = parsedParamLength + parsedParams[2] + paramPos;
+ if ((lastValuePos =
+ line.slice(Math.max(0, lastParamIndex)).indexOf(VALUE_DELIMITER)) !== -1) {
+ value = line.slice(Math.max(0, lastParamIndex + lastValuePos + 1));
+ } else {
+ throw new ParserError("Missing parameter value in '" + line + "'");
+ }
+ } else if (valuePos !== -1) {
+ // without parmeters (BEGIN:VCAENDAR, CLASS:PUBLIC)
+ name = line.slice(0, Math.max(0, valuePos)).toLowerCase();
+ value = line.slice(Math.max(0, valuePos + 1));
+
+ if (name === 'begin') {
+ let newComponent = [value.toLowerCase(), [], []];
+ if (state.stack.length === 1) {
+ state.component.push(newComponent);
+ } else {
+ state.component[2].push(newComponent);
+ }
+ state.stack.push(state.component);
+ state.component = newComponent;
+ if (!state.designSet) {
+ state.designSet = design.getDesignSet(state.component[0]);
+ }
+ return;
+ } else if (name === 'end') {
+ state.component = state.stack.pop();
+ return;
+ }
+ // If it is not begin/end, then this is a property with an empty value,
+ // which should be considered valid.
+ } else {
+ /**
+ * Invalid line.
+ * The rational to throw an error is we will
+ * never be certain that the rest of the file
+ * is sane and it is unlikely that we can serialize
+ * the result correctly either.
+ */
+ throw new ParserError(
+ 'invalid line (no token ";" or ":") "' + line + '"'
+ );
+ }
+
+ let valueType;
+ let multiValue = false;
+ let structuredValue = false;
+ let propertyDetails;
+ let splitName;
+ let ungroupedName;
+
+ // fetch the ungrouped part of the name
+ if (state.designSet.propertyGroups && name.indexOf('.') !== -1) {
+ splitName = name.split('.');
+ params.group = splitName[0];
+ ungroupedName = splitName[1];
+ } else {
+ ungroupedName = name;
+ }
+
+ if (ungroupedName in state.designSet.property) {
+ propertyDetails = state.designSet.property[ungroupedName];
+
+ if ('multiValue' in propertyDetails) {
+ multiValue = propertyDetails.multiValue;
+ }
+
+ if ('structuredValue' in propertyDetails) {
+ structuredValue = propertyDetails.structuredValue;
+ }
+
+ if (value && 'detectType' in propertyDetails) {
+ valueType = propertyDetails.detectType(value);
+ }
+ }
+
+ // attempt to determine value
+ if (!valueType) {
+ if (!('value' in params)) {
+ if (propertyDetails) {
+ valueType = propertyDetails.defaultType;
+ } else {
+ valueType = DEFAULT_VALUE_TYPE;
+ }
+ } else {
+ // possible to avoid this?
+ valueType = params.value.toLowerCase();
+ }
+ }
+
+ delete params.value;
+
+ /**
+ * Note on `var result` juggling:
+ *
+ * I observed that building the array in pieces has adverse
+ * effects on performance, so where possible we inline the creation.
+ * It is a little ugly but resulted in ~2000 additional ops/sec.
+ */
+
+ let result;
+ if (multiValue && structuredValue) {
+ value = parse._parseMultiValue(value, structuredValue, valueType, [], multiValue, state.designSet, structuredValue);
+ result = [ungroupedName, params, valueType, value];
+ } else if (multiValue) {
+ result = [ungroupedName, params, valueType];
+ parse._parseMultiValue(value, multiValue, valueType, result, null, state.designSet, false);
+ } else if (structuredValue) {
+ value = parse._parseMultiValue(value, structuredValue, valueType, [], null, state.designSet, structuredValue);
+ result = [ungroupedName, params, valueType, value];
+ } else {
+ value = parse._parseValue(value, valueType, state.designSet, false);
+ result = [ungroupedName, params, valueType, value];
+ }
+ // rfc6350 requires that in vCard 4.0 the first component is the VERSION
+ // component with as value 4.0, note that 3.0 does not have this requirement.
+ if (state.component[0] === 'vcard' && state.component[1].length === 0 &&
+ !(name === 'version' && value === '4.0')) {
+ state.designSet = design.getDesignSet("vcard3");
+ }
+ state.component[1].push(result);
+};
+
+/**
+ * Parse a value from the raw value into the jCard/jCal value.
+ *
+ * @private
+ * @function ICAL.parse._parseValue
+ * @param {String} value Original value
+ * @param {String} type Type of value
+ * @param {Object} designSet The design data to use for this value
+ * @return {Object} varies on type
+ */
+parse._parseValue = function(value, type, designSet, structuredValue) {
+ if (type in designSet.value && 'fromICAL' in designSet.value[type]) {
+ return designSet.value[type].fromICAL(value, structuredValue);
+ }
+ return value;
+};
+
+/**
+ * Parse parameters from a string to object.
+ *
+ * @function ICAL.parse._parseParameters
+ * @private
+ * @param {String} line A single unfolded line
+ * @param {Number} start Position to start looking for properties
+ * @param {Object} designSet The design data to use for this property
+ * @return {Array} Array containing key/valye pairs of parsed parameters, the
+ * parsed value, and the position of the last parameter found
+ */
+parse._parseParameters = function(line, start, designSet) {
+ let lastParam = start;
+ let pos = 0;
+ let delim = PARAM_NAME_DELIMITER;
+ let result = {};
+ let name, lcname;
+ let value, valuePos = -1;
+ let type, multiValue, mvdelim;
+
+ // find the next '=' sign
+ // use lastParam and pos to find name
+ // check if " is used if so get value from "->"
+ // then increment pos to find next ;
+
+ while ((pos !== false) &&
+ (pos = line.indexOf(delim, pos + 1)) !== -1) {
+
+ name = line.slice(lastParam + 1, pos);
+ if (name.length == 0) {
+ throw new ParserError("Empty parameter name in '" + line + "'");
+ }
+ lcname = name.toLowerCase();
+ mvdelim = false;
+ multiValue = false;
+
+ if (lcname in designSet.param && designSet.param[lcname].valueType) {
+ type = designSet.param[lcname].valueType;
+ } else {
+ type = DEFAULT_PARAM_TYPE;
+ }
+
+ if (lcname in designSet.param) {
+ multiValue = designSet.param[lcname].multiValue;
+ if (designSet.param[lcname].multiValueSeparateDQuote) {
+ mvdelim = parse._rfc6868Escape('"' + multiValue + '"');
+ }
+ }
+
+ let nextChar = line[pos + 1];
+ if (nextChar === '"') {
+ valuePos = pos + 2;
+ pos = line.indexOf('"', valuePos);
+ if (multiValue && pos != -1) {
+ let extendedValue = true;
+ while (extendedValue) {
+ if (line[pos + 1] == multiValue && line[pos + 2] == '"') {
+ pos = line.indexOf('"', pos + 3);
+ } else {
+ extendedValue = false;
+ }
+ }
+ }
+ if (pos === -1) {
+ throw new ParserError(
+ 'invalid line (no matching double quote) "' + line + '"'
+ );
+ }
+ value = line.slice(valuePos, pos);
+ lastParam = line.indexOf(PARAM_DELIMITER, pos);
+ let propValuePos = line.indexOf(VALUE_DELIMITER, pos);
+ // if either no next parameter or delimeter in property value, let's stop here
+ if (lastParam === -1 || (propValuePos !== -1 && lastParam > propValuePos)) {
+ pos = false;
+ }
+ } else {
+ valuePos = pos + 1;
+
+ // move to next ";"
+ let nextPos = line.indexOf(PARAM_DELIMITER, valuePos);
+ let propValuePos = line.indexOf(VALUE_DELIMITER, valuePos);
+ if (propValuePos !== -1 && nextPos > propValuePos) {
+ // this is a delimiter in the property value, let's stop here
+ nextPos = propValuePos;
+ pos = false;
+ } else if (nextPos === -1) {
+ // no ";"
+ if (propValuePos === -1) {
+ nextPos = line.length;
+ } else {
+ nextPos = propValuePos;
+ }
+ pos = false;
+ } else {
+ lastParam = nextPos;
+ pos = nextPos;
+ }
+
+ value = line.slice(valuePos, nextPos);
+ }
+
+ const length_before = value.length;
+ value = parse._rfc6868Escape(value);
+ valuePos += length_before - value.length;
+ if (multiValue) {
+ let delimiter = mvdelim || multiValue;
+ value = parse._parseMultiValue(value, delimiter, type, [], null, designSet);
+ } else {
+ value = parse._parseValue(value, type, designSet);
+ }
+
+ if (multiValue && (lcname in result)) {
+ if (Array.isArray(result[lcname])) {
+ result[lcname].push(value);
+ } else {
+ result[lcname] = [
+ result[lcname],
+ value
+ ];
+ }
+ } else {
+ result[lcname] = value;
+ }
+ }
+ return [result, value, valuePos];
+};
+
+/**
+ * Internal helper for rfc6868. Exposing this on ICAL.parse so that
+ * hackers can disable the rfc6868 parsing if the really need to.
+ *
+ * @function ICAL.parse._rfc6868Escape
+ * @param {String} val The value to escape
+ * @return {String} The escaped value
+ */
+parse._rfc6868Escape = function(val) {
+ return val.replace(/\^['n^]/g, function(x) {
+ return RFC6868_REPLACE_MAP[x];
+ });
+};
+
+/**
+ * Parse a multi value string. This function is used either for parsing
+ * actual multi-value property's values, or for handling parameter values. It
+ * can be used for both multi-value properties and structured value properties.
+ *
+ * @private
+ * @function ICAL.parse._parseMultiValue
+ * @param {String} buffer The buffer containing the full value
+ * @param {String} delim The multi-value delimiter
+ * @param {String} type The value type to be parsed
+ * @param {Array.>} result The array to append results to, varies on value type
+ * @param {String} innerMulti The inner delimiter to split each value with
+ * @param {designSet} designSet The design data for this value
+ * @return {?|Array.>} Either an array of results, or the first result
+ */
+parse._parseMultiValue = function(buffer, delim, type, result, innerMulti, designSet, structuredValue) {
+ let pos = 0;
+ let lastPos = 0;
+ let value;
+ if (delim.length === 0) {
+ return buffer;
+ }
+
+ // split each piece
+ while ((pos = unescapedIndexOf(buffer, delim, lastPos)) !== -1) {
+ value = buffer.slice(lastPos, pos);
+ if (innerMulti) {
+ value = parse._parseMultiValue(value, innerMulti, type, [], null, designSet, structuredValue);
+ } else {
+ value = parse._parseValue(value, type, designSet, structuredValue);
+ }
+ result.push(value);
+ lastPos = pos + delim.length;
+ }
+
+ // on the last piece take the rest of string
+ value = buffer.slice(lastPos);
+ if (innerMulti) {
+ value = parse._parseMultiValue(value, innerMulti, type, [], null, designSet, structuredValue);
+ } else {
+ value = parse._parseValue(value, type, designSet, structuredValue);
+ }
+ result.push(value);
+
+ return result.length == 1 ? result[0] : result;
+};
+
+/**
+ * Process a complete buffer of iCalendar/vCard data line by line, correctly
+ * unfolding content. Each line will be processed with the given callback
+ *
+ * @private
+ * @function ICAL.parse._eachLine
+ * @param {String} buffer The buffer to process
+ * @param {function(?String, String)} callback The callback for each line
+ */
+parse._eachLine = function(buffer, callback) {
+ let len = buffer.length;
+ let lastPos = buffer.search(CHAR);
+ let pos = lastPos;
+ let line;
+ let firstChar;
+
+ let newlineOffset;
+
+ do {
+ pos = buffer.indexOf('\n', lastPos) + 1;
+
+ if (pos > 1 && buffer[pos - 2] === '\r') {
+ newlineOffset = 2;
+ } else {
+ newlineOffset = 1;
+ }
+
+ if (pos === 0) {
+ pos = len;
+ newlineOffset = 0;
+ }
+
+ firstChar = buffer[lastPos];
+
+ if (firstChar === ' ' || firstChar === '\t') {
+ // add to line
+ line += buffer.slice(lastPos + 1, pos - newlineOffset);
+ } else {
+ if (line)
+ callback(null, line);
+ // push line
+ line = buffer.slice(lastPos, pos - newlineOffset);
+ }
+
+ lastPos = pos;
+ } while (pos !== len);
+
+ // extra ending line
+ line = line.trim();
+
+ if (line.length)
+ callback(null, line);
+};
diff --git a/cron/scripts/node_modules/ical.js/lib/ical/period.js b/cron/scripts/node_modules/ical.js/lib/ical/period.js
new file mode 100644
index 0000000..4f4acf4
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/lib/ical/period.js
@@ -0,0 +1,263 @@
+/* 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 Time from "./time.js";
+import Duration from "./duration.js";
+// needed for typescript type resolution
+// eslint-disable-next-line no-unused-vars
+import Property from "./property.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").jCalComponent} jCalComponent
+ * Imports the 'occurrenceDetails' type from the "types.js" module
+ */
+
+/**
+ * This class represents the "period" value type, with various calculation and manipulation methods.
+ *
+ * @memberof ICAL
+ */
+class Period {
+ /**
+ * Creates a new {@link ICAL.Period} instance from the passed string.
+ *
+ * @param {String} str The string to parse
+ * @param {Property} prop The property this period will be on
+ * @return {Period} The created period instance
+ */
+ static fromString(str, prop) {
+ let parts = str.split('/');
+
+ if (parts.length !== 2) {
+ throw new Error(
+ 'Invalid string value: "' + str + '" must contain a "/" char.'
+ );
+ }
+
+ let options = {
+ start: Time.fromDateTimeString(parts[0], prop)
+ };
+
+ let end = parts[1];
+
+ if (Duration.isValueString(end)) {
+ options.duration = Duration.fromString(end);
+ } else {
+ options.end = Time.fromDateTimeString(end, prop);
+ }
+
+ return new Period(options);
+ }
+
+ /**
+ * Creates a new {@link ICAL.Period} instance from the given data object.
+ * The passed data object cannot contain both and end date and a duration.
+ *
+ * @param {Object} aData An object with members of the period
+ * @param {Time=} aData.start The start of the period
+ * @param {Time=} aData.end The end of the period
+ * @param {Duration=} aData.duration The duration of the period
+ * @return {Period} The period instance
+ */
+ static fromData(aData) {
+ return new Period(aData);
+ }
+
+ /**
+ * Returns a new period instance from the given jCal data array. The first
+ * member is always the start date string, the second member is either a
+ * duration or end date string.
+ *
+ * @param {jCalComponent} aData The jCal data array
+ * @param {Property} aProp The property this jCal data is on
+ * @param {Boolean} aLenient If true, data value can be both date and date-time
+ * @return {Period} The period instance
+ */
+ static fromJSON(aData, aProp, aLenient) {
+ function fromDateOrDateTimeString(aValue, dateProp) {
+ if (aLenient) {
+ return Time.fromString(aValue, dateProp);
+ } else {
+ return Time.fromDateTimeString(aValue, dateProp);
+ }
+ }
+
+ if (Duration.isValueString(aData[1])) {
+ return Period.fromData({
+ start: fromDateOrDateTimeString(aData[0], aProp),
+ duration: Duration.fromString(aData[1])
+ });
+ } else {
+ return Period.fromData({
+ start: fromDateOrDateTimeString(aData[0], aProp),
+ end: fromDateOrDateTimeString(aData[1], aProp)
+ });
+ }
+ }
+
+ /**
+ * Creates a new ICAL.Period instance. The passed data object cannot contain both and end date and
+ * a duration.
+ *
+ * @param {Object} aData An object with members of the period
+ * @param {Time=} aData.start The start of the period
+ * @param {Time=} aData.end The end of the period
+ * @param {Duration=} aData.duration The duration of the period
+ */
+ constructor(aData) {
+ this.wrappedJSObject = this;
+
+ if (aData && 'start' in aData) {
+ if (aData.start && !(aData.start instanceof Time)) {
+ throw new TypeError('.start must be an instance of ICAL.Time');
+ }
+ this.start = aData.start;
+ }
+
+ if (aData && aData.end && aData.duration) {
+ throw new Error('cannot accept both end and duration');
+ }
+
+ if (aData && 'end' in aData) {
+ if (aData.end && !(aData.end instanceof Time)) {
+ throw new TypeError('.end must be an instance of ICAL.Time');
+ }
+ this.end = aData.end;
+ }
+
+ if (aData && 'duration' in aData) {
+ if (aData.duration && !(aData.duration instanceof Duration)) {
+ throw new TypeError('.duration must be an instance of ICAL.Duration');
+ }
+ this.duration = aData.duration;
+ }
+ }
+
+
+ /**
+ * The start of the period
+ * @type {Time}
+ */
+ start = null;
+
+ /**
+ * The end of the period
+ * @type {Time}
+ */
+ end = null;
+
+ /**
+ * The duration of the period
+ * @type {Duration}
+ */
+ duration = null;
+
+ /**
+ * The class identifier.
+ * @constant
+ * @type {String}
+ * @default "icalperiod"
+ */
+ icalclass = "icalperiod";
+
+ /**
+ * The type name, to be used in the jCal object.
+ * @constant
+ * @type {String}
+ * @default "period"
+ */
+ icaltype = "period";
+
+ /**
+ * Returns a clone of the duration object.
+ *
+ * @return {Period} The cloned object
+ */
+ clone() {
+ return Period.fromData({
+ start: this.start ? this.start.clone() : null,
+ end: this.end ? this.end.clone() : null,
+ duration: this.duration ? this.duration.clone() : null
+ });
+ }
+
+ /**
+ * Calculates the duration of the period, either directly or by subtracting
+ * start from end date.
+ *
+ * @return {Duration} The calculated duration
+ */
+ getDuration() {
+ if (this.duration) {
+ return this.duration;
+ } else {
+ return this.end.subtractDate(this.start);
+ }
+ }
+
+ /**
+ * Calculates the end date of the period, either directly or by adding
+ * duration to start date.
+ *
+ * @return {Time} The calculated end date
+ */
+ getEnd() {
+ if (this.end) {
+ return this.end;
+ } else {
+ let end = this.start.clone();
+ end.addDuration(this.duration);
+ return end;
+ }
+ }
+
+ /**
+ * Compare this period with a date or other period. To maintain the logic where a.compare(b)
+ * returns 1 when a > b, this function will return 1 when the period is after the date, 0 when the
+ * date is within the period, and -1 when the period is before the date. When comparing two
+ * periods, as soon as they overlap in any way this will return 0.
+ *
+ * @param {Time|Period} dt The date or other period to compare with
+ */
+ compare(dt) {
+ if (dt.compare(this.start) < 0) {
+ return 1;
+ } else if (dt.compare(this.getEnd()) > 0) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * The string representation of this period.
+ * @return {String}
+ */
+ toString() {
+ return this.start + "/" + (this.end || this.duration);
+ }
+
+ /**
+ * The jCal representation of this period type.
+ * @return {Object}
+ */
+ toJSON() {
+ return [this.start.toString(), (this.end || this.duration).toString()];
+ }
+
+ /**
+ * The iCalendar string representation of this period.
+ * @return {String}
+ */
+ toICALString() {
+ return this.start.toICALString() + "/" +
+ (this.end || this.duration).toICALString();
+ }
+}
+export default Period;
diff --git a/cron/scripts/node_modules/ical.js/lib/ical/property.js b/cron/scripts/node_modules/ical.js/lib/ical/property.js
new file mode 100644
index 0000000..3ded381
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/lib/ical/property.js
@@ -0,0 +1,439 @@
+/* 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 */
+
+const NAME_INDEX = 0;
+const PROP_INDEX = 1;
+const TYPE_INDEX = 2;
+const VALUE_INDEX = 3;
+
+import design from "./design.js";
+import ICALStringify from "./stringify.js";
+import ICALParse from "./parse.js";
+
+// needed for typescript type resolution
+// eslint-disable-next-line no-unused-vars
+import Component from "./component.js";
+// needed for typescript type resolution
+// eslint-disable-next-line no-unused-vars
+import Duration from "./duration.js";
+// needed for typescript type resolution
+// eslint-disable-next-line no-unused-vars
+import UtcOffset from "./utc_offset.js";
+// needed for typescript type resolution
+// eslint-disable-next-line no-unused-vars
+import Binary from "./binary.js";
+// needed for typescript type resolution
+// eslint-disable-next-line no-unused-vars
+import Period from "./period.js";
+// needed for typescript type resolution
+// eslint-disable-next-line no-unused-vars
+import Recur from "./recur.js";
+// needed for typescript type resolution
+// eslint-disable-next-line no-unused-vars
+import Time from "./time.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
+ * @typedef {import("./types.js").Geo} Geo
+ * Imports the 'Geo' type from the "types.js" module
+ */
+
+/**
+ * Provides a layer on top of the raw jCal object for manipulating a single property, with its
+ * parameters and value.
+ *
+ * @memberof ICAL
+ */
+class Property {
+ /**
+ * Create an {@link ICAL.Property} by parsing the passed iCalendar string.
+ *
+ * @param {String} str The iCalendar string to parse
+ * @param {designSet=} designSet The design data to use for this property
+ * @return {Property} The created iCalendar property
+ */
+ static fromString(str, designSet) {
+ return new Property(ICALParse.property(str, designSet));
+ }
+
+ /**
+ * Creates a new ICAL.Property instance.
+ *
+ * It is important to note that mutations done in the wrapper directly mutate the jCal object used
+ * to initialize.
+ *
+ * Can also be used to create new properties by passing the name of the property (as a String).
+ *
+ * @param {Array|String} jCal Raw jCal representation OR the new name of the property
+ * @param {Component=} parent Parent component
+ */
+ constructor(jCal, parent) {
+ this._parent = parent || null;
+
+ if (typeof(jCal) === 'string') {
+ // We are creating the property by name and need to detect the type
+ this.jCal = [jCal, {}, design.defaultType];
+ this.jCal[TYPE_INDEX] = this.getDefaultType();
+ } else {
+ this.jCal = jCal;
+ }
+ this._updateType();
+ }
+
+ /**
+ * The value type for this property
+ * @type {String}
+ */
+ get type() {
+ return this.jCal[TYPE_INDEX];
+ }
+
+ /**
+ * The name of this property, in lowercase.
+ * @type {String}
+ */
+ get name() {
+ return this.jCal[NAME_INDEX];
+ }
+
+ /**
+ * The parent component for this property.
+ * @type {Component}
+ */
+ get parent() {
+ return this._parent;
+ }
+
+ set parent(p) {
+ // Before setting the parent, check if the design set has changed. If it
+ // has, we later need to update the type if it was unknown before.
+ let designSetChanged = !this._parent || (p && p._designSet != this._parent._designSet);
+
+ this._parent = p;
+
+ if (this.type == design.defaultType && designSetChanged) {
+ this.jCal[TYPE_INDEX] = this.getDefaultType();
+ this._updateType();
+ }
+ }
+
+ /**
+ * The design set for this property, e.g. icalendar vs vcard
+ *
+ * @type {designSet}
+ * @private
+ */
+ get _designSet() {
+ return this.parent ? this.parent._designSet : design.defaultSet;
+ }
+
+ /**
+ * Updates the type metadata from the current jCal type and design set.
+ *
+ * @private
+ */
+ _updateType() {
+ let designSet = this._designSet;
+
+ if (this.type in designSet.value) {
+ if ('decorate' in designSet.value[this.type]) {
+ this.isDecorated = true;
+ } else {
+ this.isDecorated = false;
+ }
+
+ if (this.name in designSet.property) {
+ this.isMultiValue = ('multiValue' in designSet.property[this.name]);
+ this.isStructuredValue = ('structuredValue' in designSet.property[this.name]);
+ }
+ }
+ }
+
+ /**
+ * Hydrate a single value. The act of hydrating means turning the raw jCal
+ * value into a potentially wrapped object, for example {@link ICAL.Time}.
+ *
+ * @private
+ * @param {Number} index The index of the value to hydrate
+ * @return {?Object} The decorated value.
+ */
+ _hydrateValue(index) {
+ if (this._values && this._values[index]) {
+ return this._values[index];
+ }
+
+ // for the case where there is no value.
+ if (this.jCal.length <= (VALUE_INDEX + index)) {
+ return null;
+ }
+
+ if (this.isDecorated) {
+ if (!this._values) {
+ this._values = [];
+ }
+ return (this._values[index] = this._decorate(
+ this.jCal[VALUE_INDEX + index]
+ ));
+ } else {
+ return this.jCal[VALUE_INDEX + index];
+ }
+ }
+
+ /**
+ * Decorate a single value, returning its wrapped object. This is used by
+ * the hydrate function to actually wrap the value.
+ *
+ * @private
+ * @param {?} value The value to decorate
+ * @return {Object} The decorated value
+ */
+ _decorate(value) {
+ return this._designSet.value[this.type].decorate(value, this);
+ }
+
+ /**
+ * Undecorate a single value, returning its raw jCal data.
+ *
+ * @private
+ * @param {Object} value The value to undecorate
+ * @return {?} The undecorated value
+ */
+ _undecorate(value) {
+ return this._designSet.value[this.type].undecorate(value, this);
+ }
+
+ /**
+ * Sets the value at the given index while also hydrating it. The passed
+ * value can either be a decorated or undecorated value.
+ *
+ * @private
+ * @param {?} value The value to set
+ * @param {Number} index The index to set it at
+ */
+ _setDecoratedValue(value, index) {
+ if (!this._values) {
+ this._values = [];
+ }
+
+ if (typeof(value) === 'object' && 'icaltype' in value) {
+ // decorated value
+ this.jCal[VALUE_INDEX + index] = this._undecorate(value);
+ this._values[index] = value;
+ } else {
+ // undecorated value
+ this.jCal[VALUE_INDEX + index] = value;
+ this._values[index] = this._decorate(value);
+ }
+ }
+
+ /**
+ * Gets a parameter on the property.
+ *
+ * @param {String} name Parameter name (lowercase)
+ * @return {Array|String} Parameter value
+ */
+ getParameter(name) {
+ if (name in this.jCal[PROP_INDEX]) {
+ return this.jCal[PROP_INDEX][name];
+ } else {
+ return undefined;
+ }
+ }
+
+ /**
+ * Gets first parameter on the property.
+ *
+ * @param {String} name Parameter name (lowercase)
+ * @return {String} Parameter value
+ */
+ getFirstParameter(name) {
+ let parameters = this.getParameter(name);
+
+ if (Array.isArray(parameters)) {
+ return parameters[0];
+ }
+
+ return parameters;
+ }
+
+ /**
+ * Sets a parameter on the property.
+ *
+ * @param {String} name The parameter name
+ * @param {Array|String} value The parameter value
+ */
+ setParameter(name, value) {
+ let lcname = name.toLowerCase();
+ if (typeof value === "string" &&
+ lcname in this._designSet.param &&
+ 'multiValue' in this._designSet.param[lcname]) {
+ value = [value];
+ }
+ this.jCal[PROP_INDEX][name] = value;
+ }
+
+ /**
+ * Removes a parameter
+ *
+ * @param {String} name The parameter name
+ */
+ removeParameter(name) {
+ delete this.jCal[PROP_INDEX][name];
+ }
+
+ /**
+ * Get the default type based on this property's name.
+ *
+ * @return {String} The default type for this property
+ */
+ getDefaultType() {
+ let name = this.jCal[NAME_INDEX];
+ let designSet = this._designSet;
+
+ if (name in designSet.property) {
+ let details = designSet.property[name];
+ if ('defaultType' in details) {
+ return details.defaultType;
+ }
+ }
+ return design.defaultType;
+ }
+
+ /**
+ * Sets type of property and clears out any existing values of the current
+ * type.
+ *
+ * @param {String} type New iCAL type (see design.*.values)
+ */
+ resetType(type) {
+ this.removeAllValues();
+ this.jCal[TYPE_INDEX] = type;
+ this._updateType();
+ }
+
+ /**
+ * Finds the first property value.
+ *
+ * @return {Binary | Duration | Period |
+ * Recur | Time | UtcOffset | Geo | string | null} First property value
+ */
+ getFirstValue() {
+ return this._hydrateValue(0);
+ }
+
+ /**
+ * Gets all values on the property.
+ *
+ * NOTE: this creates an array during each call.
+ *
+ * @return {Array} List of values
+ */
+ getValues() {
+ let len = this.jCal.length - VALUE_INDEX;
+
+ if (len < 1) {
+ // it is possible for a property to have no value.
+ return [];
+ }
+
+ let i = 0;
+ let result = [];
+
+ for (; i < len; i++) {
+ result[i] = this._hydrateValue(i);
+ }
+
+ return result;
+ }
+
+ /**
+ * Removes all values from this property
+ */
+ removeAllValues() {
+ if (this._values) {
+ this._values.length = 0;
+ }
+ this.jCal.length = 3;
+ }
+
+ /**
+ * Sets the values of the property. Will overwrite the existing values.
+ * This can only be used for multi-value properties.
+ *
+ * @param {Array} values An array of values
+ */
+ setValues(values) {
+ if (!this.isMultiValue) {
+ throw new Error(
+ this.name + ': does not not support mulitValue.\n' +
+ 'override isMultiValue'
+ );
+ }
+
+ let len = values.length;
+ let i = 0;
+ this.removeAllValues();
+
+ if (len > 0 &&
+ typeof(values[0]) === 'object' &&
+ 'icaltype' in values[0]) {
+ this.resetType(values[0].icaltype);
+ }
+
+ if (this.isDecorated) {
+ for (; i < len; i++) {
+ this._setDecoratedValue(values[i], i);
+ }
+ } else {
+ for (; i < len; i++) {
+ this.jCal[VALUE_INDEX + i] = values[i];
+ }
+ }
+ }
+
+ /**
+ * Sets the current value of the property. If this is a multi-value
+ * property, all other values will be removed.
+ *
+ * @param {String|Object} value New property value.
+ */
+ setValue(value) {
+ this.removeAllValues();
+ if (typeof(value) === 'object' && 'icaltype' in value) {
+ this.resetType(value.icaltype);
+ }
+
+ if (this.isDecorated) {
+ this._setDecoratedValue(value, 0);
+ } else {
+ this.jCal[VALUE_INDEX] = value;
+ }
+ }
+
+ /**
+ * Returns the Object representation of this component. The returned object
+ * is a live jCal object and should be cloned if modified.
+ * @return {Object}
+ */
+ toJSON() {
+ return this.jCal;
+ }
+
+ /**
+ * The string representation of this component.
+ * @return {String}
+ */
+ toICALString() {
+ return ICALStringify.property(
+ this.jCal, this._designSet, true
+ );
+ }
+}
+export default Property;
diff --git a/cron/scripts/node_modules/ical.js/lib/ical/recur.js b/cron/scripts/node_modules/ical.js/lib/ical/recur.js
new file mode 100644
index 0000000..79c88ad
--- /dev/null
+++ b/cron/scripts/node_modules/ical.js/lib/ical/recur.js
@@ -0,0 +1,576 @@
+/* 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 RecurIterator from "./recur_iterator.js";
+import Time from "./time.js";
+import design from "./design.js";
+import { strictParseInt, clone } from "./helpers.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").weekDay} weekDay
+ * Imports the 'weekDay' type from the "types.js" module
+ * @typedef {import("./types.js").frequencyValues} frequencyValues
+ * Imports the 'frequencyValues' type from the "types.js" module
+ */
+
+const VALID_DAY_NAMES = /^(SU|MO|TU|WE|TH|FR|SA)$/;
+const VALID_BYDAY_PART = /^([+-])?(5[0-3]|[1-4][0-9]|[1-9])?(SU|MO|TU|WE|TH|FR|SA)$/;
+const DOW_MAP = {
+ SU: Time.SUNDAY,
+ MO: Time.MONDAY,
+ TU: Time.TUESDAY,
+ WE: Time.WEDNESDAY,
+ TH: Time.THURSDAY,
+ FR: Time.FRIDAY,
+ SA: Time.SATURDAY
+};
+
+const REVERSE_DOW_MAP = Object.fromEntries(Object.entries(DOW_MAP).map(entry => entry.reverse()));
+
+const ALLOWED_FREQ = ['SECONDLY', 'MINUTELY', 'HOURLY',
+ 'DAILY', 'WEEKLY', 'MONTHLY', 'YEARLY'];
+
+/**
+ * This class represents the "recur" value type, used for example by RRULE. It provides methods to
+ * calculate occurrences among others.
+ *
+ * @memberof ICAL
+ */
+class Recur {
+ /**
+ * Creates a new {@link ICAL.Recur} instance from the passed string.
+ *
+ * @param {String} string The string to parse
+ * @return {Recur} The created recurrence instance
+ */
+ static fromString(string) {
+ let data = this._stringToData(string, false);
+ return new Recur(data);
+ }
+
+ /**
+ * Creates a new {@link ICAL.Recur} instance using members from the passed
+ * data object.
+ *
+ * @param {Object} aData An object with members of the recurrence
+ * @param {frequencyValues=} aData.freq The frequency value
+ * @param {Number=} aData.interval The INTERVAL value
+ * @param {weekDay=} aData.wkst The week start value
+ * @param {Time=} aData.until The end of the recurrence set
+ * @param {Number=} aData.count The number of occurrences
+ * @param {Array.=} aData.bysecond The seconds for the BYSECOND part
+ * @param {Array.=} aData.byminute The minutes for the BYMINUTE part
+ * @param {Array.=} aData.byhour The hours for the BYHOUR part
+ * @param {Array.=} aData.byday The BYDAY values
+ * @param {Array.=} aData.bymonthday The days for the BYMONTHDAY part
+ * @param {Array.=} aData.byyearday The days for the BYYEARDAY part
+ * @param {Array.=} aData.byweekno The weeks for the BYWEEKNO part
+ * @param {Array.=} aData.bymonth The month for the BYMONTH part
+ * @param {Array.=} aData.bysetpos The positionals for the BYSETPOS part
+ */
+ static fromData(aData) {
+ return new Recur(aData);
+ }
+
+ /**
+ * Converts a recurrence string to a data object, suitable for the fromData
+ * method.
+ *
+ * @private
+ * @param {String} string The string to parse
+ * @param {Boolean} fmtIcal If true, the string is considered to be an
+ * iCalendar string
+ * @return {Recur} The recurrence instance
+ */
+ static _stringToData(string, fmtIcal) {
+ let dict = Object.create(null);
+
+ // split is slower in FF but fast enough.
+ // v8 however this is faster then manual split?
+ let values = string.split(';');
+ let len = values.length;
+
+ for (let i = 0; i < len; i++) {
+ let parts = values[i].split('=');
+ let ucname = parts[0].toUpperCase();
+ let lcname = parts[0].toLowerCase();
+ let name = (fmtIcal ? lcname : ucname);
+ let value = parts[1];
+
+ if (ucname in partDesign) {
+ let partArr = value.split(',');
+ let partSet = new Set();
+
+ for (let part of partArr) {
+ partSet.add(partDesign[ucname](part));
+ }
+ partArr = [...partSet];
+
+ dict[name] = (partArr.length == 1 ? partArr[0] : partArr);
+ } else if (ucname in optionDesign) {
+ optionDesign[ucname](value, dict, fmtIcal);
+ } else {
+ // Don't swallow unknown values. Just set them as they are.
+ dict[lcname] = value;
+ }
+ }
+
+ return dict;
+ }
+
+ /**
+ * Convert an ical representation of a day (SU, MO, etc..)
+ * into a numeric value of that day.
+ *
+ * @param {String} string The iCalendar day name
+ * @param {weekDay=} aWeekStart
+ * The week start weekday, defaults to SUNDAY
+ * @return {Number} Numeric value of given day
+ */
+ static icalDayToNumericDay(string, aWeekStart) {
+ //XXX: this is here so we can deal
+ // with possibly invalid string values.
+ let firstDow = aWeekStart || Time.SUNDAY;
+ return ((DOW_MAP[string] - firstDow + 7) % 7) + 1;
+ }
+
+ /**
+ * Convert a numeric day value into its ical representation (SU, MO, etc..)
+ *
+ * @param {Number} num Numeric value of given day
+ * @param {weekDay=} aWeekStart
+ * The week start weekday, defaults to SUNDAY
+ * @return {String} The ICAL day value, e.g SU,MO,...
+ */
+ static numericDayToIcalDay(num, aWeekStart) {
+ //XXX: this is here so we can deal with possibly invalid number values.
+ // Also, this allows consistent mapping between day numbers and day
+ // names for external users.
+ let firstDow = aWeekStart || Time.SUNDAY;
+ let dow = (num + firstDow - Time.SUNDAY);
+ if (dow > 7) {
+ dow -= 7;
+ }
+ return REVERSE_DOW_MAP[dow];
+ }
+
+ /**
+ * Create a new instance of the Recur class.
+ *
+ * @param {Object} data An object with members of the recurrence
+ * @param {frequencyValues=} data.freq The frequency value
+ * @param {Number=} data.interval The INTERVAL value
+ * @param {weekDay=} data.wkst The week start value
+ * @param {Time=} data.until The end of the recurrence set
+ * @param {Number=} data.count The number of occurrences
+ * @param {Array.=} data.bysecond The seconds for the BYSECOND part
+ * @param {Array.=} data.byminute The minutes for the BYMINUTE part
+ * @param {Array.=} data.byhour The hours for the BYHOUR part
+ * @param {Array.=} data.byday The BYDAY values
+ * @param {Array.=} data.bymonthday The days for the BYMONTHDAY part
+ * @param {Array.=} data.byyearday The days for the BYYEARDAY part
+ * @param {Array.=} data.byweekno The weeks for the BYWEEKNO part
+ * @param {Array.=} data.bymonth The month for the BYMONTH part
+ * @param {Array.=} data.bysetpos The positionals for the BYSETPOS part
+ */
+ constructor(data) {
+ this.wrappedJSObject = this;
+ this.parts = {};
+
+ if (data && typeof(data) === 'object') {
+ this.fromData(data);
+ }
+ }
+
+ /**
+ * An object holding the BY-parts of the recurrence rule
+ * @memberof ICAL.Recur
+ * @typedef {Object} byParts
+ * @property {Array.=} BYSECOND The seconds for the BYSECOND part
+ * @property {Array.=} BYMINUTE The minutes for the BYMINUTE part
+ * @property {Array.=} BYHOUR The hours for the BYHOUR part
+ * @property {Array.=} BYDAY The BYDAY values
+ * @property {Array.=} BYMONTHDAY The days for the BYMONTHDAY part
+ * @property {Array.=} BYYEARDAY The days for the BYYEARDAY part
+ * @property {Array.=} BYWEEKNO The weeks for the BYWEEKNO part
+ * @property {Array.=} BYMONTH The month for the BYMONTH part
+ * @property {Array.