|
|
|
var Module = typeof Module != 'undefined' ? Module : {}; |
|
|
|
Module['expectedDataFileDownloads'] ??= 0; |
|
Module['expectedDataFileDownloads']++; |
|
(() => { |
|
|
|
var isPthread = typeof ENVIRONMENT_IS_PTHREAD != 'undefined' && ENVIRONMENT_IS_PTHREAD; |
|
var isWasmWorker = typeof ENVIRONMENT_IS_WASM_WORKER != 'undefined' && ENVIRONMENT_IS_WASM_WORKER; |
|
if (isPthread || isWasmWorker) return; |
|
var isNode = typeof process === 'object' && typeof process.versions === 'object' && typeof process.versions.node === 'string'; |
|
function loadPackage(metadata) { |
|
|
|
var PACKAGE_PATH = ''; |
|
if (typeof window === 'object') { |
|
PACKAGE_PATH = window['encodeURIComponent'](window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/')) + '/'); |
|
} else if (typeof process === 'undefined' && typeof location !== 'undefined') { |
|
|
|
PACKAGE_PATH = encodeURIComponent(location.pathname.substring(0, location.pathname.lastIndexOf('/')) + '/'); |
|
} |
|
var PACKAGE_NAME = 'en.data'; |
|
var REMOTE_PACKAGE_BASE = 'en.data'; |
|
var REMOTE_PACKAGE_NAME = Module['locateFile'] ? Module['locateFile'](REMOTE_PACKAGE_BASE, '') : REMOTE_PACKAGE_BASE; |
|
var REMOTE_PACKAGE_SIZE = metadata['remote_package_size']; |
|
|
|
function fetchRemotePackage(packageName, packageSize, callback, errback) { |
|
if (isNode) { |
|
require('fs').readFile(packageName, (err, contents) => { |
|
if (err) { |
|
errback(err); |
|
} else { |
|
callback(contents.buffer); |
|
} |
|
}); |
|
return; |
|
} |
|
Module['dataFileDownloads'] ??= {}; |
|
fetch(packageName) |
|
.catch((cause) => Promise.reject(new Error(`Network Error: ${packageName}`, {cause}))) |
|
.then((response) => { |
|
if (!response.ok) { |
|
return Promise.reject(new Error(`${response.status}: ${response.url}`)); |
|
} |
|
|
|
if (!response.body && response.arrayBuffer) { |
|
return response.arrayBuffer().then(callback); |
|
} |
|
|
|
const reader = response.body.getReader(); |
|
const iterate = () => reader.read().then(handleChunk).catch((cause) => { |
|
return Promise.reject(new Error(`Unexpected error while handling : ${response.url} ${cause}`, {cause})); |
|
}); |
|
|
|
const chunks = []; |
|
const headers = response.headers; |
|
const total = Number(headers.get('Content-Length') ?? packageSize); |
|
let loaded = 0; |
|
|
|
const handleChunk = ({done, value}) => { |
|
if (!done) { |
|
chunks.push(value); |
|
loaded += value.length; |
|
Module['dataFileDownloads'][packageName] = {loaded, total}; |
|
|
|
let totalLoaded = 0; |
|
let totalSize = 0; |
|
|
|
for (const download of Object.values(Module['dataFileDownloads'])) { |
|
totalLoaded += download.loaded; |
|
totalSize += download.total; |
|
} |
|
|
|
Module['setStatus']?.(`Downloading data... (${totalLoaded}/${totalSize})`); |
|
return iterate(); |
|
} else { |
|
const packageData = new Uint8Array(chunks.map((c) => c.length).reduce((a, b) => a + b, 0)); |
|
let offset = 0; |
|
for (const chunk of chunks) { |
|
packageData.set(chunk, offset); |
|
offset += chunk.length; |
|
} |
|
callback(packageData.buffer); |
|
} |
|
}; |
|
|
|
Module['setStatus']?.('Downloading data...'); |
|
return iterate(); |
|
}); |
|
}; |
|
|
|
function handleError(error) { |
|
console.error('package error:', error); |
|
}; |
|
|
|
function runWithFS(Module) { |
|
|
|
function assert(check, msg) { |
|
if (!check) throw msg + new Error().stack; |
|
} |
|
|
|
|
|
function DataRequest(start, end, audio) { |
|
this.start = start; |
|
this.end = end; |
|
this.audio = audio; |
|
} |
|
DataRequest.prototype = { |
|
requests: {}, |
|
open: function(mode, name) { |
|
this.name = name; |
|
this.requests[name] = this; |
|
Module['addRunDependency'](`fp ${this.name}`); |
|
}, |
|
send: function() {}, |
|
onload: function() { |
|
var byteArray = this.byteArray.subarray(this.start, this.end); |
|
this.finish(byteArray); |
|
}, |
|
finish: function(byteArray) { |
|
var that = this; |
|
|
|
Module['FS_createDataFile'](this.name, null, byteArray, true, true, true); |
|
Module['removeRunDependency'](`fp ${that.name}`); |
|
this.requests[this.name] = null; |
|
} |
|
}; |
|
|
|
var files = metadata['files']; |
|
for (var i = 0; i < files.length; ++i) { |
|
new DataRequest(files[i]['start'], files[i]['end'], files[i]['audio'] || 0).open('GET', files[i]['filename']); |
|
} |
|
|
|
var PACKAGE_UUID = metadata['package_uuid']; |
|
var IDB_RO = "readonly"; |
|
var IDB_RW = "readwrite"; |
|
var DB_NAME = "EM_PRELOAD_CACHE"; |
|
var DB_VERSION = 1; |
|
var METADATA_STORE_NAME = 'METADATA'; |
|
var PACKAGE_STORE_NAME = 'PACKAGES'; |
|
function openDatabase(callback, errback) { |
|
if (isNode) { |
|
return errback(); |
|
} |
|
var indexedDB; |
|
if (typeof window === 'object') { |
|
indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; |
|
} else if (typeof location !== 'undefined') { |
|
|
|
indexedDB = self.indexedDB; |
|
} else { |
|
throw 'using IndexedDB to cache data can only be done on a web page or in a web worker'; |
|
} |
|
try { |
|
var openRequest = indexedDB.open(DB_NAME, DB_VERSION); |
|
} catch (e) { |
|
return errback(e); |
|
} |
|
openRequest.onupgradeneeded = (event) => { |
|
var db = (event.target.result); |
|
|
|
if (db.objectStoreNames.contains(PACKAGE_STORE_NAME)) { |
|
db.deleteObjectStore(PACKAGE_STORE_NAME); |
|
} |
|
var packages = db.createObjectStore(PACKAGE_STORE_NAME); |
|
|
|
if (db.objectStoreNames.contains(METADATA_STORE_NAME)) { |
|
db.deleteObjectStore(METADATA_STORE_NAME); |
|
} |
|
var metadata = db.createObjectStore(METADATA_STORE_NAME); |
|
}; |
|
openRequest.onsuccess = (event) => { |
|
var db = (event.target.result); |
|
callback(db); |
|
}; |
|
openRequest.onerror = (error) => errback(error); |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
var CHUNK_SIZE = 64 * 1024 * 1024; |
|
|
|
function cacheRemotePackage( |
|
db, |
|
packageName, |
|
packageData, |
|
packageMeta, |
|
callback, |
|
errback |
|
) { |
|
var transactionPackages = db.transaction([PACKAGE_STORE_NAME], IDB_RW); |
|
var packages = transactionPackages.objectStore(PACKAGE_STORE_NAME); |
|
var chunkSliceStart = 0; |
|
var nextChunkSliceStart = 0; |
|
var chunkCount = Math.ceil(packageData.byteLength / CHUNK_SIZE); |
|
var finishedChunks = 0; |
|
for (var chunkId = 0; chunkId < chunkCount; chunkId++) { |
|
nextChunkSliceStart += CHUNK_SIZE; |
|
var putPackageRequest = packages.put( |
|
packageData.slice(chunkSliceStart, nextChunkSliceStart), |
|
`package/${packageName}/${chunkId}` |
|
); |
|
chunkSliceStart = nextChunkSliceStart; |
|
putPackageRequest.onsuccess = (event) => { |
|
finishedChunks++; |
|
if (finishedChunks == chunkCount) { |
|
var transaction_metadata = db.transaction( |
|
[METADATA_STORE_NAME], |
|
IDB_RW |
|
); |
|
var metadata = transaction_metadata.objectStore(METADATA_STORE_NAME); |
|
var putMetadataRequest = metadata.put( |
|
{ |
|
'uuid': packageMeta.uuid, |
|
'chunkCount': chunkCount |
|
}, |
|
`metadata/${packageName}` |
|
); |
|
putMetadataRequest.onsuccess = (event) => callback(packageData); |
|
putMetadataRequest.onerror = (error) => errback(error); |
|
} |
|
}; |
|
putPackageRequest.onerror = (error) => errback(error); |
|
} |
|
} |
|
|
|
|
|
function checkCachedPackage(db, packageName, callback, errback) { |
|
var transaction = db.transaction([METADATA_STORE_NAME], IDB_RO); |
|
var metadata = transaction.objectStore(METADATA_STORE_NAME); |
|
var getRequest = metadata.get(`metadata/${packageName}`); |
|
getRequest.onsuccess = (event) => { |
|
var result = event.target.result; |
|
if (!result) { |
|
return callback(false, null); |
|
} else { |
|
return callback(PACKAGE_UUID === result['uuid'], result); |
|
} |
|
}; |
|
getRequest.onerror = (error) => errback(error); |
|
} |
|
|
|
function fetchCachedPackage(db, packageName, metadata, callback, errback) { |
|
var transaction = db.transaction([PACKAGE_STORE_NAME], IDB_RO); |
|
var packages = transaction.objectStore(PACKAGE_STORE_NAME); |
|
|
|
var chunksDone = 0; |
|
var totalSize = 0; |
|
var chunkCount = metadata['chunkCount']; |
|
var chunks = new Array(chunkCount); |
|
|
|
for (var chunkId = 0; chunkId < chunkCount; chunkId++) { |
|
var getRequest = packages.get(`package/${packageName}/${chunkId}`); |
|
getRequest.onsuccess = (event) => { |
|
if (!event.target.result) { |
|
errback(new Error(`CachedPackageNotFound for: ${packageName}`)); |
|
return; |
|
} |
|
|
|
if (chunkCount == 1) { |
|
callback(event.target.result); |
|
} else { |
|
chunksDone++; |
|
totalSize += event.target.result.byteLength; |
|
chunks.push(event.target.result); |
|
if (chunksDone == chunkCount) { |
|
if (chunksDone == 1) { |
|
callback(event.target.result); |
|
} else { |
|
var tempTyped = new Uint8Array(totalSize); |
|
var byteOffset = 0; |
|
for (var chunkId in chunks) { |
|
var buffer = chunks[chunkId]; |
|
tempTyped.set(new Uint8Array(buffer), byteOffset); |
|
byteOffset += buffer.byteLength; |
|
buffer = undefined; |
|
} |
|
chunks = undefined; |
|
callback(tempTyped.buffer); |
|
tempTyped = undefined; |
|
} |
|
} |
|
} |
|
}; |
|
getRequest.onerror = (error) => errback(error); |
|
} |
|
} |
|
|
|
function processPackageData(arrayBuffer) { |
|
assert(arrayBuffer, 'Loading data file failed.'); |
|
assert(arrayBuffer.constructor.name === ArrayBuffer.name, 'bad input to processPackageData'); |
|
var byteArray = new Uint8Array(arrayBuffer); |
|
var curr; |
|
|
|
DataRequest.prototype.byteArray = byteArray; |
|
var files = metadata['files']; |
|
for (var i = 0; i < files.length; ++i) { |
|
DataRequest.prototype.requests[files[i].filename].onload(); |
|
} Module['removeRunDependency']('datafile_en.data'); |
|
|
|
}; |
|
Module['addRunDependency']('datafile_en.data'); |
|
|
|
Module['preloadResults'] ??= {}; |
|
|
|
function preloadFallback(error) { |
|
console.error(error); |
|
console.error('falling back to default preload behavior'); |
|
fetchRemotePackage(REMOTE_PACKAGE_NAME, REMOTE_PACKAGE_SIZE, processPackageData, handleError); |
|
}; |
|
|
|
openDatabase( |
|
(db) => checkCachedPackage(db, PACKAGE_PATH + PACKAGE_NAME, |
|
(useCached, metadata) => { |
|
Module['preloadResults'][PACKAGE_NAME] = {fromCache: useCached}; |
|
if (useCached) { |
|
fetchCachedPackage(db, PACKAGE_PATH + PACKAGE_NAME, metadata, processPackageData, preloadFallback); |
|
} else { |
|
fetchRemotePackage(REMOTE_PACKAGE_NAME, REMOTE_PACKAGE_SIZE, |
|
(packageData) => { |
|
cacheRemotePackage(db, PACKAGE_PATH + PACKAGE_NAME, packageData, {uuid:PACKAGE_UUID}, processPackageData, |
|
(error) => { |
|
console.error(error); |
|
processPackageData(packageData); |
|
}); |
|
} |
|
, preloadFallback); |
|
} |
|
}, preloadFallback) |
|
, preloadFallback); |
|
|
|
Module['setStatus']?.('Downloading...'); |
|
|
|
} |
|
if (Module['calledRun']) { |
|
runWithFS(Module); |
|
} else { |
|
(Module['preRun'] ??= []).push(runWithFS); |
|
} |
|
|
|
Module['removeRunDependency']('en.js.metadata'); |
|
} |
|
|
|
function runMetaWithFS() { |
|
Module['addRunDependency']('en.js.metadata'); |
|
var metadataUrl = Module['locateFile'] ? Module['locateFile']('en.js.metadata', '') : 'en.js.metadata'; |
|
if (isNode) { |
|
require('fs').readFile(metadataUrl, 'utf8', (err, contents) => { |
|
if (err) { |
|
return Promise.reject(err); |
|
} else { |
|
loadPackage(JSON.parse(contents)); |
|
} |
|
}); |
|
return; |
|
} |
|
fetch(metadataUrl) |
|
.then((response) => { |
|
if (response.ok) { |
|
return response.json(); |
|
} |
|
return Promise.reject(new Error(`${response.status}: ${response.url}`)); |
|
}) |
|
.then(loadPackage); |
|
} |
|
|
|
if (Module['calledRun']) { |
|
runMetaWithFS(); |
|
} else { |
|
(Module['preRun'] ??= []).push(runMetaWithFS); |
|
} |
|
|
|
})(); |
|
|