@momentco-ai/moment-sdk 0.2.2-dev.17 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,81 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.3.0] - 2026-06-08
9
+
10
+ ### Added
11
+
12
+ - `brand-playlist` trigger type — subscribe to a brand's default playlist via `data-moment-trigger-type="brand-playlist"` and `teamSlug` only. Live resolves `defaultPlaylistId` server-side.
13
+ - `playlist` value on `MomentSubscriptionType` for OAuth popup forwarding.
14
+ - **`moment-sdk-examples` CLI** — `npx moment-sdk-examples` serves interactive demos from the published npm package (port 5051)
15
+ - `examples/` shipped in the npm tarball (hub, moment, collection, brand-schedule pages wired to **sdk-demo** fixtures)
16
+
17
+ ### Changed
18
+
19
+ - Brand-level trigger scoping now treats `brand-playlist` the same as `team`: moment, list, and ID attributes are ignored so the embed resolves the correct subscription resource.
20
+ - Examples use the Live URL baked into the installed SDK build (no dev/prod environment toggles in the UI)
21
+ - Replaced Vite-based examples dev server with a zero-dependency Node static server
22
+
23
+ ## [0.2.2] - 2026-06-03
24
+
25
+ ### Added
26
+
27
+ - Source-based moment resolution via `apiSourceId` / `externalEventId` on `MomentSdkOpenPayload`, `createMomentButton()`, and trigger data attributes `data-api-source-id` / `data-external-event-id`. When both are provided, Live resolves the moment server-side from the publisher's sync-subscription source instead of a slug.
28
+
29
+ ## [0.2.1] - 2026-05-13
30
+
31
+ ### Added
32
+
33
+ - Default `.moment-sync-trigger` button styles injected on SDK init so bare HTML embed snippets render as a minimal button without partner CSS. Styles use `:where()` so partner classes can override them.
34
+
35
+ ### Changed
36
+
37
+ - `createMomentButton()` now uses the shared trigger stylesheet instead of inline styles.
38
+
39
+ ## [0.2.0] - 2026-05-12
40
+
41
+ ### Added
42
+
43
+ - Modal UI refresh: rounded wrapper, responsive iframe sizing (auto-resize capped at 80vh), and a powered-by footer.
44
+ - `webcal://` link handling — the SDK opens Apple Calendar / ICS links in the parent window when requested by the embed iframe.
45
+
46
+ ### Changed
47
+
48
+ - **Breaking:** Renamed `triggerType` value `'schedule'` to `'team'` to align with backend `subscriptionType: 'team'` semantics for subscribing to all published moments for a brand/team. Update `data-moment-trigger-type` attributes and any explicit `triggerType: 'schedule'` passed to `open()` / `MomentSdkOpenPayload`.
49
+ - **Breaking:** Renamed the moment-slug embed attribute from `data-external-event-id` to `data-moment-slug`, and the corresponding payload field from `externalEventId` to `momentSlug` on `MomentSdkOpenPayload` and `createMomentButton()`. Update embed snippets and programmatic call sites accordingly.
50
+
51
+ > **Note:** `data-external-event-id` was reintroduced in 0.2.2 with a different meaning — it now pairs with `data-api-source-id` for source-based resolution, not as a moment slug alias.
52
+
53
+ ## [0.1.2] - 2026-04-07
54
+
55
+ ### Fixed
56
+
57
+ - Standardized embed popup bridge contracts on `triggerType` across SDK payloads and URL query params.
58
+ - Removed transitional `subscriptionType` fallback handling to align with the intentional `triggerType` protocol.
59
+ - Ensured modal iframe and OAuth popup URLs both pass `triggerType` consistently to Moment Live embed routes.
60
+
61
+ ## [0.1.0] - 2026-04-01
62
+
63
+ ### Added
64
+
65
+ - Initial SDK release
66
+ - `MomentSdk` class with modal iframe + popup bridge OAuth flow
67
+ - `createMomentButton()` helper for programmatic button creation
68
+ - Data attribute-based trigger binding (`.moment-sync-trigger`)
69
+ - Support for `moment`, `list`, and `team` trigger types
70
+ - Google Calendar and Outlook Calendar provider support
71
+ - `onSuccess`, `onError`, `onClose`, `onAnalytics` callbacks
72
+ - Programmatic `open()`, `close()`, `rebind()`, `destroy()` API
73
+ - UMD + ESM builds with TypeScript declarations
74
+ - Build-time environment configuration via `VITE_LIVE_BASE_URL`
75
+
76
+ [0.3.0]: https://github.com/Moment-Co/moment-sdk/compare/v0.2.2...v0.3.0
77
+ [0.2.2]: https://github.com/Moment-Co/moment-sdk/compare/v0.2.1...v0.2.2
78
+ [0.2.1]: https://github.com/Moment-Co/moment-sdk/compare/v0.2.0...v0.2.1
79
+ [0.2.0]: https://github.com/Moment-Co/moment-sdk/compare/v0.1.2...v0.2.0
80
+ [0.1.2]: https://github.com/Moment-Co/moment-sdk/compare/v0.1.0...v0.1.2
81
+ [0.1.0]: https://github.com/Moment-Co/moment-sdk/releases/tag/v0.1.0
package/README.md CHANGED
@@ -1,42 +1,79 @@
1
1
  # Moment SDK
2
2
 
3
- Embeddable calendar sync widget for external team websites. Zero runtime dependencies.
3
+ Embeddable calendar sync widget for partner websites. Fans click a trigger on your site, pick Google Calendar or Outlook, and subscribe to games, lists, or full team schedules — without leaving your page.
4
4
 
