@next2d/builder 0.1.7 → 0.1.10
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/cli.d.ts +59 -0
- package/dist/cli.js +141 -0
- package/dist/constants.d.ts +53 -0
- package/dist/constants.js +64 -0
- package/dist/context.d.ts +29 -0
- package/dist/context.js +19 -0
- package/dist/electron.d.ts +10 -0
- package/dist/electron.js +142 -0
- package/dist/index.js +18 -754
- package/dist/native.d.ts +27 -0
- package/dist/native.js +123 -0
- package/dist/utils.d.ts +27 -0
- package/dist/utils.js +40 -0
- package/dist/web.d.ts +18 -0
- package/dist/web.js +70 -0
- package/dist/xbox.d.ts +62 -0
- package/dist/xbox.js +426 -0
- package/package.json +12 -9
- package/templates/xbox/CMakeLists.txt +116 -11
- package/templates/xbox/README.md +34 -8
- package/templates/xbox/cmake/FetchDawn.cmake +47 -6
- package/templates/xbox/cmake/FindV8.cmake +15 -0
- package/templates/xbox/js/bootstrap.js +151 -28
- package/templates/xbox/js/selftest.js +740 -90
- package/templates/xbox/src/AssetLoader.cpp +115 -12
- package/templates/xbox/src/AssetLoader.h +4 -0
- package/templates/xbox/src/EmbeddedAssets.cpp +149 -0
- package/templates/xbox/src/EmbeddedAssets.h +60 -0
- package/templates/xbox/src/EventLoop.cpp +13 -1
- package/templates/xbox/src/EventLoop.h +12 -0
- package/templates/xbox/src/bindings/Audio.cpp +211 -31
- package/templates/xbox/src/bindings/Bindings.h +10 -0
- package/templates/xbox/src/bindings/Canvas.cpp +77 -2
- package/templates/xbox/src/bindings/Canvas2D.cpp +496 -43
- package/templates/xbox/src/bindings/Console.cpp +6 -1
- package/templates/xbox/src/bindings/DomShims.cpp +143 -0
- package/templates/xbox/src/bindings/EventTarget.cpp +11 -0
- package/templates/xbox/src/bindings/Fetch.cpp +2 -8
- package/templates/xbox/src/bindings/Image.cpp +243 -15
- package/templates/xbox/src/bindings/Polyfills.cpp +1226 -0
- package/templates/xbox/src/bindings/RasterCore.h +105 -1
- package/templates/xbox/src/bindings/Video.cpp +153 -10
- package/templates/xbox/src/bindings/webgpu/WebGPU.cpp +844 -18
- package/templates/xbox/src/bindings/webgpu/WebGPUCommon.h +7 -9
- package/templates/xbox/src/bindings/webgpu/WebGPUEnums.h +32 -0
- package/templates/xbox/src/gpu/DawnContext.cpp +245 -7
- package/templates/xbox/src/gpu/DawnContext.h +18 -0
- package/templates/xbox/src/main.cpp +459 -8
- package/templates/xbox/src/platform/AudioDecoder.cpp +121 -0
- package/templates/xbox/src/platform/AudioDecoder.h +18 -0
- package/templates/xbox/src/platform/AudioEngine.cpp +37 -6
- package/templates/xbox/src/platform/DecodeQueue.cpp +136 -0
- package/templates/xbox/src/platform/DecodeQueue.h +27 -0
- package/templates/xbox/src/platform/ImageDecoder.cpp +44 -0
- package/templates/xbox/src/platform/ImageDecoder.h +18 -0
- package/templates/xbox/src/platform/StbTextRasterizer.cpp +324 -0
- package/templates/xbox/src/platform/StbTextRasterizer.h +37 -0
- package/templates/xbox/src/platform/TextRasterizer.cpp +32 -0
- package/templates/xbox/src/platform/WicDecoder.cpp +19 -0
- package/templates/xbox/src/v8/CodeCache.h +223 -0
- package/templates/xbox/src/v8/V8Runtime.cpp +76 -11
- package/templates/xbox/src/v8/V8Util.h +79 -0
- package/templates/xbox/src/v8/WeakHandle.h +45 -0
- package/templates/xbox/src/worker/WorkerRuntime.cpp +432 -48
- package/templates/xbox/src/worker/WorkerRuntime.h +43 -13
- package/templates/xbox/tests/check_local.sh +7 -2
- package/templates/xbox/tests/gamecore_api_probe.cpp +172 -0
- package/templates/xbox/tests/raster_test.cpp +50 -0
- package/templates/xbox/tests/windows_platform_test.cpp +75 -3
- package/templates/xbox/third_party/LICENSES.md +14 -0
- package/templates/xbox/third_party/dr_mp3.h +5412 -0
- package/templates/xbox/third_party/dr_wav.h +9131 -0
- package/templates/xbox/third_party/stb_image.h +7988 -0
- package/templates/xbox/third_party/stb_truetype.h +5079 -0
- package/templates/xbox/third_party/stb_vorbis.c +5584 -0
|
@@ -0,0 +1,1226 @@
|
|
|
1
|
+
// 全コンテキスト (メイン + 各 worker) に必要な JS ポリフィルを評価する。
|
|
2
|
+
//
|
|
3
|
+
// bootstrap.js はメインコンテキストでのみ実行されるため、worker コンテキストには
|
|
4
|
+
// TextEncoder/TextDecoder 等が存在せず、Vite がバンドルした worker スクリプトが
|
|
5
|
+
// 評価時に ReferenceError で落ちる。ここで C++ から全コンテキストに注入する。
|
|
6
|
+
// (bootstrap.js 側は typeof ガード付きなので二重定義にはならない)
|
|
7
|
+
//
|
|
8
|
+
// NOTE: MSVC は 1 つの文字列リテラルが約 16KB までのため (C2026)、
|
|
9
|
+
// ポリフィルは独立した IIFE のセクション配列に分割して順に評価する。
|
|
10
|
+
#include "Bindings.h"
|
|
11
|
+
|
|
12
|
+
#include "v8/V8Util.h"
|
|
13
|
+
|
|
14
|
+
namespace next2d {
|
|
15
|
+
|
|
16
|
+
namespace {
|
|
17
|
+
|
|
18
|
+
// section 1: base (queueMicrotask)
|
|
19
|
+
const char kPolyfills1[] = R"JS(
|
|
20
|
+
(function (global) {
|
|
21
|
+
"use strict";
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
if (typeof global.queueMicrotask !== "function") {
|
|
25
|
+
global.queueMicrotask = function (callback) {
|
|
26
|
+
Promise.resolve().then(callback);
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
})(globalThis);
|
|
30
|
+
)JS";
|
|
31
|
+
|
|
32
|
+
// section 2: TextEncoder / location
|
|
33
|
+
const char kPolyfills2[] = R"JS(
|
|
34
|
+
(function (global) {
|
|
35
|
+
"use strict";
|
|
36
|
+
if (typeof global.TextEncoder === "undefined") {
|
|
37
|
+
global.TextEncoder = class TextEncoder {
|
|
38
|
+
get encoding() { return "utf-8"; }
|
|
39
|
+
encode(str) {
|
|
40
|
+
str = String(str);
|
|
41
|
+
const out = [];
|
|
42
|
+
for (let i = 0; i < str.length; i++) {
|
|
43
|
+
let code = str.charCodeAt(i);
|
|
44
|
+
if (code < 0x80) {
|
|
45
|
+
out.push(code);
|
|
46
|
+
} else if (code < 0x800) {
|
|
47
|
+
out.push(0xc0 | (code >> 6), 0x80 | (code & 0x3f));
|
|
48
|
+
} else if (code >= 0xd800 && code <= 0xdbff) {
|
|
49
|
+
const hi = code;
|
|
50
|
+
const lo = str.charCodeAt(++i);
|
|
51
|
+
code = 0x10000 + ((hi - 0xd800) << 10) + (lo - 0xdc00);
|
|
52
|
+
out.push(
|
|
53
|
+
0xf0 | (code >> 18),
|
|
54
|
+
0x80 | ((code >> 12) & 0x3f),
|
|
55
|
+
0x80 | ((code >> 6) & 0x3f),
|
|
56
|
+
0x80 | (code & 0x3f)
|
|
57
|
+
);
|
|
58
|
+
} else {
|
|
59
|
+
out.push(
|
|
60
|
+
0xe0 | (code >> 12),
|
|
61
|
+
0x80 | ((code >> 6) & 0x3f),
|
|
62
|
+
0x80 | (code & 0x3f)
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return new Uint8Array(out);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// location (worker を含む全コンテキストで参照可能にする。
|
|
72
|
+
// player の LoadUseCase が location.origin で URL を解決する)
|
|
73
|
+
if (typeof global.location === "undefined") {
|
|
74
|
+
global.location = {
|
|
75
|
+
"href": "app://local/",
|
|
76
|
+
"origin": "app://local",
|
|
77
|
+
"protocol": "app:",
|
|
78
|
+
"host": "local",
|
|
79
|
+
"hostname": "local",
|
|
80
|
+
"pathname": "/",
|
|
81
|
+
"search": "",
|
|
82
|
+
"hash": "",
|
|
83
|
+
reload() { /* no-op */ }
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// history (framework の gotoView が SPA 設定時に history.pushState を呼ぶ)。
|
|
88
|
+
// pushState/replaceState は location の pathname/search/hash にも反映する
|
|
89
|
+
if (typeof global.history === "undefined") {
|
|
90
|
+
const applyUrl = (url) => {
|
|
91
|
+
if (typeof url !== "string" || url === "") {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
let rest = url;
|
|
95
|
+
const origin = global.location.origin;
|
|
96
|
+
if (rest.indexOf(origin) === 0) {
|
|
97
|
+
rest = rest.slice(origin.length);
|
|
98
|
+
}
|
|
99
|
+
if (rest === "" || rest[0] !== "/") {
|
|
100
|
+
rest = "/" + rest;
|
|
101
|
+
}
|
|
102
|
+
let hash = "";
|
|
103
|
+
const hi = rest.indexOf("#");
|
|
104
|
+
if (hi >= 0) {
|
|
105
|
+
hash = rest.slice(hi);
|
|
106
|
+
rest = rest.slice(0, hi);
|
|
107
|
+
}
|
|
108
|
+
let search = "";
|
|
109
|
+
const qi = rest.indexOf("?");
|
|
110
|
+
if (qi >= 0) {
|
|
111
|
+
search = rest.slice(qi);
|
|
112
|
+
rest = rest.slice(0, qi);
|
|
113
|
+
}
|
|
114
|
+
global.location.pathname = rest;
|
|
115
|
+
global.location.search = search;
|
|
116
|
+
global.location.hash = hash;
|
|
117
|
+
global.location.href = origin + rest + search + hash;
|
|
118
|
+
};
|
|
119
|
+
global.history = {
|
|
120
|
+
"state": null,
|
|
121
|
+
"length": 1,
|
|
122
|
+
pushState(state, _title, url) {
|
|
123
|
+
this.state = state;
|
|
124
|
+
this.length++;
|
|
125
|
+
applyUrl(url);
|
|
126
|
+
},
|
|
127
|
+
replaceState(state, _title, url) {
|
|
128
|
+
this.state = state;
|
|
129
|
+
applyUrl(url);
|
|
130
|
+
},
|
|
131
|
+
back() { /* no-op */ },
|
|
132
|
+
forward() { /* no-op */ },
|
|
133
|
+
go() { /* no-op */ }
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
})(globalThis);
|
|
137
|
+
)JS";
|
|
138
|
+
|
|
139
|
+
// section 3: atob / btoa
|
|
140
|
+
const char kPolyfills3[] = R"JS(
|
|
141
|
+
(function (global) {
|
|
142
|
+
"use strict";
|
|
143
|
+
// atob / btoa (indexedDB のバイナリ値永続化にも使用)
|
|
144
|
+
const B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
145
|
+
if (typeof global.btoa !== "function") {
|
|
146
|
+
global.btoa = function (bin) {
|
|
147
|
+
bin = String(bin);
|
|
148
|
+
let out = "";
|
|
149
|
+
for (let i = 0; i < bin.length; i += 3) {
|
|
150
|
+
const a = bin.charCodeAt(i);
|
|
151
|
+
const b = i + 1 < bin.length ? bin.charCodeAt(i + 1) : NaN;
|
|
152
|
+
const c = i + 2 < bin.length ? bin.charCodeAt(i + 2) : NaN;
|
|
153
|
+
if (a > 255 || b > 255 || c > 255) {
|
|
154
|
+
throw new Error("btoa: character out of range");
|
|
155
|
+
}
|
|
156
|
+
out += B64[a >> 2];
|
|
157
|
+
out += B64[((a & 3) << 4) | (isNaN(b) ? 0 : b >> 4)];
|
|
158
|
+
out += isNaN(b) ? "=" : B64[((b & 15) << 2) | (isNaN(c) ? 0 : c >> 6)];
|
|
159
|
+
out += isNaN(c) ? "=" : B64[c & 63];
|
|
160
|
+
}
|
|
161
|
+
return out;
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
if (typeof global.atob !== "function") {
|
|
165
|
+
global.atob = function (b64) {
|
|
166
|
+
b64 = String(b64).replace(/[=\s]+$/, "");
|
|
167
|
+
let out = "";
|
|
168
|
+
let buffer = 0;
|
|
169
|
+
let bits = 0;
|
|
170
|
+
for (let i = 0; i < b64.length; i++) {
|
|
171
|
+
const v = B64.indexOf(b64[i]);
|
|
172
|
+
if (v < 0) { continue; }
|
|
173
|
+
buffer = (buffer << 6) | v;
|
|
174
|
+
bits += 6;
|
|
175
|
+
if (bits >= 8) {
|
|
176
|
+
bits -= 8;
|
|
177
|
+
out += String.fromCharCode((buffer >> bits) & 0xff);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return out;
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
})(globalThis);
|
|
184
|
+
)JS";
|
|
185
|
+
|
|
186
|
+
// section 4: localStorage / sessionStorage / alert / confirm
|
|
187
|
+
const char kPolyfills4[] = R"JS(
|
|
188
|
+
(function (global) {
|
|
189
|
+
"use strict";
|
|
190
|
+
// localStorage / sessionStorage。
|
|
191
|
+
// 永続化はホストの __next2d_storage_load/save (名前付きファイル I/O) に委ねる。
|
|
192
|
+
if (typeof global.localStorage === "undefined") {
|
|
193
|
+
const makeStorage = function (persistent) {
|
|
194
|
+
let data = {};
|
|
195
|
+
if (persistent && typeof global.__next2d_storage_load === "function") {
|
|
196
|
+
try {
|
|
197
|
+
const raw = global.__next2d_storage_load("localStorage");
|
|
198
|
+
if (raw) { data = JSON.parse(raw) || {}; }
|
|
199
|
+
} catch (e) { data = {}; }
|
|
200
|
+
}
|
|
201
|
+
const persist = function () {
|
|
202
|
+
if (persistent && typeof global.__next2d_storage_save === "function") {
|
|
203
|
+
try { global.__next2d_storage_save("localStorage", JSON.stringify(data)); } catch (e) { /* 保存失敗は無視 */ }
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
return {
|
|
207
|
+
getItem(k) {
|
|
208
|
+
k = String(k);
|
|
209
|
+
return Object.prototype.hasOwnProperty.call(data, k) ? data[k] : null;
|
|
210
|
+
},
|
|
211
|
+
setItem(k, v) { data[String(k)] = String(v); persist(); },
|
|
212
|
+
removeItem(k) { delete data[String(k)]; persist(); },
|
|
213
|
+
clear() { data = {}; persist(); },
|
|
214
|
+
key(i) {
|
|
215
|
+
const ks = Object.keys(data);
|
|
216
|
+
return i >= 0 && i < ks.length ? ks[i] : null;
|
|
217
|
+
},
|
|
218
|
+
get length() { return Object.keys(data).length; }
|
|
219
|
+
};
|
|
220
|
+
};
|
|
221
|
+
global.localStorage = makeStorage(true);
|
|
222
|
+
global.sessionStorage = makeStorage(false);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// alert / confirm (ダイアログの無い環境向け。confirm は肯定を返す)
|
|
226
|
+
if (typeof global.alert !== "function") {
|
|
227
|
+
global.alert = function () { /* no-op */ };
|
|
228
|
+
}
|
|
229
|
+
if (typeof global.confirm !== "function") {
|
|
230
|
+
global.confirm = function () { return true; };
|
|
231
|
+
}
|
|
232
|
+
})(globalThis);
|
|
233
|
+
)JS";
|
|
234
|
+
|
|
235
|
+
// section 5: TextDecoder
|
|
236
|
+
const char kPolyfills5[] = R"JS(
|
|
237
|
+
(function (global) {
|
|
238
|
+
"use strict";
|
|
239
|
+
if (typeof global.TextDecoder === "undefined") {
|
|
240
|
+
global.TextDecoder = class TextDecoder {
|
|
241
|
+
constructor(label) { this._label = label || "utf-8"; }
|
|
242
|
+
get encoding() { return "utf-8"; }
|
|
243
|
+
decode(input) {
|
|
244
|
+
if (!input) { return ""; }
|
|
245
|
+
const bytes = input instanceof Uint8Array
|
|
246
|
+
? input
|
|
247
|
+
: new Uint8Array(input.buffer || input);
|
|
248
|
+
let out = "";
|
|
249
|
+
let i = 0;
|
|
250
|
+
while (i < bytes.length) {
|
|
251
|
+
const c = bytes[i++];
|
|
252
|
+
if (c < 0x80) {
|
|
253
|
+
out += String.fromCharCode(c);
|
|
254
|
+
} else if (c < 0xe0) {
|
|
255
|
+
out += String.fromCharCode(((c & 0x1f) << 6) | (bytes[i++] & 0x3f));
|
|
256
|
+
} else if (c < 0xf0) {
|
|
257
|
+
out += String.fromCharCode(
|
|
258
|
+
((c & 0x0f) << 12) | ((bytes[i++] & 0x3f) << 6) | (bytes[i++] & 0x3f)
|
|
259
|
+
);
|
|
260
|
+
} else {
|
|
261
|
+
let code = ((c & 0x07) << 18) | ((bytes[i++] & 0x3f) << 12) |
|
|
262
|
+
((bytes[i++] & 0x3f) << 6) | (bytes[i++] & 0x3f);
|
|
263
|
+
code -= 0x10000;
|
|
264
|
+
out += String.fromCharCode(0xd800 + (code >> 10), 0xdc00 + (code & 0x3ff));
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return out;
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
})(globalThis);
|
|
272
|
+
)JS";
|
|
273
|
+
|
|
274
|
+
// section 6: DOM 要素ツリー (parentElement / children / getElementById / Event)。
|
|
275
|
+
// player の boot は document.body.appendChild(div) 後に div.parentElement.tagName を
|
|
276
|
+
// 参照し、無いと throw する (async 起動フロー内のため無報告で停止していた)。
|
|
277
|
+
// canvas/video はネイティブ実装 (C++) をそのまま使い、他タグを JS で本格化する。
|
|
278
|
+
const char kPolyfills6[] = R"JS(
|
|
279
|
+
(function (global) {
|
|
280
|
+
"use strict";
|
|
281
|
+
var doc = global.document;
|
|
282
|
+
if (!doc || doc.__domUpgraded) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
doc.__domUpgraded = true;
|
|
286
|
+
|
|
287
|
+
if (typeof global.Event !== "function") {
|
|
288
|
+
var Ev = function (type, init) {
|
|
289
|
+
init = init || {};
|
|
290
|
+
this.type = String(type);
|
|
291
|
+
this.bubbles = !!init.bubbles;
|
|
292
|
+
this.cancelable = !!init.cancelable;
|
|
293
|
+
this.defaultPrevented = false;
|
|
294
|
+
this.target = null;
|
|
295
|
+
this.currentTarget = null;
|
|
296
|
+
};
|
|
297
|
+
Ev.prototype.preventDefault = function () {
|
|
298
|
+
if (this.cancelable) { this.defaultPrevented = true; }
|
|
299
|
+
};
|
|
300
|
+
Ev.prototype.stopPropagation = function () {};
|
|
301
|
+
Ev.prototype.stopImmediatePropagation = function () {};
|
|
302
|
+
global.Event = Ev;
|
|
303
|
+
}
|
|
304
|
+
if (typeof global.CustomEvent !== "function") {
|
|
305
|
+
global.CustomEvent = function (type, init) {
|
|
306
|
+
var e = new global.Event(type, init);
|
|
307
|
+
e.detail = init && init.detail !== undefined ? init.detail : null;
|
|
308
|
+
return e;
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function styleSize(el, key) {
|
|
313
|
+
var v = el.style && el.style[key];
|
|
314
|
+
var n = parseFloat(v);
|
|
315
|
+
if (isFinite(n) && n > 0) { return n; }
|
|
316
|
+
var attrs = el.__attrs;
|
|
317
|
+
if (attrs && typeof attrs.style === "string") {
|
|
318
|
+
var m = attrs.style.match(
|
|
319
|
+
new RegExp("(?:^|;)\\s*" + key + "\\s*:\\s*([0-9.]+)px")
|
|
320
|
+
);
|
|
321
|
+
if (m) { return parseFloat(m[1]); }
|
|
322
|
+
}
|
|
323
|
+
if (el.tagName === "BODY" || el.tagName === "HTML") {
|
|
324
|
+
return key === "width" ? global.innerWidth : global.innerHeight;
|
|
325
|
+
}
|
|
326
|
+
return 0;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// el を DOM 要素相当へ拡張する。addEventListener はネイティブ実装
|
|
330
|
+
// (__listeners: ホストの入力配送先) が既にある場合は温存する。
|
|
331
|
+
function upgrade(el, tag) {
|
|
332
|
+
tag = String(tag || "div").toLowerCase();
|
|
333
|
+
el.tagName = tag.toUpperCase();
|
|
334
|
+
el.localName = tag;
|
|
335
|
+
if (el.id === undefined) { el.id = ""; }
|
|
336
|
+
if (!el.style) { el.style = {}; }
|
|
337
|
+
if (!el.__attrs) { el.__attrs = {}; }
|
|
338
|
+
el.children = [];
|
|
339
|
+
el.childNodes = el.children;
|
|
340
|
+
el.parentElement = null;
|
|
341
|
+
el.parentNode = null;
|
|
342
|
+
el.dataset = {};
|
|
343
|
+
|
|
344
|
+
Object.defineProperty(el, "firstChild", {
|
|
345
|
+
get: function () { return this.children.length ? this.children[0] : null; },
|
|
346
|
+
configurable: true
|
|
347
|
+
});
|
|
348
|
+
Object.defineProperty(el, "lastChild", {
|
|
349
|
+
get: function () {
|
|
350
|
+
var c = this.children;
|
|
351
|
+
return c.length ? c[c.length - 1] : null;
|
|
352
|
+
},
|
|
353
|
+
configurable: true
|
|
354
|
+
});
|
|
355
|
+
Object.defineProperty(el, "clientWidth", {
|
|
356
|
+
get: function () { return styleSize(this, "width"); },
|
|
357
|
+
configurable: true
|
|
358
|
+
});
|
|
359
|
+
Object.defineProperty(el, "clientHeight", {
|
|
360
|
+
get: function () { return styleSize(this, "height"); },
|
|
361
|
+
configurable: true
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
var html = "";
|
|
365
|
+
Object.defineProperty(el, "innerHTML", {
|
|
366
|
+
get: function () { return html; },
|
|
367
|
+
set: function (v) { html = String(v); this.children.length = 0; },
|
|
368
|
+
configurable: true
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
el.appendChild = function (child) {
|
|
372
|
+
if (child.parentElement && child.parentElement.children) {
|
|
373
|
+
var cs = child.parentElement.children;
|
|
374
|
+
var i = cs.indexOf(child);
|
|
375
|
+
if (i >= 0) { cs.splice(i, 1); }
|
|
376
|
+
}
|
|
377
|
+
this.children.push(child);
|
|
378
|
+
child.parentElement = this;
|
|
379
|
+
child.parentNode = this;
|
|
380
|
+
return child;
|
|
381
|
+
};
|
|
382
|
+
el.removeChild = function (child) {
|
|
383
|
+
var i = this.children.indexOf(child);
|
|
384
|
+
if (i >= 0) { this.children.splice(i, 1); }
|
|
385
|
+
child.parentElement = null;
|
|
386
|
+
child.parentNode = null;
|
|
387
|
+
return child;
|
|
388
|
+
};
|
|
389
|
+
el.insertBefore = function (child, ref) {
|
|
390
|
+
var i = ref ? this.children.indexOf(ref) : -1;
|
|
391
|
+
if (i < 0) { return this.appendChild(child); }
|
|
392
|
+
this.children.splice(i, 0, child);
|
|
393
|
+
child.parentElement = this;
|
|
394
|
+
child.parentNode = this;
|
|
395
|
+
return child;
|
|
396
|
+
};
|
|
397
|
+
el.contains = function (node) {
|
|
398
|
+
if (node === this) { return true; }
|
|
399
|
+
for (var i = 0; i < this.children.length; i++) {
|
|
400
|
+
var c = this.children[i];
|
|
401
|
+
if (c === node || (c.contains && c.contains(node))) { return true; }
|
|
402
|
+
}
|
|
403
|
+
return false;
|
|
404
|
+
};
|
|
405
|
+
el.remove = function () {
|
|
406
|
+
if (this.parentElement) { this.parentElement.removeChild(this); }
|
|
407
|
+
};
|
|
408
|
+
el.setAttribute = function (name, value) {
|
|
409
|
+
this.__attrs[name] = String(value);
|
|
410
|
+
if (name === "id") { this.id = String(value); }
|
|
411
|
+
};
|
|
412
|
+
el.getAttribute = function (name) {
|
|
413
|
+
return this.__attrs[name] !== undefined ? this.__attrs[name] : null;
|
|
414
|
+
};
|
|
415
|
+
el.removeAttribute = function (name) { delete this.__attrs[name]; };
|
|
416
|
+
el.getBoundingClientRect = function () {
|
|
417
|
+
var w = this.clientWidth;
|
|
418
|
+
var h = this.clientHeight;
|
|
419
|
+
return { x: 0, y: 0, top: 0, left: 0, right: w, bottom: h, width: w, height: h };
|
|
420
|
+
};
|
|
421
|
+
el.focus = function () {};
|
|
422
|
+
el.blur = function () {};
|
|
423
|
+
|
|
424
|
+
if (typeof el.addEventListener !== "function") {
|
|
425
|
+
var listeners = {};
|
|
426
|
+
el.addEventListener = function (type, fn) {
|
|
427
|
+
(listeners[type] || (listeners[type] = [])).push(fn);
|
|
428
|
+
};
|
|
429
|
+
el.removeEventListener = function (type, fn) {
|
|
430
|
+
var a = listeners[type];
|
|
431
|
+
if (!a) { return; }
|
|
432
|
+
var i = a.indexOf(fn);
|
|
433
|
+
if (i >= 0) { a.splice(i, 1); }
|
|
434
|
+
};
|
|
435
|
+
el.dispatchEvent = function (ev) {
|
|
436
|
+
ev.target = ev.target || this;
|
|
437
|
+
ev.currentTarget = this;
|
|
438
|
+
var a = listeners[ev.type];
|
|
439
|
+
if (a) {
|
|
440
|
+
for (var i = 0; i < a.length; i++) {
|
|
441
|
+
try { a[i].call(this, ev); } catch (e) {
|
|
442
|
+
if (global.console) { global.console.error(e); }
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
var on = this["on" + ev.type];
|
|
447
|
+
if (typeof on === "function") {
|
|
448
|
+
try { on.call(this, ev); } catch (e2) {
|
|
449
|
+
if (global.console) { global.console.error(e2); }
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
return !ev.defaultPrevented;
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
return el;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
var nativeCreate = doc.createElement.bind(doc);
|
|
459
|
+
doc.createElement = function (tag) {
|
|
460
|
+
var t = String(tag).toLowerCase();
|
|
461
|
+
if (t === "canvas" || t === "video") {
|
|
462
|
+
var n = nativeCreate(t);
|
|
463
|
+
if (!n.localName) {
|
|
464
|
+
n.localName = t;
|
|
465
|
+
n.tagName = t.toUpperCase();
|
|
466
|
+
}
|
|
467
|
+
if (n.parentElement === undefined) {
|
|
468
|
+
n.parentElement = null;
|
|
469
|
+
n.parentNode = null;
|
|
470
|
+
}
|
|
471
|
+
return n;
|
|
472
|
+
}
|
|
473
|
+
return upgrade({}, t);
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
// body / documentElement (同一オブジェクト)。ネイティブの addEventListener
|
|
477
|
+
// は upgrade 内で温存される。
|
|
478
|
+
var body = doc.body;
|
|
479
|
+
if (body) {
|
|
480
|
+
upgrade(body, "body");
|
|
481
|
+
Object.defineProperty(body, "clientWidth", {
|
|
482
|
+
get: function () { return global.innerWidth; },
|
|
483
|
+
configurable: true
|
|
484
|
+
});
|
|
485
|
+
Object.defineProperty(body, "clientHeight", {
|
|
486
|
+
get: function () { return global.innerHeight; },
|
|
487
|
+
configurable: true
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
doc.getElementById = function (id) {
|
|
492
|
+
id = String(id);
|
|
493
|
+
var find = function (el) {
|
|
494
|
+
if (el.id === id) { return el; }
|
|
495
|
+
var cs = el.children || [];
|
|
496
|
+
for (var i = 0; i < cs.length; i++) {
|
|
497
|
+
var r = find(cs[i]);
|
|
498
|
+
if (r) { return r; }
|
|
499
|
+
}
|
|
500
|
+
return null;
|
|
501
|
+
};
|
|
502
|
+
return body ? find(body) : null;
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
// window.addEventListener("resize", ...) 用 (main コンテキストの global)
|
|
506
|
+
if (typeof global.addEventListener !== "function") {
|
|
507
|
+
var winListeners = {};
|
|
508
|
+
global.addEventListener = function (type, fn) {
|
|
509
|
+
(winListeners[type] || (winListeners[type] = [])).push(fn);
|
|
510
|
+
};
|
|
511
|
+
global.removeEventListener = function (type, fn) {
|
|
512
|
+
var a = winListeners[type];
|
|
513
|
+
if (!a) { return; }
|
|
514
|
+
var i = a.indexOf(fn);
|
|
515
|
+
if (i >= 0) { a.splice(i, 1); }
|
|
516
|
+
};
|
|
517
|
+
global.dispatchEvent = function (ev) {
|
|
518
|
+
var a = winListeners[ev.type];
|
|
519
|
+
if (a) {
|
|
520
|
+
for (var i = 0; i < a.length; i++) {
|
|
521
|
+
try { a[i].call(global, ev); } catch (e) {
|
|
522
|
+
if (global.console) { global.console.error(e); }
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
return true;
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
})(globalThis);
|
|
530
|
+
)JS";
|
|
531
|
+
|
|
532
|
+
// section 7: indexedDB (完全実装: cursor/index/複合キー/IDBKeyRange/autoIncrement)。
|
|
533
|
+
// 31KB あり MSVC の文字列リテラル上限(~16KB)を超えるため 3 リテラルに分割し、
|
|
534
|
+
// InstallPolyfills で連結して 1 スクリプトとして評価する。atob/btoa(section3)と
|
|
535
|
+
// queueMicrotask(section4)に依存するため、それらの後で評価すること。
|
|
536
|
+
const char kIndexedDB1[] = R"JS(
|
|
537
|
+
(function (global) {
|
|
538
|
+
"use strict";
|
|
539
|
+
// indexedDB (ファイル永続化・単一スレッド同期ストア + 非同期 API 面)。
|
|
540
|
+
// 対応: open/upgrade, objectStore(keyPath / 複合keyPath / autoIncrement),
|
|
541
|
+
// get/getKey/getAll/getAllKeys/count/put/add/delete/clear,
|
|
542
|
+
// createIndex/index/deleteIndex(unique/multiEntry),
|
|
543
|
+
// openCursor/openKeyCursor(next/nextunique/prev/prevunique, IDBKeyRange),
|
|
544
|
+
// IDBKeyRange(only/lowerBound/upperBound/bound)。
|
|
545
|
+
// 値は JSON 化可能なもの + ArrayBuffer/TypedArray (base64 タグ付け)。
|
|
546
|
+
if (typeof global.indexedDB !== "undefined" ||
|
|
547
|
+
typeof global.__next2d_storage_load !== "function") {
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
const TYPED = {
|
|
552
|
+
"Int8Array": Int8Array, "Uint8Array": Uint8Array,
|
|
553
|
+
"Uint8ClampedArray": Uint8ClampedArray,
|
|
554
|
+
"Int16Array": Int16Array, "Uint16Array": Uint16Array,
|
|
555
|
+
"Int32Array": Int32Array, "Uint32Array": Uint32Array,
|
|
556
|
+
"Float32Array": Float32Array, "Float64Array": Float64Array
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
const bytesToB64 = function (u8) {
|
|
560
|
+
let bin = "";
|
|
561
|
+
for (let i = 0; i < u8.length; i += 0x8000) {
|
|
562
|
+
bin += String.fromCharCode.apply(null, u8.subarray(i, i + 0x8000));
|
|
563
|
+
}
|
|
564
|
+
return global.btoa(bin);
|
|
565
|
+
};
|
|
566
|
+
const b64ToBytes = function (b64) {
|
|
567
|
+
const bin = global.atob(b64);
|
|
568
|
+
const u8 = new Uint8Array(bin.length);
|
|
569
|
+
for (let i = 0; i < bin.length; i++) { u8[i] = bin.charCodeAt(i); }
|
|
570
|
+
return u8;
|
|
571
|
+
};
|
|
572
|
+
const encodeValue = function (v) {
|
|
573
|
+
if (v instanceof ArrayBuffer) {
|
|
574
|
+
return { "__n2d_bin": bytesToB64(new Uint8Array(v)), "__n2d_type": "ArrayBuffer" };
|
|
575
|
+
}
|
|
576
|
+
if (ArrayBuffer.isView(v)) {
|
|
577
|
+
const name = v.constructor && v.constructor.name;
|
|
578
|
+
if (TYPED[name]) {
|
|
579
|
+
return {
|
|
580
|
+
"__n2d_bin": bytesToB64(new Uint8Array(v.buffer, v.byteOffset, v.byteLength)),
|
|
581
|
+
"__n2d_type": name
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
if (Array.isArray(v)) { return v.map(encodeValue); }
|
|
586
|
+
if (v && typeof v === "object") {
|
|
587
|
+
const out = {};
|
|
588
|
+
for (const k of Object.keys(v)) { out[k] = encodeValue(v[k]); }
|
|
589
|
+
return out;
|
|
590
|
+
}
|
|
591
|
+
return v;
|
|
592
|
+
};
|
|
593
|
+
const decodeValue = function (v) {
|
|
594
|
+
if (v && typeof v === "object") {
|
|
595
|
+
if (typeof v.__n2d_bin === "string") {
|
|
596
|
+
const u8 = b64ToBytes(v.__n2d_bin);
|
|
597
|
+
if (v.__n2d_type === "ArrayBuffer") { return u8.buffer; }
|
|
598
|
+
const Ctor = TYPED[v.__n2d_type] || Uint8Array;
|
|
599
|
+
return new Ctor(u8.buffer, 0, u8.byteLength / Ctor.BYTES_PER_ELEMENT);
|
|
600
|
+
}
|
|
601
|
+
if (Array.isArray(v)) { return v.map(decodeValue); }
|
|
602
|
+
const out = {};
|
|
603
|
+
for (const k of Object.keys(v)) { out[k] = decodeValue(v[k]); }
|
|
604
|
+
return out;
|
|
605
|
+
}
|
|
606
|
+
return v;
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
// --- キー順序 (IndexedDB 仕様: number < string < array) -------------------
|
|
610
|
+
const keyType = function (k) {
|
|
611
|
+
if (Array.isArray(k)) { return 3; }
|
|
612
|
+
if (typeof k === "number") { return 0; }
|
|
613
|
+
if (typeof k === "string") { return 2; }
|
|
614
|
+
return -1;
|
|
615
|
+
};
|
|
616
|
+
const cmpKeys = function (a, b) {
|
|
617
|
+
const ta = keyType(a), tb = keyType(b);
|
|
618
|
+
if (ta !== tb) { return ta < tb ? -1 : 1; }
|
|
619
|
+
if (ta === 3) {
|
|
620
|
+
const n = Math.min(a.length, b.length);
|
|
621
|
+
for (let i = 0; i < n; i++) {
|
|
622
|
+
const c = cmpKeys(a[i], b[i]);
|
|
623
|
+
if (c !== 0) { return c; }
|
|
624
|
+
}
|
|
625
|
+
return a.length === b.length ? 0 : (a.length < b.length ? -1 : 1);
|
|
626
|
+
}
|
|
627
|
+
if (a < b) { return -1; }
|
|
628
|
+
if (a > b) { return 1; }
|
|
629
|
+
return 0;
|
|
630
|
+
};
|
|
631
|
+
const isValidKey = function (k) { return keyType(k) !== -1; };
|
|
632
|
+
// ストレージのマップ用一意文字列
|
|
633
|
+
const encKey = function (k) { return JSON.stringify(k); };
|
|
634
|
+
|
|
635
|
+
// objectStoreNames / indexNames は配列 + DOMStringList 互換 (contains/item)。
|
|
636
|
+
const nameList = function (names) {
|
|
637
|
+
names.sort();
|
|
638
|
+
names.contains = function (n) { return names.indexOf(n) !== -1; };
|
|
639
|
+
names.item = function (i) { return names[i] != null ? names[i] : null; };
|
|
640
|
+
return names;
|
|
641
|
+
};
|
|
642
|
+
|
|
643
|
+
// --- keyPath 抽出 (ネスト "a.b" / 複合 ["a","b"]) -------------------------
|
|
644
|
+
const evalPath = function (value, path) {
|
|
645
|
+
if (path === "") { return value; }
|
|
646
|
+
const parts = path.split(".");
|
|
647
|
+
let cur = value;
|
|
648
|
+
for (let i = 0; i < parts.length; i++) {
|
|
649
|
+
if (cur == null || typeof cur !== "object") { return undefined; }
|
|
650
|
+
cur = cur[parts[i]];
|
|
651
|
+
}
|
|
652
|
+
return cur;
|
|
653
|
+
};
|
|
654
|
+
const extractKey = function (value, keyPath) {
|
|
655
|
+
if (keyPath == null) { return undefined; }
|
|
656
|
+
if (Array.isArray(keyPath)) {
|
|
657
|
+
const out = [];
|
|
658
|
+
for (let i = 0; i < keyPath.length; i++) {
|
|
659
|
+
const k = evalPath(value, keyPath[i]);
|
|
660
|
+
if (k === undefined) { return undefined; }
|
|
661
|
+
out.push(k);
|
|
662
|
+
}
|
|
663
|
+
return out;
|
|
664
|
+
}
|
|
665
|
+
return evalPath(value, keyPath);
|
|
666
|
+
};
|
|
667
|
+
// in-line key を value へ書き戻す (autoIncrement 生成キー用、ネスト対応)
|
|
668
|
+
const injectKey = function (value, keyPath, key) {
|
|
669
|
+
if (typeof keyPath !== "string" || value == null || typeof value !== "object") { return; }
|
|
670
|
+
const parts = keyPath.split(".");
|
|
671
|
+
let cur = value;
|
|
672
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
673
|
+
if (cur[parts[i]] == null || typeof cur[parts[i]] !== "object") { cur[parts[i]] = {}; }
|
|
674
|
+
cur = cur[parts[i]];
|
|
675
|
+
}
|
|
676
|
+
cur[parts[parts.length - 1]] = key;
|
|
677
|
+
};
|
|
678
|
+
|
|
679
|
+
// --- IDBKeyRange --------------------------------------------------------
|
|
680
|
+
const makeRange = function (lower, upper, lowerOpen, upperOpen) {
|
|
681
|
+
return {
|
|
682
|
+
"lower": lower, "upper": upper,
|
|
683
|
+
"lowerOpen": !!lowerOpen, "upperOpen": !!upperOpen,
|
|
684
|
+
includes(key) { return rangeIncludes(this, key); }
|
|
685
|
+
};
|
|
686
|
+
};
|
|
687
|
+
const rangeIncludes = function (range, key) {
|
|
688
|
+
if (range == null) { return true; }
|
|
689
|
+
// 素のキー (range でない) は only 相当
|
|
690
|
+
if (typeof range !== "object" || !("lower" in range)) {
|
|
691
|
+
return cmpKeys(key, range) === 0;
|
|
692
|
+
}
|
|
693
|
+
if (range.lower !== undefined) {
|
|
694
|
+
const c = cmpKeys(key, range.lower);
|
|
695
|
+
if (c < 0 || (c === 0 && range.lowerOpen)) { return false; }
|
|
696
|
+
}
|
|
697
|
+
if (range.upper !== undefined) {
|
|
698
|
+
const c = cmpKeys(key, range.upper);
|
|
699
|
+
if (c > 0 || (c === 0 && range.upperOpen)) { return false; }
|
|
700
|
+
}
|
|
701
|
+
return true;
|
|
702
|
+
};
|
|
703
|
+
global.IDBKeyRange = {
|
|
704
|
+
only(v) { return makeRange(v, v, false, false); },
|
|
705
|
+
lowerBound(v, open) { return makeRange(v, undefined, open, false); },
|
|
706
|
+
upperBound(v, open) { return makeRange(undefined, v, false, open); },
|
|
707
|
+
bound(l, u, lo, uo) { return makeRange(l, u, lo, uo); }
|
|
708
|
+
};
|
|
709
|
+
|
|
710
|
+
// --- 永続化 -------------------------------------------------------------
|
|
711
|
+
const loadDb = function (name) {
|
|
712
|
+
try {
|
|
713
|
+
const raw = global.__next2d_storage_load("idb_" + name);
|
|
714
|
+
if (raw) { return JSON.parse(raw); }
|
|
715
|
+
} catch (e) { /* 破損時は初期化 */ }
|
|
716
|
+
return { "version": 0, "stores": {} };
|
|
717
|
+
};
|
|
718
|
+
const saveDb = function (name, db) {
|
|
719
|
+
try { global.__next2d_storage_save("idb_" + name, JSON.stringify(db)); } catch (e) { /* 無視 */ }
|
|
720
|
+
};
|
|
721
|
+
|
|
722
|
+
// --- 非同期 request/event ----------------------------------------------
|
|
723
|
+
const makeRequest = function () {
|
|
724
|
+
return {
|
|
725
|
+
"onsuccess": null, "onerror": null, "onupgradeneeded": null, "onblocked": null,
|
|
726
|
+
"result": undefined, "error": null, "readyState": "pending", "source": null,
|
|
727
|
+
"transaction": null
|
|
728
|
+
};
|
|
729
|
+
};
|
|
730
|
+
const fireSuccess = function (r) {
|
|
731
|
+
global.queueMicrotask(function () {
|
|
732
|
+
r.readyState = "done";
|
|
733
|
+
if (typeof r.onsuccess === "function") { r.onsuccess({ "target": r }); }
|
|
734
|
+
});
|
|
735
|
+
};
|
|
736
|
+
const fireError = function (r, e) {
|
|
737
|
+
r.error = e;
|
|
738
|
+
global.queueMicrotask(function () {
|
|
739
|
+
r.readyState = "done";
|
|
740
|
+
if (typeof r.onerror === "function") { r.onerror({ "target": r }); }
|
|
741
|
+
});
|
|
742
|
+
};
|
|
743
|
+
|
|
744
|
+
global.indexedDB = {
|
|
745
|
+
open(name, version) {
|
|
746
|
+
const req = makeRequest();
|
|
747
|
+
global.queueMicrotask(function () {
|
|
748
|
+
const data = loadDb(name);
|
|
749
|
+
|
|
750
|
+
)JS";
|
|
751
|
+
const char kIndexedDB2[] = R"JS(
|
|
752
|
+
// records: encKey -> { "key": rawKey, "value": encodedValue }
|
|
753
|
+
const storeApi = function (storeName, tx) {
|
|
754
|
+
const entry = data.stores[storeName];
|
|
755
|
+
if (!entry) { throw new Error("NotFoundError: object store '" + storeName + "'"); }
|
|
756
|
+
if (!entry.records) { entry.records = {}; }
|
|
757
|
+
if (!entry.indexes) { entry.indexes = {}; }
|
|
758
|
+
|
|
759
|
+
const run = function (fn) {
|
|
760
|
+
const r = makeRequest();
|
|
761
|
+
r.transaction = tx;
|
|
762
|
+
global.queueMicrotask(function () {
|
|
763
|
+
try { r.result = fn(); fireSuccess(r); }
|
|
764
|
+
catch (e) { fireError(r, e); }
|
|
765
|
+
});
|
|
766
|
+
return r;
|
|
767
|
+
};
|
|
768
|
+
|
|
769
|
+
const allRecords = function () {
|
|
770
|
+
return Object.keys(entry.records).map(function (ek) {
|
|
771
|
+
return entry.records[ek];
|
|
772
|
+
});
|
|
773
|
+
};
|
|
774
|
+
const sortedRecords = function () {
|
|
775
|
+
return allRecords().sort(function (a, b) { return cmpKeys(a.key, b.key); });
|
|
776
|
+
};
|
|
777
|
+
|
|
778
|
+
const resolveKey = function (value, key) {
|
|
779
|
+
let k = key;
|
|
780
|
+
if (k === undefined && entry.keyPath != null) {
|
|
781
|
+
k = extractKey(value, entry.keyPath);
|
|
782
|
+
}
|
|
783
|
+
if (k === undefined && entry.autoIncrement) {
|
|
784
|
+
entry.keyGen = (entry.keyGen || 0) + 1;
|
|
785
|
+
k = entry.keyGen;
|
|
786
|
+
if (typeof entry.keyPath === "string") { injectKey(value, entry.keyPath, k); }
|
|
787
|
+
} else if (typeof k === "number" && entry.autoIncrement && k > (entry.keyGen || 0)) {
|
|
788
|
+
entry.keyGen = Math.floor(k);
|
|
789
|
+
}
|
|
790
|
+
return k;
|
|
791
|
+
};
|
|
792
|
+
|
|
793
|
+
const putImpl = function (value, key, noOverwrite) {
|
|
794
|
+
const k = resolveKey(value, key);
|
|
795
|
+
if (k === undefined || !isValidKey(k)) { throw new Error("DataError: invalid key"); }
|
|
796
|
+
const ek = encKey(k);
|
|
797
|
+
if (noOverwrite && Object.prototype.hasOwnProperty.call(entry.records, ek)) {
|
|
798
|
+
throw new Error("ConstraintError: key already exists");
|
|
799
|
+
}
|
|
800
|
+
entry.records[ek] = { "key": k, "value": encodeValue(value) };
|
|
801
|
+
saveDb(name, data);
|
|
802
|
+
return k;
|
|
803
|
+
};
|
|
804
|
+
|
|
805
|
+
// カーソル生成 (records: 配列 [{key, primaryKey, value(encoded)}])
|
|
806
|
+
const makeCursor = function (req2, source, records, direction, keyOnly) {
|
|
807
|
+
const dir = direction || "next";
|
|
808
|
+
records.sort(function (a, b) {
|
|
809
|
+
return cmpKeys(a.key, b.key) || cmpKeys(a.primaryKey, b.primaryKey);
|
|
810
|
+
});
|
|
811
|
+
if (dir === "prev" || dir === "prevunique") { records.reverse(); }
|
|
812
|
+
if (dir === "nextunique" || dir === "prevunique") {
|
|
813
|
+
const seen = [];
|
|
814
|
+
const uniq = [];
|
|
815
|
+
for (let i = 0; i < records.length; i++) {
|
|
816
|
+
const ek = encKey(records[i].key);
|
|
817
|
+
if (seen.indexOf(ek) === -1) { seen.push(ek); uniq.push(records[i]); }
|
|
818
|
+
}
|
|
819
|
+
records = uniq;
|
|
820
|
+
}
|
|
821
|
+
let pos = -1;
|
|
822
|
+
const cursor = {
|
|
823
|
+
"direction": dir,
|
|
824
|
+
"source": source,
|
|
825
|
+
get key() { return pos >= 0 && pos < records.length ? records[pos].key : undefined; },
|
|
826
|
+
get primaryKey() { return pos >= 0 && pos < records.length ? records[pos].primaryKey : undefined; },
|
|
827
|
+
get value() {
|
|
828
|
+
if (keyOnly || pos < 0 || pos >= records.length) { return undefined; }
|
|
829
|
+
return decodeValue(records[pos].value);
|
|
830
|
+
},
|
|
831
|
+
continue(key) {
|
|
832
|
+
if (key === undefined) { pos++; }
|
|
833
|
+
else {
|
|
834
|
+
const forward = (dir === "next" || dir === "nextunique");
|
|
835
|
+
let np = pos + 1;
|
|
836
|
+
while (np < records.length) {
|
|
837
|
+
const c = cmpKeys(records[np].key, key);
|
|
838
|
+
if ((forward && c >= 0) || (!forward && c <= 0)) { break; }
|
|
839
|
+
np++;
|
|
840
|
+
}
|
|
841
|
+
pos = np;
|
|
842
|
+
}
|
|
843
|
+
step();
|
|
844
|
+
},
|
|
845
|
+
advance(count) { pos += (count > 0 ? count : 1); step(); },
|
|
846
|
+
"delete": function () {
|
|
847
|
+
const rec = records[pos];
|
|
848
|
+
return run(function () {
|
|
849
|
+
if (rec) { delete entry.records[encKey(rec.primaryKey)]; saveDb(name, data); }
|
|
850
|
+
});
|
|
851
|
+
},
|
|
852
|
+
update(value) {
|
|
853
|
+
const rec = records[pos];
|
|
854
|
+
return run(function () {
|
|
855
|
+
if (!rec) { throw new Error("InvalidStateError"); }
|
|
856
|
+
entry.records[encKey(rec.primaryKey)] = {
|
|
857
|
+
"key": rec.primaryKey, "value": encodeValue(value)
|
|
858
|
+
};
|
|
859
|
+
saveDb(name, data);
|
|
860
|
+
return rec.primaryKey;
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
};
|
|
864
|
+
const step = function () {
|
|
865
|
+
if (pos >= 0 && pos < records.length) { req2.result = cursor; }
|
|
866
|
+
else { req2.result = null; }
|
|
867
|
+
fireSuccess(req2);
|
|
868
|
+
};
|
|
869
|
+
// カーソルを最初のレコードへ位置づけて success を発火する
|
|
870
|
+
pos++;
|
|
871
|
+
step();
|
|
872
|
+
};
|
|
873
|
+
|
|
874
|
+
const api = {
|
|
875
|
+
"name": storeName,
|
|
876
|
+
"keyPath": entry.keyPath != null ? entry.keyPath : null,
|
|
877
|
+
"autoIncrement": !!entry.autoIncrement,
|
|
878
|
+
get indexNames() { return nameList(Object.keys(entry.indexes)); },
|
|
879
|
+
"transaction": tx,
|
|
880
|
+
get(key) {
|
|
881
|
+
return run(function () {
|
|
882
|
+
const recs = sortedRecords();
|
|
883
|
+
for (let i = 0; i < recs.length; i++) {
|
|
884
|
+
if (rangeIncludes(key, recs[i].key)) { return decodeValue(recs[i].value); }
|
|
885
|
+
}
|
|
886
|
+
return undefined;
|
|
887
|
+
});
|
|
888
|
+
},
|
|
889
|
+
getKey(key) {
|
|
890
|
+
return run(function () {
|
|
891
|
+
const recs = sortedRecords();
|
|
892
|
+
for (let i = 0; i < recs.length; i++) {
|
|
893
|
+
if (rangeIncludes(key, recs[i].key)) { return recs[i].key; }
|
|
894
|
+
}
|
|
895
|
+
return undefined;
|
|
896
|
+
});
|
|
897
|
+
},
|
|
898
|
+
put(value, key) { return run(function () { return putImpl(value, key, false); }); },
|
|
899
|
+
add(value, key) { return run(function () { return putImpl(value, key, true); }); },
|
|
900
|
+
"delete": function (key) {
|
|
901
|
+
return run(function () {
|
|
902
|
+
const recs = sortedRecords();
|
|
903
|
+
for (let i = 0; i < recs.length; i++) {
|
|
904
|
+
if (rangeIncludes(key, recs[i].key)) { delete entry.records[encKey(recs[i].key)]; }
|
|
905
|
+
}
|
|
906
|
+
saveDb(name, data);
|
|
907
|
+
});
|
|
908
|
+
},
|
|
909
|
+
clear() { return run(function () { entry.records = {}; saveDb(name, data); }); },
|
|
910
|
+
count(key) {
|
|
911
|
+
return run(function () {
|
|
912
|
+
const recs = sortedRecords();
|
|
913
|
+
let n = 0;
|
|
914
|
+
for (let i = 0; i < recs.length; i++) {
|
|
915
|
+
if (rangeIncludes(key, recs[i].key)) { n++; }
|
|
916
|
+
}
|
|
917
|
+
return n;
|
|
918
|
+
});
|
|
919
|
+
},
|
|
920
|
+
getAll(query, count) {
|
|
921
|
+
return run(function () {
|
|
922
|
+
const out = [];
|
|
923
|
+
const recs = sortedRecords();
|
|
924
|
+
for (let i = 0; i < recs.length; i++) {
|
|
925
|
+
if (rangeIncludes(query, recs[i].key)) { out.push(decodeValue(recs[i].value)); }
|
|
926
|
+
if (count && out.length >= count) { break; }
|
|
927
|
+
}
|
|
928
|
+
return out;
|
|
929
|
+
});
|
|
930
|
+
},
|
|
931
|
+
getAllKeys(query, count) {
|
|
932
|
+
return run(function () {
|
|
933
|
+
const out = [];
|
|
934
|
+
const recs = sortedRecords();
|
|
935
|
+
for (let i = 0; i < recs.length; i++) {
|
|
936
|
+
if (rangeIncludes(query, recs[i].key)) { out.push(recs[i].key); }
|
|
937
|
+
if (count && out.length >= count) { break; }
|
|
938
|
+
}
|
|
939
|
+
return out;
|
|
940
|
+
});
|
|
941
|
+
},
|
|
942
|
+
openCursor(query, direction) {
|
|
943
|
+
const r = makeRequest();
|
|
944
|
+
r.transaction = tx;
|
|
945
|
+
global.queueMicrotask(function () {
|
|
946
|
+
const recs = sortedRecords()
|
|
947
|
+
.filter(function (rec) { return rangeIncludes(query, rec.key); })
|
|
948
|
+
.map(function (rec) {
|
|
949
|
+
return { "key": rec.key, "primaryKey": rec.key, "value": rec.value };
|
|
950
|
+
});
|
|
951
|
+
makeCursor(r, api, recs, direction, false);
|
|
952
|
+
});
|
|
953
|
+
return r;
|
|
954
|
+
},
|
|
955
|
+
openKeyCursor(query, direction) {
|
|
956
|
+
const r = makeRequest();
|
|
957
|
+
r.transaction = tx;
|
|
958
|
+
global.queueMicrotask(function () {
|
|
959
|
+
const recs = sortedRecords()
|
|
960
|
+
.filter(function (rec) { return rangeIncludes(query, rec.key); })
|
|
961
|
+
.map(function (rec) {
|
|
962
|
+
return { "key": rec.key, "primaryKey": rec.key, "value": rec.value };
|
|
963
|
+
});
|
|
964
|
+
makeCursor(r, api, recs, direction, true);
|
|
965
|
+
});
|
|
966
|
+
)JS";
|
|
967
|
+
const char kIndexedDB3[] = R"JS(
|
|
968
|
+
return r;
|
|
969
|
+
},
|
|
970
|
+
createIndex(indexName, keyPath, options) {
|
|
971
|
+
entry.indexes[indexName] = {
|
|
972
|
+
"keyPath": keyPath,
|
|
973
|
+
"unique": !!(options && options.unique),
|
|
974
|
+
"multiEntry": !!(options && options.multiEntry)
|
|
975
|
+
};
|
|
976
|
+
saveDb(name, data);
|
|
977
|
+
return indexApi(indexName);
|
|
978
|
+
},
|
|
979
|
+
deleteIndex(indexName) {
|
|
980
|
+
delete entry.indexes[indexName];
|
|
981
|
+
saveDb(name, data);
|
|
982
|
+
},
|
|
983
|
+
index(indexName) { return indexApi(indexName); }
|
|
984
|
+
};
|
|
985
|
+
|
|
986
|
+
// インデックス: レコードを走査してインデックスキーを抽出する。
|
|
987
|
+
const indexApi = function (indexName) {
|
|
988
|
+
const meta = entry.indexes[indexName];
|
|
989
|
+
if (!meta) { throw new Error("NotFoundError: index '" + indexName + "'"); }
|
|
990
|
+
// [{ indexKey, primaryKey, value }] を multiEntry 展開込みで生成
|
|
991
|
+
const indexRecords = function () {
|
|
992
|
+
const out = [];
|
|
993
|
+
const recs = allRecords();
|
|
994
|
+
for (let i = 0; i < recs.length; i++) {
|
|
995
|
+
const decoded = decodeValue(recs[i].value);
|
|
996
|
+
let ik = extractKey(decoded, meta.keyPath);
|
|
997
|
+
if (ik === undefined) { continue; }
|
|
998
|
+
if (meta.multiEntry && Array.isArray(ik)) {
|
|
999
|
+
for (let j = 0; j < ik.length; j++) {
|
|
1000
|
+
if (isValidKey(ik[j])) {
|
|
1001
|
+
out.push({ "key": ik[j], "primaryKey": recs[i].key, "value": recs[i].value });
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
} else if (isValidKey(ik)) {
|
|
1005
|
+
out.push({ "key": ik, "primaryKey": recs[i].key, "value": recs[i].value });
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
out.sort(function (a, b) {
|
|
1009
|
+
return cmpKeys(a.key, b.key) || cmpKeys(a.primaryKey, b.primaryKey);
|
|
1010
|
+
});
|
|
1011
|
+
return out;
|
|
1012
|
+
};
|
|
1013
|
+
return {
|
|
1014
|
+
"name": indexName,
|
|
1015
|
+
"keyPath": meta.keyPath,
|
|
1016
|
+
"unique": meta.unique,
|
|
1017
|
+
"multiEntry": meta.multiEntry,
|
|
1018
|
+
"objectStore": api,
|
|
1019
|
+
get(key) {
|
|
1020
|
+
return run(function () {
|
|
1021
|
+
const recs = indexRecords();
|
|
1022
|
+
for (let i = 0; i < recs.length; i++) {
|
|
1023
|
+
if (rangeIncludes(key, recs[i].key)) { return decodeValue(recs[i].value); }
|
|
1024
|
+
}
|
|
1025
|
+
return undefined;
|
|
1026
|
+
});
|
|
1027
|
+
},
|
|
1028
|
+
getKey(key) {
|
|
1029
|
+
return run(function () {
|
|
1030
|
+
const recs = indexRecords();
|
|
1031
|
+
for (let i = 0; i < recs.length; i++) {
|
|
1032
|
+
if (rangeIncludes(key, recs[i].key)) { return recs[i].primaryKey; }
|
|
1033
|
+
}
|
|
1034
|
+
return undefined;
|
|
1035
|
+
});
|
|
1036
|
+
},
|
|
1037
|
+
getAll(query, count) {
|
|
1038
|
+
return run(function () {
|
|
1039
|
+
const out = [];
|
|
1040
|
+
const recs = indexRecords();
|
|
1041
|
+
for (let i = 0; i < recs.length; i++) {
|
|
1042
|
+
if (rangeIncludes(query, recs[i].key)) { out.push(decodeValue(recs[i].value)); }
|
|
1043
|
+
if (count && out.length >= count) { break; }
|
|
1044
|
+
}
|
|
1045
|
+
return out;
|
|
1046
|
+
});
|
|
1047
|
+
},
|
|
1048
|
+
getAllKeys(query, count) {
|
|
1049
|
+
return run(function () {
|
|
1050
|
+
const out = [];
|
|
1051
|
+
const recs = indexRecords();
|
|
1052
|
+
for (let i = 0; i < recs.length; i++) {
|
|
1053
|
+
if (rangeIncludes(query, recs[i].key)) { out.push(recs[i].primaryKey); }
|
|
1054
|
+
if (count && out.length >= count) { break; }
|
|
1055
|
+
}
|
|
1056
|
+
return out;
|
|
1057
|
+
});
|
|
1058
|
+
},
|
|
1059
|
+
count(key) {
|
|
1060
|
+
return run(function () {
|
|
1061
|
+
const recs = indexRecords();
|
|
1062
|
+
let n = 0;
|
|
1063
|
+
for (let i = 0; i < recs.length; i++) {
|
|
1064
|
+
if (rangeIncludes(key, recs[i].key)) { n++; }
|
|
1065
|
+
}
|
|
1066
|
+
return n;
|
|
1067
|
+
});
|
|
1068
|
+
},
|
|
1069
|
+
openCursor(query, direction) {
|
|
1070
|
+
const r = makeRequest();
|
|
1071
|
+
r.transaction = tx;
|
|
1072
|
+
global.queueMicrotask(function () {
|
|
1073
|
+
const recs = indexRecords().filter(function (rec) {
|
|
1074
|
+
return rangeIncludes(query, rec.key);
|
|
1075
|
+
});
|
|
1076
|
+
makeCursor(r, this, recs, direction, false);
|
|
1077
|
+
});
|
|
1078
|
+
return r;
|
|
1079
|
+
},
|
|
1080
|
+
openKeyCursor(query, direction) {
|
|
1081
|
+
const r = makeRequest();
|
|
1082
|
+
r.transaction = tx;
|
|
1083
|
+
global.queueMicrotask(function () {
|
|
1084
|
+
const recs = indexRecords().filter(function (rec) {
|
|
1085
|
+
return rangeIncludes(query, rec.key);
|
|
1086
|
+
});
|
|
1087
|
+
makeCursor(r, this, recs, direction, true);
|
|
1088
|
+
});
|
|
1089
|
+
return r;
|
|
1090
|
+
}
|
|
1091
|
+
};
|
|
1092
|
+
};
|
|
1093
|
+
|
|
1094
|
+
return api;
|
|
1095
|
+
};
|
|
1096
|
+
|
|
1097
|
+
const dbObj = {
|
|
1098
|
+
"name": name,
|
|
1099
|
+
"version": 0,
|
|
1100
|
+
get objectStoreNames() { return nameList(Object.keys(data.stores)); },
|
|
1101
|
+
createObjectStore(storeName, options) {
|
|
1102
|
+
if (!data.stores[storeName]) {
|
|
1103
|
+
data.stores[storeName] = {
|
|
1104
|
+
"keyPath": options && options.keyPath !== undefined ? options.keyPath : null,
|
|
1105
|
+
"autoIncrement": !!(options && options.autoIncrement),
|
|
1106
|
+
"records": {}, "indexes": {}, "keyGen": 0
|
|
1107
|
+
};
|
|
1108
|
+
saveDb(name, data);
|
|
1109
|
+
}
|
|
1110
|
+
return storeApi(storeName, currentTx);
|
|
1111
|
+
},
|
|
1112
|
+
deleteObjectStore(storeName) {
|
|
1113
|
+
delete data.stores[storeName];
|
|
1114
|
+
saveDb(name, data);
|
|
1115
|
+
},
|
|
1116
|
+
transaction(_names, _mode) {
|
|
1117
|
+
const tx = {
|
|
1118
|
+
"oncomplete": null, "onerror": null, "onabort": null,
|
|
1119
|
+
"db": dbObj, "mode": _mode || "readonly", "error": null,
|
|
1120
|
+
objectStore(s) { return storeApi(s, tx); },
|
|
1121
|
+
abort() { /* 単純化: ロールバック無し */ },
|
|
1122
|
+
commit() { /* 自動コミット */ }
|
|
1123
|
+
};
|
|
1124
|
+
// 全リクエストのマイクロタスク消化後に complete
|
|
1125
|
+
let depth = 0;
|
|
1126
|
+
const settle = function () {
|
|
1127
|
+
if (depth++ < 4) { global.queueMicrotask(settle); return; }
|
|
1128
|
+
if (typeof tx.oncomplete === "function") { tx.oncomplete({ "target": tx }); }
|
|
1129
|
+
};
|
|
1130
|
+
global.queueMicrotask(settle);
|
|
1131
|
+
return tx;
|
|
1132
|
+
},
|
|
1133
|
+
close() { /* no-op */ },
|
|
1134
|
+
"onversionchange": null, "onabort": null, "onerror": null, "onclose": null
|
|
1135
|
+
};
|
|
1136
|
+
|
|
1137
|
+
const oldVersion = data.version || 0;
|
|
1138
|
+
const newVersion = typeof version === "number" ? version : Math.max(1, oldVersion);
|
|
1139
|
+
dbObj.version = newVersion;
|
|
1140
|
+
let currentTx = null;
|
|
1141
|
+
|
|
1142
|
+
req.result = dbObj;
|
|
1143
|
+
req.readyState = "done";
|
|
1144
|
+
if (newVersion > oldVersion) {
|
|
1145
|
+
data.version = newVersion;
|
|
1146
|
+
// upgrade トランザクション (versionchange)
|
|
1147
|
+
currentTx = {
|
|
1148
|
+
"oncomplete": null, "onerror": null, "onabort": null,
|
|
1149
|
+
"db": dbObj, "mode": "versionchange", "error": null,
|
|
1150
|
+
objectStore(s) { return storeApi(s, currentTx); },
|
|
1151
|
+
abort() {}, commit() {}
|
|
1152
|
+
};
|
|
1153
|
+
if (typeof req.onupgradeneeded === "function") {
|
|
1154
|
+
req.onupgradeneeded({
|
|
1155
|
+
"target": req, "oldVersion": oldVersion, "newVersion": newVersion
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
saveDb(name, data);
|
|
1159
|
+
currentTx = null;
|
|
1160
|
+
}
|
|
1161
|
+
if (typeof req.onsuccess === "function") {
|
|
1162
|
+
req.onsuccess({ "target": req });
|
|
1163
|
+
}
|
|
1164
|
+
});
|
|
1165
|
+
return req;
|
|
1166
|
+
},
|
|
1167
|
+
deleteDatabase(name) {
|
|
1168
|
+
const req = makeRequest();
|
|
1169
|
+
global.queueMicrotask(function () {
|
|
1170
|
+
try {
|
|
1171
|
+
global.__next2d_storage_save("idb_" + name, "");
|
|
1172
|
+
fireSuccess(req);
|
|
1173
|
+
} catch (e) {
|
|
1174
|
+
fireError(req, e);
|
|
1175
|
+
}
|
|
1176
|
+
});
|
|
1177
|
+
return req;
|
|
1178
|
+
},
|
|
1179
|
+
cmp(a, b) { return cmpKeys(a, b); }
|
|
1180
|
+
};
|
|
1181
|
+
})(globalThis);
|
|
1182
|
+
)JS";
|
|
1183
|
+
const char* const kIndexedDBChunks[] = {
|
|
1184
|
+
kIndexedDB1, kIndexedDB2, kIndexedDB3,
|
|
1185
|
+
};
|
|
1186
|
+
|
|
1187
|
+
const char* const kPolyfillSections[] = {
|
|
1188
|
+
kPolyfills1,
|
|
1189
|
+
kPolyfills2,
|
|
1190
|
+
kPolyfills3,
|
|
1191
|
+
kPolyfills4,
|
|
1192
|
+
kPolyfills5,
|
|
1193
|
+
kPolyfills6,
|
|
1194
|
+
};
|
|
1195
|
+
|
|
1196
|
+
} // namespace
|
|
1197
|
+
|
|
1198
|
+
void InstallPolyfills(v8::Isolate* isolate, v8::Local<v8::Context> context)
|
|
1199
|
+
{
|
|
1200
|
+
v8::Context::Scope cs(context);
|
|
1201
|
+
for (const char* source : kPolyfillSections) {
|
|
1202
|
+
v8::TryCatch tc(isolate);
|
|
1203
|
+
v8::Local<v8::String> src = v8util::Str(isolate, source);
|
|
1204
|
+
v8::Local<v8::Script> script;
|
|
1205
|
+
if (v8::Script::Compile(context, src).ToLocal(&script)) {
|
|
1206
|
+
(void) script->Run(context);
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
// indexedDB は 3 リテラルを連結して 1 スクリプトとして評価する
|
|
1211
|
+
// (単一 IIFE のため分割評価できない)。
|
|
1212
|
+
{
|
|
1213
|
+
std::string idb;
|
|
1214
|
+
for (const char* chunk : kIndexedDBChunks) {
|
|
1215
|
+
idb += chunk;
|
|
1216
|
+
}
|
|
1217
|
+
v8::TryCatch tc(isolate);
|
|
1218
|
+
v8::Local<v8::String> src = v8util::Str(isolate, idb);
|
|
1219
|
+
v8::Local<v8::Script> script;
|
|
1220
|
+
if (v8::Script::Compile(context, src).ToLocal(&script)) {
|
|
1221
|
+
(void) script->Run(context);
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
} // namespace next2d
|