-
Notifications
You must be signed in to change notification settings - Fork 3
/
shim.js
114 lines (98 loc) · 5 KB
/
shim.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// SPDX-License-Identifier: MIT
// Copyright © 2021 fvtt-lib-wrapper Rui Pinheiro
'use strict';
// A shim for the libWrapper library
export let libWrapper = undefined;
Hooks.once('init', () => {
// Check if the real module is already loaded - if so, use it
if(globalThis.libWrapper && !(globalThis.libWrapper.is_fallback ?? true)) {
libWrapper = globalThis.libWrapper;
return;
}
// Fallback implementation
libWrapper = class {
static get is_fallback() { return true };
static register(module, target, fn, type="MIXED", {chain=undefined}={}) {
const is_setter = target.endsWith('#set');
target = !is_setter ? target : target.slice(0, -4);
const split = target.split('.');
const fn_name = split.pop();
const root_nm = split.splice(0,1)[0];
const _eval = eval; // The browser doesn't expose all global variables (e.g. 'Game') inside globalThis, but it does to an eval. We copy it to a variable to have it run in global scope.
const obj = split.reduce((x,y)=>x[y], globalThis[root_nm] ?? _eval(root_nm));
let iObj = obj;
let descriptor = null;
while(iObj) {
descriptor = Object.getOwnPropertyDescriptor(iObj, fn_name);
if(descriptor) break;
iObj = Object.getPrototypeOf(iObj);
}
if(!descriptor || descriptor?.configurable === false) throw `libWrapper Shim: '${target}' does not exist, could not be found, or has a non-configurable descriptor.`;
let original = null;
const wrapper = (chain ?? type != 'OVERRIDE') ? function() { return fn.call(this, original.bind(this), ...arguments); } : function() { return fn.apply(this, arguments); };
if(!is_setter) {
if(descriptor.value) {
original = descriptor.value;
descriptor.value = wrapper;
}
else {
original = descriptor.get;
descriptor.get = wrapper;
}
}
else {
if(!descriptor.set) throw `libWrapper Shim: '${target}' does not have a setter`;
original = descriptor.set;
descriptor.set = wrapper;
}
descriptor.configurable = true;
Object.defineProperty(obj, fn_name, descriptor);
}
}
//************** USER CUSTOMIZABLE:
// Whether to warn GM that the fallback is being used
const WARN_FALLBACK = true;
// Set up the ready hook that shows the "libWrapper not installed" warning dialog
if(WARN_FALLBACK) {
//************** USER CUSTOMIZABLE:
// Module ID & Module Title - by default attempts to auto-detect, but you might want to hardcode your module ID and title here to avoid potential auto-detect issues
const [MODULE_ID, MODULE_TITLE] = (()=>{
const match = (import.meta?.url ?? Error().stack)?.match(/\/(worlds|systems|modules)\/(.+)(?=\/)/i);
if(match?.length !== 3) return [null,null];
const dirs = match[2].split('/');
if(match[1] === 'worlds') return dirs.find(n => n && game.world.id === n) ? [game.world.id, game.world.title] : [null,null];
if(match[1] === 'systems') return dirs.find(n => n && game.system.id === n) ? [game.system.id, game.system.data.title] : [null,null];
const id = dirs.find(n => n && game.modules.has(n));
return [id, game.modules.get(id)?.data?.title];
})();
if(!MODULE_ID || !MODULE_TITLE) {
console.error("libWrapper Shim: Could not auto-detect module ID and/or title. The libWrapper fallback warning dialog will be disabled.");
return;
}
Hooks.once('ready', () => {
//************** USER CUSTOMIZABLE:
// Title and message for the dialog shown when the real libWrapper is not installed.
const FALLBACK_MESSAGE_TITLE = MODULE_TITLE;
const FALLBACK_MESSAGE = `
<p><b>'${MODULE_TITLE}' depends on the 'libWrapper' module, which is not present.</b></p>
<p>A fallback implementation will be used, which increases the chance of compatibility issues with other modules.</p>
<small><p>'libWrapper' is a library which provides module developers with a simple way to modify core Foundry VTT code, while reducing the likelihood of conflict with other modules.</p>
<p>You can install it from the "Add-on Modules" tab in the <a href="javascript:game.shutDown()">Foundry VTT Setup</a>, from the <a href="https://foundryvtt.com/packages/lib-wrapper">Foundry VTT package repository</a>, or from <a href="https://github.com/ruipin/fvtt-lib-wrapper/">libWrapper's Github page</a>.</p></small>
`;
// Settings key used for the "Don't remind me again" setting
const DONT_REMIND_AGAIN_KEY = "libwrapper-dont-remind-again";
// Dialog code
console.warn(`${MODULE_TITLE}: libWrapper not present, using fallback implementation.`);
game.settings.register(MODULE_ID, DONT_REMIND_AGAIN_KEY, { name: '', default: false, type: Boolean, scope: 'world', config: false });
if(game.user.isGM && !game.settings.get(MODULE_ID, DONT_REMIND_AGAIN_KEY)) {
new Dialog({
title: FALLBACK_MESSAGE_TITLE,
content: FALLBACK_MESSAGE, buttons: {
ok: { icon: '<i class="fas fa-check"></i>', label: 'Understood' },
dont_remind: { icon: '<i class="fas fa-times"></i>', label: "Don't remind me again", callback: () => game.settings.set(MODULE_ID, DONT_REMIND_AGAIN_KEY, true) }
}
}).render(true);
}
});
}
});