5
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5
+ Zero runtime dependencies. Works via a single `<script>` tag or npm import.
6
+
7
+ [License: MIT](LICENSE)
8
+ [npm version](https://www.npmjs.com/package/@momentco-ai/moment-sdk)
9
+
10
+ ---
11
+
12
+ ## What ships on npm
13
+
14
+ | Path | Format | Use case |
15
+ | --------------------- | ----------------------- | --------------------------------------------------------------------- |
16
+ | `dist/moment-sdk.js` | IIFE (UMD-style) | `<script>` tag — exposes `MomentSdk` and `createMomentButton` globals |
17
+ | `dist/moment-sdk.mjs` | ESM | Bundler / `import` |
18
+ | `dist/types/` | TypeScript declarations | Type-safe integrations |
19
+ | `examples/` | Static HTML + CLI | Interactive demos (`npx moment-sdk-examples`) |
20
+
21
+ Pin a version in production (see [Installation](#installation)).
6
22
 
7
23
  ---
8
24
 
9
25
  ## Installation
10
26
 
11
- Add the SDK via a script tag:
27
+ ### Script tag (recommended for partner sites)
28
+
29
+ Load from a public CDN and **pin the version**:
30
+
31
+ ```html
32
+ <script src="https://cdn.jsdelivr.net/npm/@momentco-ai/moment-sdk@0.3.0"></script>
33
+ ```
34
+
35
+ Alternatives:
12
36
 
13
37
  ```html
14
- <script src="https://unpkg.com/@momentco-ai/moment-sdk"></script>
38
+ <!-- unpkg -->
39
+ <script src="https://unpkg.com/@momentco-ai/moment-sdk@0.3.0"></script>
15
40
  ```
16
41
 
17
- Or via npm:
42
+ ### npm / bundler
18
43
 
19
44
  ```bash
20
45
  npm install @momentco-ai/moment-sdk
46
+ # or
47
+ pnpm add @momentco-ai/moment-sdk
48
+ ```
49
+
50
+ ```javascript
51
+ import { MomentSdk, createMomentButton } from '@momentco-ai/moment-sdk';
52
+
53
+ const sdk = new MomentSdk({
54
+ onSuccess: (result) => console.log('Synced!', result),
55
+ });
21
56
  ```
22
57
 
23
58
  ---
24
59
 
25
- ## Quick Start
60
+ ## Quick start
61
+
62
+ Copy this into any page. Replace `your-team-slug` and `your-event-slug` with values provided by Moment.
26
63
 
27
64
  ```html
28
- <!-- 1. Add trigger buttons with data attributes -->
65
+ <!-- 1. Trigger button -->
29
66
  <button
30
67
  class="moment-sync-trigger"
31
68
  data-moment-team-slug="your-team-slug"
32
- data-moment-slug="event-001"
69
+ data-moment-slug="your-event-slug"
33
70
  data-moment-trigger-type="moment"
34
71
  >
35
72
  Add to Calendar
36
73
  </button>
37
74
 
38
- <!-- 2. Load the SDK and initialize -->
39
- <script src="https://unpkg.com/@momentco-ai/moment-sdk"></script>
75
+ <!-- 2. Load SDK (pin version in production) -->
76
+ <script src="https://cdn.jsdelivr.net/npm/@momentco-ai/moment-sdk@0.3.0"></script>
40
77
  <script>
41
78
  const sdk = new MomentSdk({
42
79
  onSuccess: (result) => console.log('Synced!', result),
@@ -46,162 +83,162 @@ npm install @momentco-ai/moment-sdk
46
83
  </script>
47
84
  ```
48
85
 
49
- That's it — clicking the button opens a modal where users can sync the event to Google Calendar or Outlook.
50
-
51
- See [`examples/basic/`](./examples/basic/) for a complete working example.
86
+ Clicking the button opens a modal where the fan selects Google Calendar or Outlook and completes OAuth in a popup. Your callbacks fire when sync succeeds, fails, or the modal closes.
52
87
 
53
88
  ---
54
89
 
55
- ## Init Options
90
+ ## Trigger types
91
+
92
+ Set `data-moment-trigger-type` (or `triggerType` in `open()` / `createMomentButton()`):
93
+
94
+ | Type | Required attributes | Subscribes to |
95
+ | ------------------ | ------------------------------------------------- | ------------------------------------------------------ |
96
+ | `moment` (default) | `data-moment-team-slug` + `data-moment-slug` | One or more specific games/events |
97
+ | `list` | `data-moment-team-slug` + `data-moment-list-slug` | A curated list of moments |
98
+ | `team` | `data-moment-team-slug` | All published moments for the brand/team |
99
+ | `brand-playlist` | `data-moment-team-slug` | The brand's default playlist (dynamic schedule thread) |
56
100
 
57
- | Option | Type | Required | Description |
58
- | ----------------- | ------------------- | -------- | ------------------------------------------------------------------ |
59
- | `triggerSelector` | `string` | No | CSS selector for trigger elements. Default: `.moment-sync-trigger` |
60
- | `onSuccess` | `(payload) => void` | No | Called on successful calendar sync |
61
- | `onError` | `(payload) => void` | No | Called on sync failure |
62
- | `onClose` | `(payload) => void` | No | Called when modal closes (success, error, or cancel) |
63
- | `onAnalytics` | `(event) => void` | No | Called for every analytics event |
101
+ For `team` and `brand-playlist`, omit moment/list/ID and source-resolution attributes — only `data-moment-team-slug` is required.
64
102
 
65
103
  ---
66
104
 
67
- ## Data Attributes
105
+ ## Data attributes
68
106
 
69
107
  Place these on any element matching the trigger selector (default: `.moment-sync-trigger`):
70
108
 
71
- | Attribute | Required | Description |
72
- | -------------------------- | -------- | ------------------------------------------------------------ |
73
- | `data-moment-team-slug` | | Team/brand slug |
74
- | `data-moment-slug` | No | Brand-level moment slug (resolved to real ID by Moment) |
75
- | `data-moment-list-slug` | No | List slug (resolved to a list-level subscription by Moment) |
76
- | `data-moment-trigger-type` | No | `moment` (default), `list`, or `team` |
77
- | `data-moment-ids` | No | Comma-separated moment IDs (fallback if slugs don't resolve) |
78
- | `data-moment-game-id` | No | Alias for `data-moment-ids` |
79
- | `data-moment-calendar` | No | Pre-select provider: `google` or `outlook` |
109
+ | Attribute | Required | Description |
110
+ | -------------------------- | ------------ | ----------------------------------------------------------------- |
111
+ | `data-moment-team-slug` | Yes | Team/brand slug provided by Moment |
112
+ | `data-moment-slug` | For `moment` | Brand-level moment slug (resolved server-side) |
113
+ | `data-moment-list-slug` | For `list` | List slug |
114
+ | `data-moment-trigger-type` | No | `moment` (default), `list`, `team`, or `brand-playlist` |
115
+ | `data-moment-ids` | No | Comma-separated moment IDs (fallback if slugs don't resolve) |
116
+ | `data-moment-game-id` | No | Alias for `data-moment-ids` |
117
+ | `data-moment-calendar` | No | Pre-select provider: `google` or `outlook` |
118
+ | `data-api-source-id` | No | Sync-subscription source ID (pairs with `data-external-event-id`) |
119
+ | `data-external-event-id` | No | External event ID from your feed, resolved against the source ID |
120
+
121
+ When both `data-api-source-id` and `data-external-event-id` are set, Live resolves the moment server-side — useful when your CMS emits native event IDs instead of Moment slugs.
80
122
 
81
123
  ---
82
124
 
83
- ## Programmatic API
125
+ ## Init options
84
126
 
85
- You can also open the modal programmatically instead of relying on data attributes:
127
+ | Option | Type | Default | Description |
128
+ | ----------------- | ------------------- | ---------------------- | ---------------------------------------------------- |
129
+ | `triggerSelector` | `string` | `.moment-sync-trigger` | CSS selector for trigger elements |
130
+ | `onSuccess` | `(payload) => void` | — | Called on successful calendar sync |
131
+ | `onError` | `(payload) => void` | — | Called on sync failure |
132
+ | `onClose` | `(payload) => void` | — | Called when modal closes (success, error, or cancel) |
133
+ | `onAnalytics` | `(event) => void` | — | Called for every analytics event |
86
134
 
87
- ```html
88
- <script>
89
- const sdk = new MomentSdk({
90
- /* callbacks */
91
- });
92
-
93
- // Open modal
94
- sdk.open({
95
- teamSlug: 'your-team-slug',
96
- momentSlug: 'event-123',
97
- triggerType: 'moment',
98
- ids: ['moment-id-1'],
99
- calendar: 'google', // optional: skip provider selection
100
- });
101
-
102
- // Close modal
103
- sdk.close();
135
+ ---
104
136
 
105
- // Re-bind triggers after dynamic DOM updates
106
- sdk.rebind();
137
+ ## Programmatic API
107
138
 
108
- // Destroy instance entirely
109
- sdk.destroy();
110
- </script>
139
+ Open the modal without data attributes:
140
+
141
+ ```javascript
142
+ const sdk = new MomentSdk({
143
+ /* callbacks */
144
+ });
145
+
146
+ // Single moment
147
+ sdk.open({
148
+ teamSlug: 'your-team-slug',
149
+ momentSlug: 'your-event-slug',
150
+ triggerType: 'moment',
151
+ });
152
+
153
+ // Source-based resolution (CMS event ID)
154
+ sdk.open({
155
+ teamSlug: 'your-team-slug',
156
+ triggerType: 'moment',
157
+ apiSourceId: 'your-sync-source-id',
158
+ externalEventId: 'native-event-12345',
159
+ });
160
+
161
+ // Follow full team schedule
162
+ sdk.open({
163
+ teamSlug: 'your-team-slug',
164
+ triggerType: 'team',
165
+ });
166
+
167
+ // Follow default playlist
168
+ sdk.open({
169
+ teamSlug: 'your-team-slug',
170
+ triggerType: 'brand-playlist',
171
+ });
172
+
173
+ sdk.close(); // Close modal
174
+ sdk.rebind(); // Re-bind triggers after dynamic DOM updates
175
+ sdk.destroy(); // Tear down instance
111
176
  ```
112
177
 
113
178
  ---
114
179
 
115
180
  ## `createMomentButton()`
116
181
 
117
- Programmatically create a styled trigger button:
182
+ Build a trigger button in JavaScript:
118
183
 
119
- ```html
120
- <script>
121
- const btn = createMomentButton({
122
- teamSlug: 'your-team-slug',
123
- momentSlug: 'event-001',
124
- triggerType: 'moment',
125
- label: 'Add to Calendar',
126
- });
184
+ ```javascript
185
+ const btn = createMomentButton({
186
+ teamSlug: 'your-team-slug',
187
+ momentSlug: 'your-event-slug',
188
+ triggerType: 'moment',
189
+ label: 'Add to Calendar',
190
+ className: 'my-calendar-btn', // optional — partner CSS
191
+ });
127
192
 
128
- document.getElementById('container').appendChild(btn);
129
- </script>
193
+ document.getElementById('container').appendChild(btn);
130
194
  ```
131
195
 
132
- The button uses the same default `.moment-sync-trigger` styling as declarative HTML triggers. Override with CSS or pass a `className` option.
196
+ The button gets the `.moment-sync-trigger` class and the correct `data-moment-*` attributes. Style it with your own CSS or rely on the SDK's minimal default button styles.
133
197
 
134
- ### Styling, fonts, and labels
198
+ ### Styling
135
199
 
136
- You can customize trigger button appearance in a few ways:
200
+ - **Trigger buttons** fully themeable on your page. The SDK injects minimal defaults via `:where(.moment-sync-trigger)` so your classes win.
201
+ - **Modal UI** — hosted in a secure iframe on Moment Live; not styled by your page CSS.
137
202
 
138
203
  ```html
139
204
  <style>
140
205
  .my-calendar-btn {
141
- font-family:
142
- Inter,
143
- system-ui,
144
- -apple-system,
145
- sans-serif;
146
- font-size: 15px;
206
+ font-family: Inter, system-ui, sans-serif;
147
207
  font-weight: 600;
148
- letter-spacing: 0.01em;
149
208
  border-radius: 9999px;
150
209
  padding: 10px 18px;
151
- border: 1px solid #1e3a8a;
152
210
  background: #1d4ed8;
153
- color: #ffffff;
154
- }
155
-
156
- .my-calendar-btn:hover {
157
- background: #1e40af;
211
+ color: #fff;
212
+ border: none;
213
+ cursor: pointer;
158
214
  }
159
215
  </style>
160
-
161
- <script>
162
- const primaryBtn = createMomentButton({
163
- teamSlug: 'your-team-slug',
164
- momentSlug: 'event-001',
165
- triggerType: 'moment',
166
- label: 'Get Tickets + Calendar',
167
- className: 'my-calendar-btn',
168
- });
169
-
170
- document.getElementById('container').appendChild(primaryBtn);
171
- </script>
172
216
  ```
173
217
 
174
- You can also create plain HTML triggers — the SDK applies minimal default button styles automatically, or you can fully own styles/text with your own CSS classes:
218
+ Or use plain HTML triggers with your own classes:
175
219
 
176
220
  ```html
177
221
  <button
178
222
  class="my-calendar-btn moment-sync-trigger"
179
223
  data-moment-team-slug="your-team-slug"
180
- data-moment-slug="event-001"
224
+ data-moment-slug="your-event-slug"
181
225
  data-moment-trigger-type="moment"
182
226
  >
183
- Add This Match to My Calendar
227
+ Add This Game to My Calendar
184
228
  </button>
185
-
186
- <script src="https://unpkg.com/@momentco-ai/moment-sdk"></script>
187
- <script>
188
- new MomentSdk();
189
- </script>
190
229
  ```
191
230
 
192
- Note: these customization options apply to trigger buttons. The modal UI itself is hosted in a secure iframe and is not themed by host-page CSS.
193
-
194
231
  ---
195
232
 
196
- ## Analytics Events
233
+ ## Analytics events
197
234
 
198
- The `onAnalytics` callback receives events with these names:
235
+ The `onAnalytics` callback receives:
199
236
 
200
237
  | Event | When |
201
238
  | --------------------- | ----------------------------------- |
202
239
  | `embed.init` | SDK initialized |
203
- | `embed.trigger.click` | User clicked a trigger button |
204
- | `embed.open` | Modal iframe opened |
240
+ | `embed.trigger.click` | User clicked a trigger |
241
+ | `embed.open` | Modal opened |
205
242
  | `oauth.start` | OAuth popup requested |
206
243
  | `oauth.popup.blocked` | Browser blocked the popup |
207
244
  | `oauth.popup.closed` | User closed popup before completing |
@@ -211,37 +248,108 @@ The `onAnalytics` callback receives events with these names:
211
248
 
212
249
  ---
213
250
 
214
- ## Result Payload
251
+ ## Result payload
215
252
 
216
- All callbacks (`onSuccess`, `onError`, `onClose`) receive a result payload:
253
+ `onSuccess`, `onError`, and `onClose` receive:
217
254
 
218
255
  ```json
219
256
  {
220
257
  "source": "moment-live-embed",
221
258
  "type": "moment.embed.result",
222
- "result": "success | error | cancelled",
223
- "provider": "google | outlook",
224
- "triggerType": "moment | list | team",
259
+ "result": "success",
260
+ "provider": "google",
261
+ "triggerType": "moment",
225
262
  "momentIds": ["..."],
226
263
  "message": "optional details"
227
264
  }
228
265
  ```
229
266
 
267
+ `result` is one of `success`, `error`, or `cancelled`. `provider` is `google` or `outlook` when applicable. `triggerType` is `moment`, `list`, `team`, or `brand-playlist`.
268
+
230
269
  ---
231
270
 
232
- ## Examples
271
+ ## Integration patterns
272
+
273
+ ### Single game / event page
233
274
 
234
- See the [`examples/basic/`](./examples/basic/) directory for a complete standalone integration example.
275
+ ```html
276
+ <button
277
+ class="moment-sync-trigger"
278
+ data-moment-team-slug="your-team-slug"
279
+ data-moment-slug="your-moment-slug"
280
+ data-moment-trigger-type="moment"
281
+ >
282
+ Sync to Calendar
283
+ </button>
284
+ ```
285
+
286
+ ### CMS with native event IDs
287
+
288
+ When Moment provides a sync-subscription source ID for your feed:
289
+
290
+ ```html
291
+ <button
292
+ class="moment-sync-trigger"
293
+ data-moment-team-slug="your-team-slug"
294
+ data-moment-trigger-type="moment"
295
+ data-api-source-id="your-source-id"
296
+ data-external-event-id="0022400123"
297
+ >
298
+ Sync to Calendar
299
+ </button>
300
+ ```
301
+
302
+ ### Follow the full schedule
303
+
304
+ ```html
305
+ <button
306
+ class="moment-sync-trigger"
307
+ data-moment-team-slug="your-team-slug"
308
+ data-moment-trigger-type="team"
309
+ >
310
+ Sync Full Schedule
311
+ </button>
312
+ ```
313
+
314
+ ### Dynamic buttons (SPA / React / Vue)
315
+
316
+ After rendering new trigger elements, call `sdk.rebind()` so click handlers attach to the new DOM nodes.
235
317
 
236
318
  ---
237
319
 
238
- ## Contributing
320
+ ## Examples playground
321
+
322
+ After installing the package, run the bundled demos (no repo clone required):
323
+
324
+ ```bash
325
+ npx moment-sdk-examples
326
+ ```
327
+
328
+ Opens **http://localhost:5051** with moment, collection, and brand-schedule triggers wired to the **sdk-demo** fixture brand.
329
+
330
+ Repo maintainers: `pnpm examples` runs the same server from a local clone.
239
331
 
240
- See [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup and guidelines.
332
+ See [examples/README.md](./examples/README.md) for routes and fixture slugs.
333
+
334
+ ---
241
335
 
242
336
  ## Security
243
337
 
244
- See [SECURITY.md](./SECURITY.md) for reporting vulnerabilities.
338
+ OAuth tokens never touch your site — authorization happens on Moment Live's backend. The SDK validates `postMessage` origin on every iframe and popup message.
339
+
340
+ See [SECURITY.md](./SECURITY.md) for the full security model and vulnerability reporting.
341
+
342
+ ---
343
+
344
+ ## Changelog
345
+
346
+ See [CHANGELOG.md](./CHANGELOG.md) for version history and migration notes.
347
+
348
+ ---
349
+
350
+ ## Support
351
+
352
+ - **Email:** [support@momentco.ai](mailto:support@momentco.ai)
245
353
 
246
354
  ## License
247
355
 
@@ -1,4 +1,4 @@
1
- (function(f){"use strict";class w{popup=null;pollTimer=null;messageHandler=null;resultReceived=!1;opts;constructor(e){this.opts=e}open(e){this.cleanup(),this.resultReceived=!1;const t=this.buildPopupUrl(e),i=Math.round(window.screenX+(window.outerWidth-500)/2),r=Math.round(window.screenY+(window.outerHeight-700)/2);if(this.popup=window.open(t,"moment-oauth-popup",`width=500,height=700,left=${i},top=${r},menubar=no,toolbar=no,location=yes,status=no`),!this.popup||this.popup.closed){this.sendStatusToIframe("popup-blocked"),this.opts.onPopupBlocked();return}this.sendStatusToIframe("popup-opened"),this.startPolling(),this.messageHandler=n=>{if(n.origin!==this.opts.liveOrigin||n.source!==this.popup)return;const o=n.data;!o||typeof o!="object"||o.source!=="moment-live-embed"||o.type!=="moment.embed.result"||(this.resultReceived=!0,this.sendStatusToIframe("completed"),this.opts.iframeWindow&&this.opts.iframeWindow.postMessage(o,this.opts.liveOrigin),this.opts.onResult(o),this.cleanup())},window.addEventListener("message",this.messageHandler)}cleanup(){if(this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.popup&&!this.popup.closed)try{this.popup.close()}catch{}this.popup=null}buildPopupUrl(e){const t=window.location.origin,i=new URLSearchParams({provider:e.provider,teamSlug:e.teamSlug,ids:e.ids.join(","),triggerType:e.triggerType,returnOrigin:t});return e.subscriptionType&&i.set("subscriptionType",e.subscriptionType),`${this.opts.liveBaseUrl}/en/embed/oauth-popup?${i.toString()}`}startPolling(){this.pollTimer=setInterval(()=>{(!this.popup||this.popup.closed)&&(this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),this.resultReceived||(this.sendStatusToIframe("popup-closed"),this.opts.onPopupClosed()),this.cleanup())},500)}sendStatusToIframe(e){if(!this.opts.iframeWindow)return;const t={source:"moment-sdk",type:"moment.embed.oauth.status",status:e};this.opts.iframeWindow.postMessage(t,this.opts.liveOrigin)}}const v="moment-sdk-trigger-styles",T=`
1
+ (function(h){"use strict";class w{popup=null;pollTimer=null;messageHandler=null;resultReceived=!1;opts;constructor(e){this.opts=e}open(e){this.cleanup(),this.resultReceived=!1;const t=this.buildPopupUrl(e),i=Math.round(window.screenX+(window.outerWidth-500)/2),o=Math.round(window.screenY+(window.outerHeight-700)/2);if(this.popup=window.open(t,"moment-oauth-popup",`width=500,height=700,left=${i},top=${o},menubar=no,toolbar=no,location=yes,status=no`),!this.popup||this.popup.closed){this.sendStatusToIframe("popup-blocked"),this.opts.onPopupBlocked();return}this.sendStatusToIframe("popup-opened"),this.startPolling(),this.messageHandler=n=>{if(n.origin!==this.opts.liveOrigin||n.source!==this.popup)return;const r=n.data;!r||typeof r!="object"||r.source!=="moment-live-embed"||r.type!=="moment.embed.result"||(this.resultReceived=!0,this.sendStatusToIframe("completed"),this.opts.iframeWindow&&this.opts.iframeWindow.postMessage(r,this.opts.liveOrigin),this.opts.onResult(r),this.cleanup())},window.addEventListener("message",this.messageHandler)}cleanup(){if(this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.popup&&!this.popup.closed)try{this.popup.close()}catch{}this.popup=null}buildPopupUrl(e){const t=window.location.origin,i=new URLSearchParams({provider:e.provider,teamSlug:e.teamSlug,ids:e.ids.join(","),triggerType:e.triggerType,returnOrigin:t});return e.subscriptionType&&i.set("subscriptionType",e.subscriptionType),`${this.opts.liveBaseUrl}/en/embed/oauth-popup?${i.toString()}`}startPolling(){this.pollTimer=setInterval(()=>{(!this.popup||this.popup.closed)&&(this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),this.resultReceived||(this.sendStatusToIframe("popup-closed"),this.opts.onPopupClosed()),this.cleanup())},500)}sendStatusToIframe(e){if(!this.opts.iframeWindow)return;const t={source:"moment-sdk",type:"moment.embed.oauth.status",status:e};this.opts.iframeWindow.postMessage(t,this.opts.liveOrigin)}}const g="moment-sdk-trigger-styles",T=`
2
2
  :where(.moment-sync-trigger) {
3
3
  background-color: #1c1917;
4
4
  color: #ffffff;
@@ -22,4 +22,4 @@
22
22
  outline: 2px solid #1c1917;
23
23
  outline-offset: 2px;
24
24
  }
25
- `;function S(){if(typeof document>"u"||document.getElementById(v))return;const s=document.createElement("style");s.id=v,s.textContent=T,document.head.appendChild(s)}const E=".moment-sync-trigger",I="https://live.dev.momentco.ai";class b{opts;liveBaseUrl;liveOrigin;overlay=null;iframe=null;popupBridge=null;messageHandler=null;boundClickHandlers=new Map;activePayload=null;previousFocusedElement=null;previousBodyOverflow="";didManageBodyOverflow=!1;closeTimer=null;constructor(e){this.opts={triggerSelector:E,...e},this.liveBaseUrl=I;try{this.liveOrigin=new URL(this.liveBaseUrl).origin}catch{throw new Error(`[MomentSdk] Invalid VITE_LIVE_BASE_URL: "${this.liveBaseUrl}"`)}S(),this.bindTriggers(),this.emitAnalytics("embed.init")}open(e){this.emitAnalytics("embed.open",{teamSlug:e.teamSlug,triggerType:e.triggerType}),this.createModal(e)}close(){this.handleCancel()}rebind(){this.unbindTriggers(),this.bindTriggers()}destroy(){this.cleanup(),this.unbindTriggers()}bindTriggers(){document.querySelectorAll(this.opts.triggerSelector).forEach(t=>{const i=n=>{n.preventDefault(),this.handleTriggerClick(t)},r=this.boundClickHandlers.get(t);r&&t.removeEventListener("click",r),t.addEventListener("click",i),this.boundClickHandlers.set(t,i)})}unbindTriggers(){this.boundClickHandlers.forEach((e,t)=>{t.removeEventListener("click",e)}),this.boundClickHandlers.clear()}handleTriggerClick(e){const t=e.getAttribute("data-moment-team-slug")??"",i=e.getAttribute("data-moment-slug")??void 0,r=e.getAttribute("data-moment-list-slug")??void 0,n=e.getAttribute("data-moment-trigger-type");let o="moment";n!=null&&n!==""&&(n==="moment"||n==="list"||n==="team"?o=n:console.warn("[MomentSdk] Invalid data-moment-trigger-type, falling back to 'moment':",n));const d=e.getAttribute("data-moment-ids")??e.getAttribute("data-moment-game-id")??"",u=d?d.split(",").filter(Boolean):[],a=e.getAttribute("data-moment-calendar");let m;a!=null&&a!==""&&(a==="google"||a==="outlook"?m=a:console.warn("[MomentSdk] Invalid data-moment-calendar, ignoring value:",a));const l=e.getAttribute("data-api-source-id")??void 0,c=e.getAttribute("data-external-event-id")??void 0;if(!t){console.warn("[MomentSdk] Missing data-moment-team-slug on trigger element");return}const h=o==="team"?void 0:i,g=o==="team"?void 0:r,p=o==="team"?[]:u;this.emitAnalytics("embed.trigger.click",{teamSlug:t,triggerType:o,momentIds:p}),this.open({teamSlug:t,momentSlug:h,listSlug:g,triggerType:o,ids:p,calendar:m??void 0,apiSourceId:l,externalEventId:c})}createModal(e){this.cleanup(),this.previousFocusedElement=document.activeElement||null,this.activePayload=e;const t=this.buildIframeUrl(e);this.overlay=document.createElement("div"),this.overlay.setAttribute("role","presentation"),Object.assign(this.overlay.style,{position:"fixed",inset:"0",zIndex:"999999",display:"flex",alignItems:"center",justifyContent:"center",backgroundColor:"rgba(0, 0, 0, 0.5)",backdropFilter:"blur(2px)",padding:"16px"}),this.overlay.addEventListener("click",l=>{l.target===this.overlay&&this.handleCancel()});const i=l=>{l.key==="Escape"&&this.handleCancel()};document.addEventListener("keydown",i);const r=document.createElement("div");Object.assign(r.style,{position:"relative",width:"100%",maxWidth:"380px",borderRadius:"16px",overflow:"hidden",backgroundColor:"#000000",boxShadow:"0 25px 50px -12px rgba(0, 0, 0, 0.25)",display:"flex",flexDirection:"column"});const n=document.createElement("div");n.setAttribute("role","dialog"),n.setAttribute("aria-modal","true"),n.setAttribute("aria-label","Calendar sync modal"),n.tabIndex=-1,Object.assign(n.style,{position:"relative",width:"100%",maxHeight:"80vh",borderRadius:"12px",overflow:"auto",backgroundColor:"#ffffff"});const o=document.createElement("button");o.type="button",o.innerHTML='<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 1l12 12M13 1L1 13" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>',o.setAttribute("aria-label","Close"),Object.assign(o.style,{position:"absolute",top:"10px",right:"10px",zIndex:"10",border:"none",backgroundColor:"transparent",color:"#999",cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",width:"24px",height:"24px",padding:"0"}),o.addEventListener("mouseenter",()=>{o.style.color="#666"}),o.addEventListener("mouseleave",()=>{o.style.color="#999"}),o.addEventListener("click",()=>this.handleCancel()),this.iframe=document.createElement("iframe"),this.iframe.src=t,this.iframe.setAttribute("allow",""),this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-popups allow-forms"),this.iframe.setAttribute("scrolling","no"),Object.assign(this.iframe.style,{width:"100%",border:"none",display:"block",height:"420px",overflow:"hidden",transition:"height 0.15s ease"}),n.appendChild(o),n.appendChild(this.iframe);const d=document.createElement("div");Object.assign(d.style,{display:"flex",alignItems:"center",justifyContent:"center",gap:"6px",padding:"12px 0"});const u=document.createElement("span");u.textContent="Powered by",Object.assign(u.style,{fontFamily:"Inter, system-ui, -apple-system, sans-serif",fontSize:"12px",lineHeight:"16px",color:"#ffffff"});const a=document.createElement("img");a.alt="Moment",a.src=`${this.liveBaseUrl}/brand/moment-text-white.png`,Object.assign(a.style,{height:"auto",width:"67px"}),d.appendChild(u),d.appendChild(a),r.appendChild(n),r.appendChild(d),this.overlay.appendChild(r),document.body.appendChild(this.overlay);const m=l=>{if(l.key!=="Tab")return;const c=n.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');if(c.length===0){l.preventDefault(),n.focus();return}const h=c[0],g=c[c.length-1],p=document.activeElement;l.shiftKey&&p===h?(l.preventDefault(),g.focus()):!l.shiftKey&&p===g&&(l.preventDefault(),h.focus())};n.addEventListener("keydown",m),o.focus(),this.previousBodyOverflow=document.body.style.overflow,document.body.style.overflow="hidden",this.didManageBodyOverflow=!0,this.setupMessageListener(),this.overlay._escHandler=i,this.overlay._focusTrapHandler=m}buildIframeUrl(e){const t=new URLSearchParams;return t.set("teamSlug",e.teamSlug),e.momentSlug&&t.set("momentSlug",e.momentSlug),e.listSlug&&t.set("listSlug",e.listSlug),t.set("triggerType",e.triggerType),e.ids?.length&&t.set("ids",e.ids.join(",")),e.calendar&&t.set("calendar",e.calendar),e.apiSourceId&&t.set("subscriptionId",e.apiSourceId),e.externalEventId&&t.set("externalId",e.externalEventId),t.set("returnOrigin",window.location.origin),t.set("embed","1"),`${this.liveBaseUrl}/en/embed/sync?${t.toString()}`}setupMessageListener(){this.messageHandler=e=>{if(e.origin!==this.liveOrigin||e.source!==this.iframe?.contentWindow)return;const t=e.data;if(!(!t||typeof t!="object")){if(t.source==="moment-live-embed"&&t.type==="moment.embed.oauth.start"){const i=t;this.emitAnalytics("oauth.start",{provider:i.provider,teamSlug:i.teamSlug}),this.handleOAuthStart(i);return}if(t.source==="moment-live-embed"&&t.type==="moment.embed.resize"){const i=typeof t.height=="number"?t.height:0;if(i>0&&this.iframe){const r=Math.round(window.innerHeight*.8);this.iframe.style.height=`${Math.min(i,r)}px`}return}if(t.source==="moment-live-embed"&&t.type==="moment.embed.open.url"){const{url:i}=t;i&&typeof i=="string"&&i.startsWith("webcal://")&&window.open(i,"_blank");return}if(t.source==="moment-live-embed"&&t.type==="moment.embed.result"){const i=t;this.handleResult(i)}}},window.addEventListener("message",this.messageHandler)}handleOAuthStart(e){this.popupBridge&&(this.popupBridge.cleanup(),this.popupBridge=null),this.popupBridge=new w({liveBaseUrl:this.liveBaseUrl,liveOrigin:this.liveOrigin,iframeWindow:this.iframe?.contentWindow??null,onResult:t=>{this.handleResult(t)},onPopupBlocked:()=>{this.emitAnalytics("oauth.popup.blocked",{provider:e.provider})},onPopupClosed:()=>{this.emitAnalytics("oauth.popup.closed",{provider:e.provider})}}),this.popupBridge.open(e)}handleResult(e){if(e.result==="success")this.emitAnalytics("subscribe.success",{provider:e.provider,triggerType:e.triggerType,momentIds:e.momentIds}),this.opts.onSuccess?.(e);else if(e.result==="error")this.emitAnalytics("subscribe.error",{provider:e.provider,triggerType:e.triggerType,momentIds:e.momentIds}),this.opts.onError?.(e);else if(e.result==="cancelled")return;this.opts.onClose?.(e),this.emitAnalytics("embed.close"),this.closeTimer=setTimeout(()=>this.cleanup(),1500)}handleCancel(){this.emitAnalytics("embed.close");const t={source:"moment-live-embed",type:"moment.embed.result",result:"cancelled",triggerType:this.activePayload?.triggerType??"moment",momentIds:this.activePayload?.ids??[]};this.opts.onClose?.(t),this.cleanup()}cleanup(){if(this.closeTimer&&(clearTimeout(this.closeTimer),this.closeTimer=null),this.popupBridge&&(this.popupBridge.cleanup(),this.popupBridge=null),this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.overlay){const{_escHandler:e,_focusTrapHandler:t}=this.overlay;e&&document.removeEventListener("keydown",e);const i=this.overlay.firstElementChild;i&&t&&i.removeEventListener("keydown",t),this.overlay.remove(),this.overlay=null}this.iframe=null,this.activePayload=null,this.didManageBodyOverflow&&(document.body.style.overflow=this.previousBodyOverflow,this.didManageBodyOverflow=!1),this.previousFocusedElement&&typeof this.previousFocusedElement.focus=="function"&&this.previousFocusedElement.focus(),this.previousFocusedElement=null}emitAnalytics(e,t){this.opts.onAnalytics?.({event:e,timestamp:Date.now(),...t})}}function y(s){const e=document.createElement("button");return e.type="button",e.classList.add("moment-sync-trigger"),s.className&&s.className.split(" ").forEach(t=>{t&&e.classList.add(t)}),e.setAttribute("data-moment-team-slug",s.teamSlug),s.momentSlug&&e.setAttribute("data-moment-slug",s.momentSlug),s.listSlug&&e.setAttribute("data-moment-list-slug",s.listSlug),s.triggerType&&e.setAttribute("data-moment-trigger-type",s.triggerType),s.ids?.length&&e.setAttribute("data-moment-ids",s.ids.join(",")),s.calendar&&e.setAttribute("data-moment-calendar",s.calendar),s.apiSourceId&&e.setAttribute("data-api-source-id",s.apiSourceId),s.externalEventId&&e.setAttribute("data-external-event-id",s.externalEventId),e.textContent=s.label??"Add to Calendar",e}if(typeof window<"u"){const s=window;s.MomentSdk=b,s.createMomentButton=y}f.MomentSdk=b,f.createMomentButton=y,Object.defineProperty(f,Symbol.toStringTag,{value:"Module"})})(this.MomentSdk=this.MomentSdk||{});
25
+ `;function S(){if(typeof document>"u"||document.getElementById(g))return;const s=document.createElement("style");s.id=g,s.textContent=T,document.head.appendChild(s)}const E=".moment-sync-trigger",x="https://live.momentco.ai";class f{opts;liveBaseUrl;liveOrigin;overlay=null;iframe=null;popupBridge=null;messageHandler=null;boundClickHandlers=new Map;activePayload=null;previousFocusedElement=null;previousBodyOverflow="";didManageBodyOverflow=!1;closeTimer=null;constructor(e){this.opts={triggerSelector:E,...e},this.liveBaseUrl=x;try{this.liveOrigin=new URL(this.liveBaseUrl).origin}catch{throw new Error(`[MomentSdk] Invalid VITE_LIVE_BASE_URL: "${this.liveBaseUrl}"`)}S(),this.bindTriggers(),this.emitAnalytics("embed.init")}open(e){const t=this.scopeOpenPayload(e);this.emitAnalytics("embed.open",{teamSlug:t.teamSlug,triggerType:t.triggerType}),this.createModal(t)}close(){this.handleCancel()}rebind(){this.unbindTriggers(),this.bindTriggers()}destroy(){this.cleanup(),this.unbindTriggers()}bindTriggers(){document.querySelectorAll(this.opts.triggerSelector).forEach(t=>{const i=n=>{n.preventDefault(),this.handleTriggerClick(t)},o=this.boundClickHandlers.get(t);o&&t.removeEventListener("click",o),t.addEventListener("click",i),this.boundClickHandlers.set(t,i)})}unbindTriggers(){this.boundClickHandlers.forEach((e,t)=>{t.removeEventListener("click",e)}),this.boundClickHandlers.clear()}handleTriggerClick(e){const t=e.getAttribute("data-moment-team-slug")??"",i=e.getAttribute("data-moment-slug")??void 0,o=e.getAttribute("data-moment-list-slug")??void 0,n=e.getAttribute("data-moment-trigger-type");let r="moment";n!=null&&n!==""&&(n==="moment"||n==="list"||n==="team"||n==="brand-playlist"?r=n:console.warn("[MomentSdk] Invalid data-moment-trigger-type, falling back to 'moment':",n));const d=e.getAttribute("data-moment-ids")??e.getAttribute("data-moment-game-id")??"",c=d?d.split(",").filter(Boolean):[],a=e.getAttribute("data-moment-calendar");let m;a!=null&&a!==""&&(a==="google"||a==="outlook"?m=a:console.warn("[MomentSdk] Invalid data-moment-calendar, ignoring value:",a));const l=e.getAttribute("data-api-source-id")??void 0,u=e.getAttribute("data-external-event-id")??void 0;if(!t){console.warn("[MomentSdk] Missing data-moment-team-slug on trigger element");return}const p=r==="team"||r==="brand-playlist";this.emitAnalytics("embed.trigger.click",{teamSlug:t,triggerType:r,momentIds:p?[]:c}),this.open({teamSlug:t,momentSlug:i,listSlug:o,triggerType:r,ids:c,calendar:m??void 0,apiSourceId:l,externalEventId:u})}scopeOpenPayload(e){return e.triggerType==="team"||e.triggerType==="brand-playlist"?{teamSlug:e.teamSlug,triggerType:e.triggerType,calendar:e.calendar}:e}createModal(e){this.cleanup(),this.previousFocusedElement=document.activeElement||null,this.activePayload=e;const t=this.buildIframeUrl(e);this.overlay=document.createElement("div"),this.overlay.setAttribute("role","presentation"),Object.assign(this.overlay.style,{position:"fixed",inset:"0",zIndex:"999999",display:"flex",alignItems:"center",justifyContent:"center",backgroundColor:"rgba(0, 0, 0, 0.5)",backdropFilter:"blur(2px)",padding:"16px"}),this.overlay.addEventListener("click",l=>{l.target===this.overlay&&this.handleCancel()});const i=l=>{l.key==="Escape"&&this.handleCancel()};document.addEventListener("keydown",i);const o=document.createElement("div");Object.assign(o.style,{position:"relative",width:"100%",maxWidth:"380px",borderRadius:"16px",overflow:"hidden",backgroundColor:"#000000",boxShadow:"0 25px 50px -12px rgba(0, 0, 0, 0.25)",display:"flex",flexDirection:"column"});const n=document.createElement("div");n.setAttribute("role","dialog"),n.setAttribute("aria-modal","true"),n.setAttribute("aria-label","Calendar sync modal"),n.tabIndex=-1,Object.assign(n.style,{position:"relative",width:"100%",maxHeight:"80vh",borderRadius:"12px",overflow:"auto",backgroundColor:"#ffffff"});const r=document.createElement("button");r.type="button",r.innerHTML='<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 1l12 12M13 1L1 13" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>',r.setAttribute("aria-label","Close"),Object.assign(r.style,{position:"absolute",top:"10px",right:"10px",zIndex:"10",border:"none",backgroundColor:"transparent",color:"#999",cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",width:"24px",height:"24px",padding:"0"}),r.addEventListener("mouseenter",()=>{r.style.color="#666"}),r.addEventListener("mouseleave",()=>{r.style.color="#999"}),r.addEventListener("click",()=>this.handleCancel()),this.iframe=document.createElement("iframe"),this.iframe.src=t,this.iframe.setAttribute("allow",""),this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-popups allow-forms"),this.iframe.setAttribute("scrolling","no"),Object.assign(this.iframe.style,{width:"100%",border:"none",display:"block",height:"420px",overflow:"hidden",transition:"height 0.15s ease"}),n.appendChild(r),n.appendChild(this.iframe);const d=document.createElement("div");Object.assign(d.style,{display:"flex",alignItems:"center",justifyContent:"center",gap:"6px",padding:"12px 0"});const c=document.createElement("span");c.textContent="Powered by",Object.assign(c.style,{fontFamily:"Inter, system-ui, -apple-system, sans-serif",fontSize:"12px",lineHeight:"16px",color:"#ffffff"});const a=document.createElement("img");a.alt="Moment",a.src=`${this.liveBaseUrl}/brand/moment-text-white.png`,Object.assign(a.style,{height:"auto",width:"67px"}),d.appendChild(c),d.appendChild(a),o.appendChild(n),o.appendChild(d),this.overlay.appendChild(o),document.body.appendChild(this.overlay);const m=l=>{if(l.key!=="Tab")return;const u=n.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');if(u.length===0){l.preventDefault(),n.focus();return}const p=u[0],b=u[u.length-1],y=document.activeElement;l.shiftKey&&y===p?(l.preventDefault(),b.focus()):!l.shiftKey&&y===b&&(l.preventDefault(),p.focus())};n.addEventListener("keydown",m),r.focus(),this.previousBodyOverflow=document.body.style.overflow,document.body.style.overflow="hidden",this.didManageBodyOverflow=!0,this.setupMessageListener(),this.overlay._escHandler=i,this.overlay._focusTrapHandler=m}buildIframeUrl(e){const t=new URLSearchParams;return t.set("teamSlug",e.teamSlug),e.momentSlug&&t.set("momentSlug",e.momentSlug),e.listSlug&&t.set("listSlug",e.listSlug),t.set("triggerType",e.triggerType),e.ids?.length&&t.set("ids",e.ids.join(",")),e.calendar&&t.set("calendar",e.calendar),e.apiSourceId&&t.set("subscriptionId",e.apiSourceId),e.externalEventId&&t.set("externalId",e.externalEventId),t.set("returnOrigin",window.location.origin),t.set("embed","1"),`${this.liveBaseUrl}/en/embed/sync?${t.toString()}`}setupMessageListener(){this.messageHandler=e=>{if(e.origin!==this.liveOrigin||e.source!==this.iframe?.contentWindow)return;const t=e.data;if(!(!t||typeof t!="object")){if(t.source==="moment-live-embed"&&t.type==="moment.embed.oauth.start"){const i=t;this.emitAnalytics("oauth.start",{provider:i.provider,teamSlug:i.teamSlug}),this.handleOAuthStart(i);return}if(t.source==="moment-live-embed"&&t.type==="moment.embed.resize"){const i=typeof t.height=="number"?t.height:0;if(i>0&&this.iframe){const o=Math.round(window.innerHeight*.8);this.iframe.style.height=`${Math.min(i,o)}px`}return}if(t.source==="moment-live-embed"&&t.type==="moment.embed.open.url"){const{url:i}=t;i&&typeof i=="string"&&i.startsWith("webcal://")&&window.open(i,"_blank");return}if(t.source==="moment-live-embed"&&t.type==="moment.embed.result"){const i=t;this.handleResult(i)}}},window.addEventListener("message",this.messageHandler)}handleOAuthStart(e){this.popupBridge&&(this.popupBridge.cleanup(),this.popupBridge=null),this.popupBridge=new w({liveBaseUrl:this.liveBaseUrl,liveOrigin:this.liveOrigin,iframeWindow:this.iframe?.contentWindow??null,onResult:t=>{this.handleResult(t)},onPopupBlocked:()=>{this.emitAnalytics("oauth.popup.blocked",{provider:e.provider})},onPopupClosed:()=>{this.emitAnalytics("oauth.popup.closed",{provider:e.provider})}}),this.popupBridge.open(e)}handleResult(e){if(e.result==="success")this.emitAnalytics("subscribe.success",{provider:e.provider,triggerType:e.triggerType,momentIds:e.momentIds}),this.opts.onSuccess?.(e);else if(e.result==="error")this.emitAnalytics("subscribe.error",{provider:e.provider,triggerType:e.triggerType,momentIds:e.momentIds}),this.opts.onError?.(e);else if(e.result==="cancelled")return;this.opts.onClose?.(e),this.emitAnalytics("embed.close"),this.closeTimer=setTimeout(()=>this.cleanup(),1500)}handleCancel(){this.emitAnalytics("embed.close");const t={source:"moment-live-embed",type:"moment.embed.result",result:"cancelled",triggerType:this.activePayload?.triggerType??"moment",momentIds:this.activePayload?.ids??[]};this.opts.onClose?.(t),this.cleanup()}cleanup(){if(this.closeTimer&&(clearTimeout(this.closeTimer),this.closeTimer=null),this.popupBridge&&(this.popupBridge.cleanup(),this.popupBridge=null),this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.overlay){const{_escHandler:e,_focusTrapHandler:t}=this.overlay;e&&document.removeEventListener("keydown",e);const i=this.overlay.firstElementChild;i&&t&&i.removeEventListener("keydown",t),this.overlay.remove(),this.overlay=null}this.iframe=null,this.activePayload=null,this.didManageBodyOverflow&&(document.body.style.overflow=this.previousBodyOverflow,this.didManageBodyOverflow=!1),this.previousFocusedElement&&typeof this.previousFocusedElement.focus=="function"&&this.previousFocusedElement.focus(),this.previousFocusedElement=null}emitAnalytics(e,t){this.opts.onAnalytics?.({event:e,timestamp:Date.now(),...t})}}function v(s){const e=document.createElement("button");return e.type="button",e.classList.add("moment-sync-trigger"),s.className&&s.className.split(" ").forEach(t=>{t&&e.classList.add(t)}),e.setAttribute("data-moment-team-slug",s.teamSlug),s.momentSlug&&e.setAttribute("data-moment-slug",s.momentSlug),s.listSlug&&e.setAttribute("data-moment-list-slug",s.listSlug),s.triggerType&&e.setAttribute("data-moment-trigger-type",s.triggerType),s.ids?.length&&e.setAttribute("data-moment-ids",s.ids.join(",")),s.calendar&&e.setAttribute("data-moment-calendar",s.calendar),s.apiSourceId&&e.setAttribute("data-api-source-id",s.apiSourceId),s.externalEventId&&e.setAttribute("data-external-event-id",s.externalEventId),e.textContent=s.label??"Add to Calendar",e}if(typeof window<"u"){const s=window;s.MomentSdk=f,s.createMomentButton=v}h.MomentSdk=f,h.createMomentButton=v,Object.defineProperty(h,Symbol.toStringTag,{value:"Module"})})(this.MomentSdk=this.MomentSdk||{});