@wvdsh/sdk-js 1.3.25 → 1.3.27
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/dist/index.d.ts +30 -57
- package/dist/index.js +332 -170
- package/dist/inject.global.js +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -94,69 +94,30 @@ var WavedashManager = class {
|
|
|
94
94
|
};
|
|
95
95
|
|
|
96
96
|
// src/services/audio.ts
|
|
97
|
-
var WeakRefSet = class {
|
|
98
|
-
constructor() {
|
|
99
|
-
this.set = /* @__PURE__ */ new Set();
|
|
100
|
-
}
|
|
101
|
-
add(value) {
|
|
102
|
-
for (const ref of this.set) {
|
|
103
|
-
if (ref.deref() === value) return;
|
|
104
|
-
}
|
|
105
|
-
this.set.add(new WeakRef(value));
|
|
106
|
-
}
|
|
107
|
-
forEach(callback) {
|
|
108
|
-
for (const ref of this.set) {
|
|
109
|
-
const v = ref.deref();
|
|
110
|
-
if (v === void 0) this.set.delete(ref);
|
|
111
|
-
else callback(v);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
clear() {
|
|
115
|
-
this.set.clear();
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
97
|
var AudioManager = class extends WavedashManager {
|
|
119
98
|
constructor(sdk) {
|
|
120
99
|
super(sdk);
|
|
121
100
|
this._isMuted = false;
|
|
122
|
-
//
|
|
123
|
-
this.
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
//
|
|
128
|
-
this.
|
|
129
|
-
|
|
130
|
-
this.
|
|
131
|
-
this.originalWebKitAudioContext = null;
|
|
132
|
-
this.originalAudio = null;
|
|
133
|
-
this.originalMutedDescriptor = null;
|
|
134
|
-
this.originalPlay = null;
|
|
135
|
-
this.originalSpeak = null;
|
|
136
|
-
this.originalUtteranceVolumeDescriptor = null;
|
|
137
|
-
this.mutationObserver = null;
|
|
101
|
+
// One shim per frame we've attached to.
|
|
102
|
+
this.frames = /* @__PURE__ */ new Set();
|
|
103
|
+
// Per-iframe state so we can re-shim across navigations/swaps and tear down
|
|
104
|
+
// the right frame when an iframe is removed or re-navigates. Keyed on the
|
|
105
|
+
// Document (replaced on every navigation), not contentWindow (a stable
|
|
106
|
+
// WindowProxy that survives navigations and so can't reveal a realm change).
|
|
107
|
+
this.iframeBindings = /* @__PURE__ */ new WeakMap();
|
|
108
|
+
this.iframeLoadHandlers = /* @__PURE__ */ new Map();
|
|
109
|
+
this.boundIframes = /* @__PURE__ */ new Set();
|
|
138
110
|
this.handleMute = (data) => {
|
|
139
111
|
if (this._isMuted === data.isMuted) return;
|
|
140
112
|
this._isMuted = data.isMuted;
|
|
141
|
-
|
|
142
|
-
this.contexts.forEach((gain, ctx) => {
|
|
143
|
-
const now = ctx.currentTime;
|
|
144
|
-
gain.gain.cancelScheduledValues(now);
|
|
145
|
-
gain.gain.setValueAtTime(gain.gain.value, now);
|
|
146
|
-
gain.gain.linearRampToValueAtTime(target, now + 0.05);
|
|
147
|
-
});
|
|
148
|
-
const setMutedNative = this.originalMutedDescriptor?.set;
|
|
149
|
-
if (setMutedNative) {
|
|
150
|
-
this.elements.forEach((el) => {
|
|
151
|
-
const intended = this.intendedMuted.get(el) ?? false;
|
|
152
|
-
setMutedNative.call(el, this._isMuted ? true : intended);
|
|
153
|
-
});
|
|
154
|
-
}
|
|
113
|
+
this.frames.forEach((shim) => shim.applyMute(this._isMuted));
|
|
155
114
|
this.sdk.gameEventManager.notifyGame(WavedashEvents.MUTE_CHANGED, {
|
|
156
115
|
isMuted: this._isMuted
|
|
157
116
|
});
|
|
158
117
|
};
|
|
159
|
-
|
|
118
|
+
if (typeof window !== "undefined") {
|
|
119
|
+
this.attachWindow(window);
|
|
120
|
+
}
|
|
160
121
|
this.sdk.iframeMessenger.addEventListener(
|
|
161
122
|
IFRAME_MESSAGE_TYPE.MUTE_CHANGED,
|
|
162
123
|
this.handleMute
|
|
@@ -190,10 +151,148 @@ var AudioManager = class extends WavedashManager {
|
|
|
190
151
|
);
|
|
191
152
|
return response.success;
|
|
192
153
|
}
|
|
154
|
+
/** Shim a window we can reach. Same-origin only (cross-origin access throws). */
|
|
155
|
+
attachWindow(win) {
|
|
156
|
+
try {
|
|
157
|
+
void win.document;
|
|
158
|
+
} catch {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const shim = new AudioFrameShim(this, win);
|
|
162
|
+
this.frames.add(shim);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Start tracking an iframe: attach now (already-loaded frames) and on every
|
|
166
|
+
* `load` (about:blank → game, and later src swaps). Idempotent.
|
|
167
|
+
*/
|
|
168
|
+
bindIframe(iframe) {
|
|
169
|
+
if (!this.boundIframes.has(iframe)) {
|
|
170
|
+
this.boundIframes.add(iframe);
|
|
171
|
+
const onLoad = () => this.attachIframe(iframe);
|
|
172
|
+
this.iframeLoadHandlers.set(iframe, onLoad);
|
|
173
|
+
iframe.addEventListener("load", onLoad);
|
|
174
|
+
}
|
|
175
|
+
this.attachIframe(iframe);
|
|
176
|
+
}
|
|
177
|
+
/** Stop tracking an iframe and tear down its frame (iframe removed from DOM). */
|
|
178
|
+
unbindIframe(iframe) {
|
|
179
|
+
const handler = this.iframeLoadHandlers.get(iframe);
|
|
180
|
+
if (handler) {
|
|
181
|
+
iframe.removeEventListener("load", handler);
|
|
182
|
+
this.iframeLoadHandlers.delete(iframe);
|
|
183
|
+
}
|
|
184
|
+
this.boundIframes.delete(iframe);
|
|
185
|
+
this.teardownFrame(iframe);
|
|
186
|
+
}
|
|
193
187
|
/**
|
|
194
|
-
*
|
|
195
|
-
*
|
|
188
|
+
* Install (or re-install) a shim for an iframe's current document. No-ops
|
|
189
|
+
* while not yet navigated or already shimmed; replaces the previous shim when
|
|
190
|
+
* the iframe navigates to a fresh document, and drops it when it goes
|
|
191
|
+
* cross-origin (we can no longer reach it).
|
|
196
192
|
*/
|
|
193
|
+
attachIframe(iframe) {
|
|
194
|
+
let win = null;
|
|
195
|
+
let doc = null;
|
|
196
|
+
try {
|
|
197
|
+
const cw = iframe.contentWindow;
|
|
198
|
+
if (cw) {
|
|
199
|
+
doc = cw.document;
|
|
200
|
+
win = cw;
|
|
201
|
+
}
|
|
202
|
+
} catch {
|
|
203
|
+
}
|
|
204
|
+
if (!win || !doc) {
|
|
205
|
+
this.teardownFrame(iframe);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const existing = this.iframeBindings.get(iframe);
|
|
209
|
+
if (existing) {
|
|
210
|
+
if (existing.doc === doc) return;
|
|
211
|
+
this.teardownFrame(iframe);
|
|
212
|
+
}
|
|
213
|
+
const shim = new AudioFrameShim(this, win);
|
|
214
|
+
this.frames.add(shim);
|
|
215
|
+
this.iframeBindings.set(iframe, { doc, shim });
|
|
216
|
+
if (this._isMuted) shim.applyMute(true);
|
|
217
|
+
}
|
|
218
|
+
/** Remove and uninstall the shim bound to an iframe's (previous) document. */
|
|
219
|
+
teardownFrame(iframe) {
|
|
220
|
+
const binding = this.iframeBindings.get(iframe);
|
|
221
|
+
if (!binding) return;
|
|
222
|
+
this.frames.delete(binding.shim);
|
|
223
|
+
binding.shim.uninstall();
|
|
224
|
+
this.iframeBindings.delete(iframe);
|
|
225
|
+
}
|
|
226
|
+
destroy() {
|
|
227
|
+
this.sdk.iframeMessenger.removeEventListener(
|
|
228
|
+
IFRAME_MESSAGE_TYPE.MUTE_CHANGED,
|
|
229
|
+
this.handleMute
|
|
230
|
+
);
|
|
231
|
+
this.boundIframes.forEach((iframe) => {
|
|
232
|
+
const handler = this.iframeLoadHandlers.get(iframe);
|
|
233
|
+
if (handler) iframe.removeEventListener("load", handler);
|
|
234
|
+
});
|
|
235
|
+
this.boundIframes.clear();
|
|
236
|
+
this.iframeLoadHandlers.clear();
|
|
237
|
+
this.frames.forEach((shim) => shim.uninstall());
|
|
238
|
+
this.frames.clear();
|
|
239
|
+
super.destroy();
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
var AudioFrameShim = class {
|
|
243
|
+
constructor(manager, win) {
|
|
244
|
+
this.contexts = /* @__PURE__ */ new Map();
|
|
245
|
+
// Tracked media elements + the game's intended muted value (what it last set).
|
|
246
|
+
this.elements = new WeakRefSet();
|
|
247
|
+
this.intendedMuted = /* @__PURE__ */ new WeakMap();
|
|
248
|
+
// Utterances + the game's intended volume.
|
|
249
|
+
this.intendedUtteranceVolume = /* @__PURE__ */ new WeakMap();
|
|
250
|
+
// Child iframes discovered in this frame's document. Tracked so we can
|
|
251
|
+
// cascade-unbind them when this frame is torn down — their own removal events
|
|
252
|
+
// never fire when the containing document is discarded wholesale.
|
|
253
|
+
this.boundChildren = /* @__PURE__ */ new Set();
|
|
254
|
+
// Originals, restored on uninstall.
|
|
255
|
+
this.originalAudioContext = null;
|
|
256
|
+
this.originalWebKitAudioContext = null;
|
|
257
|
+
this.originalAudio = null;
|
|
258
|
+
this.originalMutedDescriptor = null;
|
|
259
|
+
this.originalPlay = null;
|
|
260
|
+
this.originalSpeak = null;
|
|
261
|
+
this.originalUtteranceVolumeDescriptor = null;
|
|
262
|
+
this.mutationObserver = null;
|
|
263
|
+
this.manager = manager;
|
|
264
|
+
this.win = win;
|
|
265
|
+
this.doc = win.document ?? null;
|
|
266
|
+
this.installShims();
|
|
267
|
+
}
|
|
268
|
+
/** Push the current mute state onto everything this frame is tracking. */
|
|
269
|
+
applyMute(isMuted) {
|
|
270
|
+
const target = isMuted ? 0 : 1;
|
|
271
|
+
this.contexts.forEach((gain, ctx) => {
|
|
272
|
+
const now = ctx.currentTime;
|
|
273
|
+
gain.gain.cancelScheduledValues(now);
|
|
274
|
+
gain.gain.setValueAtTime(gain.gain.value, now);
|
|
275
|
+
gain.gain.linearRampToValueAtTime(target, now + 0.05);
|
|
276
|
+
});
|
|
277
|
+
const setMutedNative = this.originalMutedDescriptor?.set;
|
|
278
|
+
if (setMutedNative) {
|
|
279
|
+
this.elements.forEach((el) => {
|
|
280
|
+
const intended = this.intendedMuted.get(el) ?? false;
|
|
281
|
+
setMutedNative.call(el, isMuted ? true : intended);
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/** Hand a discovered child iframe to the manager, remembering it for teardown. */
|
|
286
|
+
bindChild(iframe) {
|
|
287
|
+
this.boundChildren.add(iframe);
|
|
288
|
+
this.manager.bindIframe(iframe);
|
|
289
|
+
}
|
|
290
|
+
/** Stop tracking a child iframe that was removed from this document. */
|
|
291
|
+
unbindChild(iframe) {
|
|
292
|
+
this.boundChildren.delete(iframe);
|
|
293
|
+
this.manager.unbindIframe(iframe);
|
|
294
|
+
}
|
|
295
|
+
/** Track a media element and silence it if currently muted. Idempotent. */
|
|
197
296
|
trackElement(el) {
|
|
198
297
|
if (this.intendedMuted.has(el)) return;
|
|
199
298
|
const getMuted = this.originalMutedDescriptor?.get;
|
|
@@ -201,139 +300,161 @@ var AudioManager = class extends WavedashManager {
|
|
|
201
300
|
const current = getMuted ? getMuted.call(el) : el.muted;
|
|
202
301
|
this.intendedMuted.set(el, current);
|
|
203
302
|
this.elements.add(el);
|
|
204
|
-
if (this.
|
|
303
|
+
if (this.manager.isMuted() && !current && setMuted) {
|
|
205
304
|
setMuted.call(el, true);
|
|
206
305
|
}
|
|
207
306
|
}
|
|
208
307
|
installShims() {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const OriginalAudio = window.Audio;
|
|
308
|
+
const win = this.win;
|
|
309
|
+
const doc = this.doc;
|
|
310
|
+
if (win.AudioContext) {
|
|
311
|
+
this.originalAudioContext = win.AudioContext;
|
|
312
|
+
win.AudioContext = this.shimAudioContextClass(win.AudioContext);
|
|
313
|
+
}
|
|
314
|
+
const w = win;
|
|
315
|
+
if (w.webkitAudioContext) {
|
|
316
|
+
this.originalWebKitAudioContext = w.webkitAudioContext;
|
|
317
|
+
w.webkitAudioContext = this.shimAudioContextClass(w.webkitAudioContext);
|
|
318
|
+
}
|
|
319
|
+
if (win.Audio) {
|
|
320
|
+
const OriginalAudio = win.Audio;
|
|
223
321
|
this.originalAudio = OriginalAudio;
|
|
224
|
-
((
|
|
322
|
+
((shim) => {
|
|
225
323
|
const Shimmed = function(src) {
|
|
226
324
|
const audio = new OriginalAudio(src);
|
|
227
|
-
|
|
325
|
+
shim.trackElement(audio);
|
|
228
326
|
return audio;
|
|
229
327
|
};
|
|
230
328
|
Shimmed.prototype = OriginalAudio.prototype;
|
|
231
|
-
|
|
329
|
+
win.Audio = Shimmed;
|
|
232
330
|
})(this);
|
|
233
331
|
}
|
|
234
|
-
if (
|
|
235
|
-
|
|
332
|
+
if (doc) {
|
|
333
|
+
const HTMLMediaElementCtor = win.HTMLMediaElement;
|
|
334
|
+
const HTMLIFrameElementCtor = win.HTMLIFrameElement;
|
|
335
|
+
const HTMLElementCtor = win.HTMLElement;
|
|
336
|
+
doc.querySelectorAll("audio, video").forEach((el) => {
|
|
236
337
|
this.trackElement(el);
|
|
237
338
|
});
|
|
238
|
-
|
|
339
|
+
doc.querySelectorAll("iframe").forEach((el) => {
|
|
340
|
+
this.bindChild(el);
|
|
341
|
+
});
|
|
342
|
+
this.mutationObserver = new win.MutationObserver((mutations) => {
|
|
239
343
|
for (const m of mutations) {
|
|
240
344
|
m.addedNodes.forEach((node) => {
|
|
241
|
-
if (node instanceof
|
|
345
|
+
if (node instanceof HTMLMediaElementCtor) {
|
|
242
346
|
this.trackElement(node);
|
|
243
|
-
} else if (node instanceof
|
|
244
|
-
|
|
245
|
-
|
|
347
|
+
} else if (node instanceof HTMLIFrameElementCtor) {
|
|
348
|
+
this.bindChild(node);
|
|
349
|
+
} else if (node instanceof HTMLElementCtor) {
|
|
350
|
+
const el = node;
|
|
351
|
+
el.querySelectorAll("audio, video").forEach((m2) => {
|
|
352
|
+
this.trackElement(m2);
|
|
353
|
+
});
|
|
354
|
+
el.querySelectorAll("iframe").forEach((f) => {
|
|
355
|
+
this.bindChild(f);
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
m.removedNodes.forEach((node) => {
|
|
360
|
+
if (node instanceof HTMLIFrameElementCtor) {
|
|
361
|
+
this.unbindChild(node);
|
|
362
|
+
} else if (node instanceof HTMLElementCtor) {
|
|
363
|
+
node.querySelectorAll("iframe").forEach((f) => {
|
|
364
|
+
this.unbindChild(f);
|
|
246
365
|
});
|
|
247
366
|
}
|
|
248
367
|
});
|
|
249
368
|
}
|
|
250
369
|
});
|
|
251
|
-
this.mutationObserver.observe(
|
|
370
|
+
this.mutationObserver.observe(doc.documentElement, {
|
|
252
371
|
childList: true,
|
|
253
372
|
subtree: true
|
|
254
373
|
});
|
|
255
374
|
}
|
|
256
|
-
this.originalMutedDescriptor = Object.getOwnPropertyDescriptor(
|
|
375
|
+
this.originalMutedDescriptor = Object.getOwnPropertyDescriptor(
|
|
376
|
+
win.HTMLMediaElement.prototype,
|
|
377
|
+
"muted"
|
|
378
|
+
) ?? null;
|
|
257
379
|
const original = this.originalMutedDescriptor;
|
|
258
380
|
if (original?.get && original?.set) {
|
|
259
|
-
((
|
|
260
|
-
Object.defineProperty(HTMLMediaElement.prototype, "muted", {
|
|
381
|
+
((shim) => {
|
|
382
|
+
Object.defineProperty(win.HTMLMediaElement.prototype, "muted", {
|
|
261
383
|
configurable: true,
|
|
262
384
|
get() {
|
|
263
|
-
const intended =
|
|
385
|
+
const intended = shim.intendedMuted.get(this);
|
|
264
386
|
return intended !== void 0 ? intended : original.get.call(this);
|
|
265
387
|
},
|
|
266
388
|
set(value) {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
original.set.call(this, manager.
|
|
389
|
+
shim.intendedMuted.set(this, value);
|
|
390
|
+
shim.elements.add(this);
|
|
391
|
+
original.set.call(this, shim.manager.isMuted() ? true : value);
|
|
270
392
|
}
|
|
271
393
|
});
|
|
272
394
|
})(this);
|
|
273
395
|
}
|
|
274
|
-
const originalPlay = HTMLMediaElement.prototype.play;
|
|
396
|
+
const originalPlay = win.HTMLMediaElement.prototype.play;
|
|
275
397
|
this.originalPlay = originalPlay;
|
|
276
|
-
((
|
|
277
|
-
HTMLMediaElement.prototype.play = function() {
|
|
278
|
-
|
|
398
|
+
((shim) => {
|
|
399
|
+
win.HTMLMediaElement.prototype.play = function() {
|
|
400
|
+
shim.trackElement(this);
|
|
279
401
|
return originalPlay.call(this);
|
|
280
402
|
};
|
|
281
403
|
})(this);
|
|
282
404
|
this.shimSpeechSynthesis();
|
|
283
405
|
}
|
|
284
406
|
/**
|
|
285
|
-
* Shim `
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
* off (onstart/onend, synth.speaking/pending checks), so every call is
|
|
289
|
-
* delegated and silenced via volume instead. Volume is sampled at speak()
|
|
290
|
-
* time, so forcing the native value to 0 right before delegating silences
|
|
291
|
-
* anything spoken while muted; in-flight speech at the mute edge is
|
|
292
|
-
* deliberately left to finish (can't be softened mid-utterance, and
|
|
293
|
-
* cancel() would discard the pending queue).
|
|
407
|
+
* Shim `speechSynthesis`. We never swallow `speak()` (games sequence off its
|
|
408
|
+
* lifecycle) — instead we sample volume at call time and force it to 0 while
|
|
409
|
+
* muted. Speech already in flight at the mute edge is left to finish.
|
|
294
410
|
*/
|
|
295
411
|
shimSpeechSynthesis() {
|
|
296
|
-
|
|
412
|
+
const win = this.win;
|
|
413
|
+
if (!win.speechSynthesis || typeof win.SpeechSynthesisUtterance === "undefined") {
|
|
297
414
|
return;
|
|
298
415
|
}
|
|
299
416
|
this.originalUtteranceVolumeDescriptor = Object.getOwnPropertyDescriptor(
|
|
300
|
-
SpeechSynthesisUtterance.prototype,
|
|
417
|
+
win.SpeechSynthesisUtterance.prototype,
|
|
301
418
|
"volume"
|
|
302
419
|
) ?? null;
|
|
303
420
|
const volDesc = this.originalUtteranceVolumeDescriptor;
|
|
304
421
|
if (volDesc?.get && volDesc?.set) {
|
|
305
|
-
((
|
|
306
|
-
Object.defineProperty(
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
422
|
+
((shim) => {
|
|
423
|
+
Object.defineProperty(
|
|
424
|
+
win.SpeechSynthesisUtterance.prototype,
|
|
425
|
+
"volume",
|
|
426
|
+
{
|
|
427
|
+
configurable: true,
|
|
428
|
+
get() {
|
|
429
|
+
const intended = shim.intendedUtteranceVolume.get(this);
|
|
430
|
+
return intended !== void 0 ? intended : volDesc.get.call(this);
|
|
431
|
+
},
|
|
432
|
+
set(value) {
|
|
433
|
+
shim.intendedUtteranceVolume.set(this, value);
|
|
434
|
+
volDesc.set.call(this, value);
|
|
435
|
+
}
|
|
315
436
|
}
|
|
316
|
-
|
|
437
|
+
);
|
|
317
438
|
})(this);
|
|
318
439
|
}
|
|
319
|
-
const speechSynthesis =
|
|
440
|
+
const speechSynthesis = win.speechSynthesis;
|
|
320
441
|
const originalSpeak = speechSynthesis.speak;
|
|
321
442
|
this.originalSpeak = originalSpeak;
|
|
322
|
-
((
|
|
443
|
+
((shim) => {
|
|
323
444
|
speechSynthesis.speak = function(utterance) {
|
|
324
|
-
if (manager.
|
|
325
|
-
if (!
|
|
445
|
+
if (shim.manager.isMuted()) {
|
|
446
|
+
if (!shim.intendedUtteranceVolume.has(utterance)) {
|
|
326
447
|
const current = volDesc?.get ? volDesc.get.call(utterance) : utterance.volume;
|
|
327
|
-
|
|
448
|
+
shim.intendedUtteranceVolume.set(utterance, current);
|
|
328
449
|
}
|
|
329
450
|
if (volDesc?.set) volDesc.set.call(utterance, 0);
|
|
330
451
|
else utterance.volume = 0;
|
|
331
452
|
} else {
|
|
332
|
-
const intended =
|
|
453
|
+
const intended = shim.intendedUtteranceVolume.get(utterance);
|
|
333
454
|
if (intended !== void 0) {
|
|
334
455
|
if (volDesc?.set) volDesc.set.call(utterance, intended);
|
|
335
456
|
else utterance.volume = intended;
|
|
336
|
-
|
|
457
|
+
shim.intendedUtteranceVolume.delete(utterance);
|
|
337
458
|
}
|
|
338
459
|
}
|
|
339
460
|
return originalSpeak.call(speechSynthesis, utterance);
|
|
@@ -341,13 +462,13 @@ var AudioManager = class extends WavedashManager {
|
|
|
341
462
|
})(this);
|
|
342
463
|
}
|
|
343
464
|
shimAudioContextClass(Original) {
|
|
344
|
-
return /* @__PURE__ */ ((
|
|
465
|
+
return /* @__PURE__ */ ((shim) => class extends Original {
|
|
345
466
|
constructor(opts) {
|
|
346
467
|
super(opts);
|
|
347
468
|
const masterGain = this.createGain();
|
|
348
469
|
masterGain.connect(this.destination);
|
|
349
470
|
masterGain.gain.setValueAtTime(
|
|
350
|
-
manager.
|
|
471
|
+
shim.manager.isMuted() ? 0 : 1,
|
|
351
472
|
this.currentTime
|
|
352
473
|
);
|
|
353
474
|
Object.defineProperty(this, "destination", {
|
|
@@ -356,60 +477,101 @@ var AudioManager = class extends WavedashManager {
|
|
|
356
477
|
return masterGain;
|
|
357
478
|
}
|
|
358
479
|
});
|
|
359
|
-
|
|
480
|
+
shim.contexts.set(this, masterGain);
|
|
360
481
|
}
|
|
361
482
|
close() {
|
|
362
|
-
|
|
483
|
+
shim.contexts.delete(this);
|
|
363
484
|
return super.close();
|
|
364
485
|
}
|
|
365
486
|
})(this);
|
|
366
487
|
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
this.mutationObserver
|
|
488
|
+
/**
|
|
489
|
+
* Restore the globals we patched. Best-effort per statement: a frame reached
|
|
490
|
+
* through an iframe may have navigated away (globals gone) before teardown.
|
|
491
|
+
*/
|
|
492
|
+
uninstall() {
|
|
493
|
+
const win = this.win;
|
|
494
|
+
try {
|
|
495
|
+
if (this.mutationObserver) {
|
|
496
|
+
this.mutationObserver.disconnect();
|
|
497
|
+
this.mutationObserver = null;
|
|
498
|
+
}
|
|
499
|
+
} catch {
|
|
375
500
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
501
|
+
this.boundChildren.forEach((child) => this.manager.unbindIframe(child));
|
|
502
|
+
this.boundChildren.clear();
|
|
503
|
+
const restore = (fn) => {
|
|
504
|
+
try {
|
|
505
|
+
fn();
|
|
506
|
+
} catch {
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
restore(() => {
|
|
510
|
+
if (this.originalAudioContext)
|
|
511
|
+
win.AudioContext = this.originalAudioContext;
|
|
512
|
+
});
|
|
513
|
+
restore(() => {
|
|
514
|
+
const w = win;
|
|
515
|
+
if (this.originalWebKitAudioContext && w.webkitAudioContext) {
|
|
516
|
+
w.webkitAudioContext = this.originalWebKitAudioContext;
|
|
379
517
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
518
|
+
});
|
|
519
|
+
restore(() => {
|
|
520
|
+
if (this.originalAudio) win.Audio = this.originalAudio;
|
|
521
|
+
});
|
|
522
|
+
restore(() => {
|
|
523
|
+
if (this.originalSpeak && win.speechSynthesis) {
|
|
524
|
+
win.speechSynthesis.speak = this.originalSpeak;
|
|
383
525
|
}
|
|
384
|
-
|
|
385
|
-
|
|
526
|
+
});
|
|
527
|
+
restore(() => {
|
|
528
|
+
if (this.originalUtteranceVolumeDescriptor && typeof win.SpeechSynthesisUtterance !== "undefined") {
|
|
529
|
+
Object.defineProperty(
|
|
530
|
+
win.SpeechSynthesisUtterance.prototype,
|
|
531
|
+
"volume",
|
|
532
|
+
this.originalUtteranceVolumeDescriptor
|
|
533
|
+
);
|
|
386
534
|
}
|
|
387
|
-
|
|
388
|
-
|
|
535
|
+
});
|
|
536
|
+
restore(() => {
|
|
537
|
+
if (this.originalPlay) {
|
|
538
|
+
win.HTMLMediaElement.prototype.play = this.originalPlay;
|
|
389
539
|
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
}
|
|
401
|
-
if (this.originalMutedDescriptor) {
|
|
402
|
-
Object.defineProperty(
|
|
403
|
-
HTMLMediaElement.prototype,
|
|
404
|
-
"muted",
|
|
405
|
-
this.originalMutedDescriptor
|
|
406
|
-
);
|
|
407
|
-
}
|
|
540
|
+
});
|
|
541
|
+
restore(() => {
|
|
542
|
+
if (this.originalMutedDescriptor) {
|
|
543
|
+
Object.defineProperty(
|
|
544
|
+
win.HTMLMediaElement.prototype,
|
|
545
|
+
"muted",
|
|
546
|
+
this.originalMutedDescriptor
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
});
|
|
408
550
|
this.contexts.clear();
|
|
409
551
|
this.elements.clear();
|
|
410
552
|
this.intendedMuted = /* @__PURE__ */ new WeakMap();
|
|
411
553
|
this.intendedUtteranceVolume = /* @__PURE__ */ new WeakMap();
|
|
412
|
-
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
var WeakRefSet = class {
|
|
557
|
+
constructor() {
|
|
558
|
+
this.set = /* @__PURE__ */ new Set();
|
|
559
|
+
}
|
|
560
|
+
add(value) {
|
|
561
|
+
for (const ref of this.set) {
|
|
562
|
+
if (ref.deref() === value) return;
|
|
563
|
+
}
|
|
564
|
+
this.set.add(new WeakRef(value));
|
|
565
|
+
}
|
|
566
|
+
forEach(callback) {
|
|
567
|
+
for (const ref of this.set) {
|
|
568
|
+
const v = ref.deref();
|
|
569
|
+
if (v === void 0) this.set.delete(ref);
|
|
570
|
+
else callback(v);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
clear() {
|
|
574
|
+
this.set.clear();
|
|
413
575
|
}
|
|
414
576
|
};
|
|
415
577
|
|
|
@@ -3328,9 +3490,9 @@ var PaidContentManager = class extends WavedashManager {
|
|
|
3328
3490
|
super(...arguments);
|
|
3329
3491
|
this.paywallOpen = false;
|
|
3330
3492
|
}
|
|
3331
|
-
async isEntitled(
|
|
3493
|
+
async isEntitled(contentIdentifier) {
|
|
3332
3494
|
const jwt = await this.sdk.ensureGameplayJwt();
|
|
3333
|
-
return readEntitlementsFromJwt(jwt).includes(
|
|
3495
|
+
return readEntitlementsFromJwt(jwt).includes(contentIdentifier);
|
|
3334
3496
|
}
|
|
3335
3497
|
async getEntitlements() {
|
|
3336
3498
|
const jwt = await this.sdk.ensureGameplayJwt();
|
|
@@ -4954,23 +5116,23 @@ var WavedashSDK = class extends EventTarget {
|
|
|
4954
5116
|
* doesn't actually unlock anything. Pair with triggerPaywall() to drive
|
|
4955
5117
|
* in-game UI.
|
|
4956
5118
|
*/
|
|
4957
|
-
async isEntitled(
|
|
5119
|
+
async isEntitled(contentIdentifier) {
|
|
4958
5120
|
return this.apiCall(
|
|
4959
5121
|
this.paidContentManager,
|
|
4960
5122
|
"isEntitled",
|
|
4961
|
-
[["
|
|
4962
|
-
|
|
5123
|
+
[["contentIdentifier", vString]],
|
|
5124
|
+
contentIdentifier
|
|
4963
5125
|
);
|
|
4964
5126
|
}
|
|
4965
5127
|
// Kept for backwards compatibility
|
|
4966
|
-
async isEntitled_EXPERIMENTAL(
|
|
4967
|
-
return this.isEntitled(
|
|
5128
|
+
async isEntitled_EXPERIMENTAL(contentIdentifier) {
|
|
5129
|
+
return this.isEntitled(contentIdentifier);
|
|
4968
5130
|
}
|
|
4969
5131
|
/**
|
|
4970
|
-
* Returns the full list of paid-content
|
|
5132
|
+
* Returns the full list of paid-content identifiers the player owns for this game.
|
|
4971
5133
|
* Reads the `entitlements` claim from the gameplay JWT — this is a UX hint,
|
|
4972
5134
|
* not a security check (see {@link isEntitled}). Useful
|
|
4973
|
-
* for access gating multiple items at once without a call per content
|
|
5135
|
+
* for access gating multiple items at once without a call per content identifier.
|
|
4974
5136
|
*/
|
|
4975
5137
|
async getEntitlements() {
|
|
4976
5138
|
return this.apiCall(this.paidContentManager, "getEntitlements", []);
|