#include commitment.js #include xhr.js const Flashii = function(baseUrl) { if(typeof baseUrl !== 'string') throw new Error('baseUrl must be a string'); if(baseUrl.indexOf('//') === 0) baseUrl = window.location.protocol + baseUrl; const sortArray = typeof Array.prototype.toSorted === 'function' ? array => array.toSorted() : array => { array = array.slice(); array.sort(); return array; }; const convertFields = fields => { if(fields === true) return '*'; if(Array.isArray(fields)) return sortArray(fields).join(','); if(typeof fields === 'string') return fields; return null; }; const createUrl = (path, fields=null) => { const url = baseUrl + path; const params = {}; if(fields) params['fields'] = convertFields(fields); return { setParam(name, value) { params[name] = value; }, deleteParam(name) { delete params[name]; }, toString() { let str = url; const queryParts = []; for(const name in params) if(params.hasOwnProperty(name)) queryParts.push(encodeURIComponent(name) + '=' + encodeURIComponent(params[name])); if(queryParts.length > 0) str += (str.indexOf('?') < 0 ? '?' : '&') + queryParts.join('&'); return str; }, }; }; const send = ({ method, path, fresh=false, params=null, fields=null, body=null, headers=null, type='json', authed=false, }) => { return new Commitment((success, fail) => { const url = createUrl(path, fields); if(params) for(const name in params) { if(name === 'fields' || params[name] === null || params[name] === undefined) continue; url.setParam(name, params[name]); } headers ??= {}; if(fresh) headers['Cache-Control'] = 'no-cache'; const options = { type, headers, authed }; $xhr.send(method, url, options, body) .success(success) .fail(fail) .run(); }); }; const fii = {}; fii.v1 = {}; const verifyChatServerId = id => { if(typeof id === 'number') id = id.toString(); if(/^([^0-9]+)$/gu.test(id)) throw new Error('id argument is not an acceptable chat server id.'); return id; }; fii.v1.chat = {}; fii.v1.chat.login = function({ redirect=null, assign=true }) { return new Commitment(success => { redirect ??= `${location.protocol}//${location.host}`; if(typeof redirect !== 'string') throw new Error('redirect must a string.'); const url = createUrl('/v1/chat/login'); url.setParam('redirect', redirect); // intentionally does not call success if(assign) location.assign(url); else success(url); }); }; fii.v1.chat.servers = function({ proto=null, secure=null, fields=null, fresh=false }) { return new Commitment((success, fail) => { const params = {}; if(proto !== null && typeof proto !== 'string') throw new Error('proto must be a string or null'); params['proto'] = proto; if(secure !== null) params['secure'] = secure ? '1' : '0'; send({ method: 'GET', path: '/v1/chat/servers', fields, fresh, params }) .success(({ status, body }) => { if(status === 400) throw new Error('An argument contains an unsupported value.'); if(status > 299) throw new Error(`Failed to fetch chat server list with error code ${status}.`); success(body); }) .fail(fail) .run(); }); }; fii.v1.chat.servers.server = function({ id, fields=null, fresh=false }) { return new Commitment((success, fail) => { id = verifyChatServerId(id); send({ method: 'GET', path: `/v1/chat/servers/${id}`, fields, fresh }) .success(({ status, body }) => { if(status === 400) throw new Error('Fields argument contains unsupported value.'); if(status === 404) throw new Error('No chat server with that id could be found.'); if(status > 299) throw new Error(`Failed to fetch chat server with error code ${status}.`); success(body); }) .fail(fail) .run(); }); }; fii.v1.chat.servers.recommended = function({ proto, secure=null, fields=null, fresh=false }) { return new Commitment((success, fail) => { if(typeof proto !== 'string') throw new Error('proto must be a string'); const params = { proto }; if(secure !== null) params['secure'] = secure ? '1' : '0'; send({ method: 'GET', path: '/v1/chat/servers/recommended', fields, fresh, params }) .success(({ status, body }) => { if(status === 400) throw new Error('An argument contains an unsupported value.'); if(status === 404) throw new Error('Was unable to determine a server based on the requested parameters.'); if(status > 299) throw new Error(`Failed to fetch recommended chat server with error code ${status}.`); success(body); }) .fail(fail) .run(); }); }; fii.v1.chat.token = function() { return new Commitment((success, fail) => { send({ method: 'GET', path: '/v1/chat/token', authed: true, fresh: true }) .success(({ status, body }) => { if(status === 403) throw new Error('You must be logged in to use chat.'); if(status > 299) throw new Error(`Failed to fetch authorization token with error code ${status}.`); success(body); }) .fail(fail) .run(); }); }; const verifyColourPresetName = name => { if(/^([^A-Za-z0-9\-_]+)$/gu.test(name)) throw new Error('name argument is not an acceptable colour preset name.'); return name; }; fii.v1.colours = {}; fii.v1.colours.presets = function({ fields=null, fresh=false }) { return new Commitment((success, fail) => { send({ method: 'GET', path: '/v1/colours/presets', fields, fresh }) .success(({ status, body }) => { if(status === 400) throw new Error('Fields argument contains unsupported value.'); if(status > 299) throw new Error(`Failed to fetch colour presets with error code ${status}.`); success(body); }) .fail(fail) .run(); }); }; fii.v1.colours.presets.preset = function({ name, fields=null, fresh=false }) { return new Commitment((success, fail) => { name = verifyColourPresetName(name); send({ method: 'GET', path: `/v1/colours/presets/${name}`, fields, fresh }) .success(({ status, body }) => { if(status === 400) throw new Error('Fields argument contains unsupported value.'); if(status === 404) throw new Error('Requested colour preset does not exist.'); if(status > 299) throw new Error(`Failed to fetch colour preset "${name}" with error code ${status}.`); success(body); }) .fail(fail) .run(); }); }; const verifyEmoticonId = id => { if(typeof id === 'number') id = id.toString(); if(/^([^0-9]+)$/gu.test(id)) throw new Error('id argument is not an acceptable emoticon id.'); return id; }; fii.v1.emotes = function({ fields=null, fresh=false }) { return new Commitment((success, fail) => { send({ method: 'GET', path: '/v1/emotes', fields, fresh }) .success(({ status, body }) => { if(status === 400) throw new Error('Fields argument contains unsupported value.'); if(status > 299) throw new Error(`Failed to fetch emoticons with error code ${status}.`); success(body); }) .fail(fail) .run(); }); }; fii.v1.emotes.emote = function({ id, fields=null, fresh=false }) { return new Commitment((success, fail) => { id = verifyEmoticonId(id); send({ method: 'GET', path: `/v1/emotes/${id}`, fields, fresh }) .success(({ status, body }) => { if(status === 400) throw new Error('Fields argument contains unsupported value.'); if(status === 404) throw new Error('Requested emoticon does not exist.'); if(status > 299) throw new Error(`Failed to fetch emoticon "${id}" with error code ${status}.`); success(body); }) .fail(fail) .run(); }); }; fii.v1.emotes.emote.strings = function({ id, fields=null, fresh=false }) { return new Commitment((success, fail) => { id = verifyEmoticonId(id); send({ method: 'GET', path: `/v1/emotes/${id}/strings`, fields, fresh }) .success(({ status, body }) => { if(status === 400) throw new Error('Fields argument contains unsupported value.'); if(status === 404) throw new Error('Requested emoticon does not exist.'); if(status > 299) throw new Error(`Failed to fetch emoticon "${id}" with error code ${status}.`); success(body); }) .fail(fail) .run(); }); }; const verifyKaomojiId = id => { if(typeof id === 'number') id = id.toString(); if(/^([^0-9]+)$/gu.test(id)) throw new Error('id argument is not an acceptable kaomoji id.'); return id; }; fii.v1.kaomoji = function({ as=null, fields=null, fresh=false }) { return new Commitment((success, fail) => { send({ method: 'GET', path: '/v1/kaomoji', params: { as }, fields, fresh }) .success(({ status, body }) => { if(status === 400) throw new Error('As or fields argument contains unsupported value.'); if(status > 299) throw new Error(`Failed to fetch kaomoji with error code ${status}.`); success(body); }) .fail(fail) .run(); }); }; fii.v1.kaomoji.kaomoji = function({ id, fields=null, fresh=false }) { return new Commitment((success, fail) => { id = verifyKaomojiId(id); send({ method: 'GET', path: `/v1/kaomoji/${id}`, fields, fresh }) .success(({ status, body }) => { if(status === 400) throw new Error('Fields argument contains unsupported value.'); if(status === 404) throw new Error('Requested kaomoji does not exist.'); if(status > 299) throw new Error(`Failed to fetch kaomoji "${id}" with error code ${status}.`); success(body); }) .fail(fail) .run(); }); }; return fii; };