flash
cf71bab92d
This has been in the works for over a month and might break things because it's a very radical change. If it causes you to be unable to join chat, report it on the forum or try joining using the legacy chat on https://sockchat.flashii.net.
215 lines
7.2 KiB
JavaScript
215 lines
7.2 KiB
JavaScript
#include uniqstr.js
|
|
|
|
const WorkerSkeletonObject = function(name, defineObjectMethod, deleteObjectMethod, deleteObject) {
|
|
if(typeof name !== 'string')
|
|
throw 'name must be a string';
|
|
if(typeof defineObjectMethod !== 'function')
|
|
throw 'defineObjectMethod must be a function';
|
|
if(typeof deleteObjectMethod !== 'function')
|
|
throw 'deleteObjectMethod must be a function';
|
|
if(typeof deleteObject !== 'function')
|
|
throw 'deleteObject must be a function';
|
|
|
|
return {
|
|
getObjectName: () => name,
|
|
defineMethod: (...args) => defineObjectMethod(name, ...args),
|
|
deleteMethod: (...args) => deleteObjectMethod(name, ...args),
|
|
destroy: () => deleteObject(name),
|
|
};
|
|
};
|
|
|
|
const WorkerSkeleton = function(globalScope) {
|
|
if(globalScope === undefined)
|
|
globalScope = self;
|
|
|
|
const objects = new Map;
|
|
const handlers = {};
|
|
let initialised = false;
|
|
|
|
const sendPayload = (type, detail) => {
|
|
globalScope.postMessage({ type: type, detail: detail });
|
|
};
|
|
|
|
const sendObjectDefinePayload = (objName, objBody, eventPrefix) => {
|
|
sendPayload('objdef', { object: objName, methods: Object.keys(objBody), eventPrefix: eventPrefix });
|
|
};
|
|
|
|
const defineObject = (objName, objBody, eventPrefix) => {
|
|
if(typeof objName !== 'string')
|
|
throw 'objName must be a string';
|
|
if(typeof eventPrefix !== 'string' && eventPrefix !== undefined)
|
|
throw 'eventPrefix must be string or undefined';
|
|
if(objects.has(objName))
|
|
throw 'objName is already defined';
|
|
|
|
const object = {};
|
|
for(const name in objBody) {
|
|
const item = objBody[name];
|
|
if(typeof item === 'function')
|
|
object[name] = { body: item };
|
|
}
|
|
|
|
objects.set(objName, object);
|
|
if(initialised)
|
|
sendObjectDefinePayload(objName, object, eventPrefix);
|
|
};
|
|
|
|
const deleteObject = objName => {
|
|
if(typeof objName !== 'string')
|
|
throw 'objName must be a string';
|
|
if(!objects.has(objName))
|
|
throw 'objName is not defined';
|
|
|
|
objects.delete(objName);
|
|
if(initialised)
|
|
sendPayload('objdel', { object: objName });
|
|
};
|
|
|
|
const defineObjectMethod = (objName, metName, metBody, returnsObject) => {
|
|
if(typeof objName !== 'string')
|
|
throw 'objName must be a string';
|
|
if(typeof metName !== 'string')
|
|
throw 'metName must be a string';
|
|
if(typeof metBody !== 'function')
|
|
throw 'metBody must be a function';
|
|
|
|
const objBody = objects.get(objName);
|
|
if(objBody === undefined)
|
|
throw 'objName has not been defined';
|
|
|
|
objBody[metName] = { body: metBody, returnsObject: returnsObject === true };
|
|
if(initialised)
|
|
sendPayload('metdef', { object: objName, method: metName });
|
|
};
|
|
|
|
const deleteObjectMethod = (objName, metName) => {
|
|
if(typeof objName !== 'string')
|
|
throw 'objName must be a string';
|
|
if(typeof metName !== 'string')
|
|
throw 'metName must be a string';
|
|
|
|
const objBody = objects.get(objName);
|
|
if(objBody === undefined)
|
|
throw 'objName has not been defined';
|
|
|
|
delete objBody[objName];
|
|
if(initialised)
|
|
sendPayload('metdel', { object: objName, method: metName });
|
|
};
|
|
|
|
const createDispatcher = prefix => {
|
|
if(prefix === undefined)
|
|
prefix = '';
|
|
else if(typeof prefix !== 'string')
|
|
throw 'prefix must be a string or undefined';
|
|
|
|
if(prefix !== '' && !prefix.endsWith(':'))
|
|
prefix += ':';
|
|
|
|
return (name, detail) => sendPayload('evtdisp', { name: prefix + name, detail: detail });
|
|
};
|
|
|
|
defineObject('', {});
|
|
|
|
const defineHandler = (name, handler) => {
|
|
if(typeof name !== 'string')
|
|
throw 'name must be a string';
|
|
if(typeof handler !== 'function')
|
|
throw 'handler must be a function';
|
|
if(name in handlers)
|
|
throw 'name is already defined';
|
|
|
|
handlers[name] = handler;
|
|
};
|
|
|
|
defineHandler('init', () => {
|
|
if(initialised)
|
|
return;
|
|
initialised = true;
|
|
|
|
sendObjectDefinePayload('', objects.get(''));
|
|
});
|
|
|
|
defineHandler('metcall', req => {
|
|
if(typeof req.id !== 'string')
|
|
throw 'call id is not a string';
|
|
|
|
const respond = (id, success, result, mightBeObject) => {
|
|
let isObject = false;
|
|
if(mightBeObject) {
|
|
const resultType = typeof result;
|
|
if(resultType === 'string' && objects.has(result))
|
|
isObject = true;
|
|
else if(resultType === 'object' && result !== null && typeof result.getObjectName === 'function') {
|
|
const objectName = result.getObjectName();
|
|
if(objects.has(objectName)) {
|
|
isObject = true;
|
|
result = objectName;
|
|
}
|
|
}
|
|
}
|
|
|
|
sendPayload('funcret', {
|
|
id: id,
|
|
success: success,
|
|
result: result,
|
|
object: isObject,
|
|
});
|
|
};
|
|
|
|
try {
|
|
if(typeof req.object !== 'string')
|
|
throw 'object name is not a string';
|
|
if(typeof req.method !== 'string')
|
|
throw 'method name is not a string';
|
|
|
|
const object = objects.get(req.object);
|
|
if(object === undefined)
|
|
throw 'object is not defined';
|
|
if(!(req.method in object))
|
|
throw 'method is not defined in object';
|
|
|
|
const args = Array.isArray(req.args) ? req.args : [];
|
|
const info = object[req.method];
|
|
let result = info.body(...args);
|
|
|
|
if(result instanceof Promise) {
|
|
result.then(result => respond(req.id, true, result, info.returnsObject)).catch(ex => respond(req.id, false, ex));
|
|
} else
|
|
respond(req.id, true, result, info.returnsObject);
|
|
} catch(ex) {
|
|
respond(req.id, false, ex);
|
|
}
|
|
});
|
|
|
|
globalScope.addEventListener('message', ev => {
|
|
if(typeof ev.data === 'string') {
|
|
if(ev.data.startsWith('ping:')) {
|
|
globalScope.postMessage(`pong:${ev.data.substring(5)}`);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(typeof ev.data === 'object' && ev.data !== null && typeof ev.data.type === 'string') {
|
|
if(ev.data.type in handlers)
|
|
handlers[ev.data.type](ev.data.detail);
|
|
return;
|
|
}
|
|
});
|
|
|
|
return {
|
|
sendPayload: sendPayload,
|
|
createDispatcher: createDispatcher,
|
|
defineObject: (object, eventPrefix) => {
|
|
if(typeof object !== 'object' || object === null)
|
|
return undefined;
|
|
|
|
const name = MamiUniqueStr(8);
|
|
defineObject(name, object, eventPrefix);
|
|
return new WorkerSkeletonObject(name, defineObjectMethod, deleteObjectMethod, deleteObject);
|
|
},
|
|
defineMethod: (...args) => defineObjectMethod('', ...args),
|
|
deleteMethod: (...args) => deleteObjectMethod('', ...args),
|
|
};
|
|
};
|