diff options
| author | caleb.mabry.15@cnu.edu <caleb.mabry.15@cnu.edu> | 2022-03-08 23:07:09 -0500 |
|---|---|---|
| committer | caleb.mabry.15@cnu.edu <caleb.mabry.15@cnu.edu> | 2022-03-08 23:07:09 -0500 |
| commit | 6f4874fa20d4fa156dd762d5dacefd8a2e656bf0 (patch) | |
| tree | b2258517e17d4b1971789959fa3868c5c58d2e59 | |
| parent | 63a282481d033640a87f97b74f31136410c93717 (diff) | |
Lots of changes
| -rw-r--r-- | webAO/client.js | 241 | ||||
| -rw-r--r-- | webAO/encoding.js | 2 | ||||
| -rw-r--r-- | webAO/master.js | 8 | ||||
| -rw-r--r-- | webAO/services/request.js | 34 | ||||
| -rw-r--r-- | webAO/utils/calculateGifLength.js | 27 | ||||
| -rw-r--r-- | webAO/utils/calculateWebpLength.js | 22 | ||||
| -rw-r--r-- | webAO/utils/calculatorHandler.js | 7 | ||||
| -rw-r--r-- | webAO/utils/getCookie.js | 26 | ||||
| -rw-r--r-- | webAO/utils/setCookie.js | 10 |
9 files changed, 229 insertions, 148 deletions
diff --git a/webAO/client.js b/webAO/client.js index ac5cfeb..1d04349 100644 --- a/webAO/client.js +++ b/webAO/client.js @@ -8,7 +8,7 @@ import FingerprintJS from '@fingerprintjs/fingerprintjs' import { EventEmitter } from 'events'; import { - escapeChat, encodeChat, prepChat, safe_tags, + escapeChat, encodeChat, prepChat, safeTags, } from './encoding.js'; // Load some defaults for the background and evidence dropdowns @@ -19,6 +19,10 @@ import vanilla_evidence_arr from "./constants/evidence.js"; import chatbox_arr from './styles/chatbox/chatboxes.js'; import iniParse from './iniParse'; +import calculatorHandler from './utils/calculatorHandler.js'; +import getCookie from './utils/getCookie.js'; +import setCookie from './utils/setCookie.js'; +import request from './services/request.js'; const version = process.env.npm_package_version; @@ -31,8 +35,7 @@ location.search.substr(1).split('&').forEach((item) => { queryDict[item.split('=')[0]] = item.split('=')[1]; }); -const serverIP = queryDict.ip; -let { mode } = queryDict; +let { ip: serverIP, mode } = queryDict; // Unless there is an asset URL specified, use the wasabi one const DEFAULT_HOST = 'http://attorneyoffline.de/base/'; @@ -50,25 +53,24 @@ const transparentPNG = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCA let oldLoading = false; // presettings -const selectedEffect = 0; let selectedMenu = 1; let selectedShout = 0; let extrafeatures = []; let hdid; -const options = { fonts: { extendedJsFonts: true, userDefinedFonts: ['Ace Attorney', '8bitoperator', 'DINEngschrift'] }, excludes: { userAgent: true, enumerateDevices: true } }; + function isLowMemory() { if (/webOS|iPod|BlackBerry|BB|PlayBook|IEMobile|Windows Phone|Kindle|Silk|PlayStation|Nintendo|Opera Mini/i.test(navigator.userAgent)) { oldLoading = true; } } - const fpPromise = FingerprintJS.load() fpPromise .then(fp => fp.get()) .then(result => { + hdid = result.visitorId; client = new Client(serverIP); viewport = new Viewport(); @@ -82,7 +84,6 @@ let lastICMessageTime = new Date(0); class Client extends EventEmitter { constructor(address) { super(); - console.log(`mode: ${mode}`); if (mode !== 'replay') { this.serv = new WebSocket(`ws://${address}`); // Assign the websocket events @@ -610,7 +611,7 @@ class Client extends EventEmitter { document.getElementById('client_inner_chat').innerHTML = ''; const char_id = Number(args[9]); - const char_name = safe_tags(args[3]); + const char_name = safeTags(args[3]); let msg_nameplate = args[3]; let msg_blips = 'male'; @@ -638,21 +639,21 @@ class Client extends EventEmitter { if (char_muted === false) { let chatmsg = { - deskmod: safe_tags(args[1]).toLowerCase(), - preanim: safe_tags(args[2]).toLowerCase(), // get preanim + deskmod: safeTags(args[1]).toLowerCase(), + preanim: safeTags(args[2]).toLowerCase(), // get preanim nameplate: msg_nameplate, chatbox: char_chatbox, name: char_name, - sprite: safe_tags(args[4]).toLowerCase(), + sprite: safeTags(args[4]).toLowerCase(), content: prepChat(args[5]), // Escape HTML tags side: args[6].toLowerCase(), - sound: safe_tags(args[7]).toLowerCase(), - blips: safe_tags(msg_blips), + sound: safeTags(args[7]).toLowerCase(), + blips: safeTags(msg_blips), type: Number(args[8]), charid: char_id, snddelay: Number(args[10]), objection: Number(args[11]), - evidence: safe_tags(args[12]), + evidence: safeTags(args[12]), flip: Number(args[13]), flash: Number(args[14]), color: Number(args[15]), @@ -660,10 +661,10 @@ class Client extends EventEmitter { if (extrafeatures.includes('cccc_ic_support')) { const extra_cccc = { - showname: safe_tags(args[16]), + showname: safeTags(args[16]), other_charid: Number(args[17]), - other_name: safe_tags(args[18]), - other_emote: safe_tags(args[19]), + other_name: safeTags(args[18]), + other_emote: safeTags(args[19]), self_offset: args[20].split('<and>'), // HACK: here as well, client is fucked and uses this instead of & other_offset: args[21].split('<and>'), other_flip: Number(args[22]), @@ -675,9 +676,9 @@ class Client extends EventEmitter { const extra_27 = { looping_sfx: Number(args[24]), screenshake: Number(args[25]), - frame_screenshake: safe_tags(args[26]), - frame_realization: safe_tags(args[27]), - frame_sfx: safe_tags(args[28]), + frame_screenshake: safeTags(args[26]), + frame_realization: safeTags(args[27]), + frame_sfx: safeTags(args[28]), }; chatmsg = Object.assign(extra_27, chatmsg); @@ -841,9 +842,9 @@ class Client extends EventEmitter { } const mute_select = document.getElementById('mute_select'); - mute_select.add(new Option(safe_tags(chargs[0]), charid)); + mute_select.add(new Option(safeTags(chargs[0]), charid)); const pair_select = document.getElementById('pair_select'); - pair_select.add(new Option(safe_tags(chargs[0]), charid)); + pair_select.add(new Option(safeTags(chargs[0]), charid)); // sometimes ini files lack important settings const default_options = { @@ -863,13 +864,13 @@ class Client extends EventEmitter { cini.emotions = Object.assign(default_emotions, cini.emotions); this.chars[charid] = { - name: safe_tags(chargs[0]), - showname: safe_tags(cini.options.showname), - desc: safe_tags(chargs[1]), - blips: safe_tags(cini.options.blips).toLowerCase(), - gender: safe_tags(cini.options.gender).toLowerCase(), - side: safe_tags(cini.options.side).toLowerCase(), - chat: (cini.options.chat === '') ? safe_tags(cini.options.chat).toLowerCase() : safe_tags(cini.options.category).toLowerCase(), + name: safeTags(chargs[0]), + showname: safeTags(cini.options.showname), + desc: safeTags(chargs[1]), + blips: safeTags(cini.options.blips).toLowerCase(), + gender: safeTags(cini.options.gender).toLowerCase(), + side: safeTags(cini.options.side).toLowerCase(), + chat: (cini.options.chat === '') ? safeTags(cini.options.chat).toLowerCase() : safeTags(cini.options.category).toLowerCase(), evidence: chargs[3], icon, inifile: cini, @@ -879,7 +880,7 @@ class Client extends EventEmitter { if (this.chars[charid].blips === '') { this.chars[charid].blips = this.chars[charid].gender; } const iniedit_select = document.getElementById('client_ininame'); - iniedit_select.add(new Option(safe_tags(chargs[0]))); + iniedit_select.add(new Option(safeTags(chargs[0]))); } else { console.warn(`missing charid ${charid}`); const img = document.getElementById(`demo_${charid}`); @@ -949,7 +950,7 @@ class Client extends EventEmitter { this.evidences[i - 1] = { name: prepChat(arg[0]), desc: prepChat(arg[1]), - filename: safe_tags(arg[2]), + filename: safeTags(arg[2]), icon: `${AO_HOST}evidence/${encodeURI(arg[2].toLowerCase())}`, }; } @@ -1032,15 +1033,8 @@ class Client extends EventEmitter { } isAudio(trackname) { - if (trackname.endsWith('.wav') - || trackname.endsWith('.mp3') - || trackname.endsWith('.mp4') - || trackname.endsWith('.ogg') - || trackname.endsWith('.opus')) // NOT category markers - { - return true; - } - return false; + const audioEndings = ['.wav', '.mp3', '.ogg', '.opus'] + return audioEndings.filter(ending => trackname.endsWith(ending)).length === 1 } addTrack(trackname) { @@ -1105,7 +1099,7 @@ class Client extends EventEmitter { for (let i = 2; i < args.length - 1; i++) { if (i % 2 === 0) { document.getElementById('client_loadingtext').innerHTML = `Loading Music ${args[1]}/${this.music_list_length}`; - const trackname = safe_tags(args[i]); + const trackname = safeTags(args[i]); const trackindex = args[i - 1]; if (this.musics_time) { this.addTrack(trackname); @@ -1136,7 +1130,7 @@ class Client extends EventEmitter { for (let i = 1; i < args.length - 1; i++) { // Check when found the song for the first time - const trackname = safe_tags(args[i]); + const trackname = safeTags(args[i]); const trackindex = i - 1; document.getElementById('client_loadingtext').innerHTML = `Loading Music ${i}/${this.music_list_length}`; if (this.musics_time) { @@ -1163,7 +1157,7 @@ class Client extends EventEmitter { for (let i = 1; i < args.length - 1; i++) { // Check when found the song for the first time - this.addTrack(safe_tags(args[i])); + this.addTrack(safeTags(args[i])); } } @@ -1175,7 +1169,7 @@ class Client extends EventEmitter { this.resetAreaList(); for (let i = 1; i < args.length - 1; i++) { - this.createArea(i - 1, safe_tags(args[i])); + this.createArea(i - 1, safeTags(args[i])); } } @@ -1204,7 +1198,7 @@ class Client extends EventEmitter { * @param {Array} args kick reason */ handleKK(args) { - this.handleBans('Kicked', safe_tags(args[1])); + this.handleBans('Kicked', safeTags(args[1])); } /** @@ -1213,7 +1207,7 @@ class Client extends EventEmitter { * @param {Array} args ban reason */ handleKB(args) { - this.handleBans('Banned', safe_tags(args[1])); + this.handleBans('Banned', safeTags(args[1])); this.banned = true; } @@ -1223,7 +1217,7 @@ class Client extends EventEmitter { * @param {Array} args ban reason */ handleBB(args) { - alert(safe_tags(args[1])); + alert(safeTags(args[1])); } /** @@ -1232,7 +1226,7 @@ class Client extends EventEmitter { * @param {Array} args ban reason */ handleBD(args) { - this.handleBans('Banned', safe_tags(args[1])); + this.handleBans('Banned', safeTags(args[1])); this.banned = true; } @@ -1256,7 +1250,7 @@ class Client extends EventEmitter { * @param {Array} args packet arguments */ handleBN(args) { - viewport.bgname = safe_tags(args[1]); + viewport.bgname = safeTags(args[1]); const bgfolder = viewport.bgFolder; const bg_index = getIndexFromSelect('bg_select', viewport.bgname); document.getElementById('bg_select').selectedIndex = bg_index; @@ -1427,13 +1421,13 @@ class Client extends EventEmitter { this.areas[i].players = Number(args[i + 1]); break; case 1: // status - this.areas[i].status = safe_tags(args[i + 1]); + this.areas[i].status = safeTags(args[i + 1]); break; case 2: - this.areas[i].cm = safe_tags(args[i + 1]); + this.areas[i].cm = safeTags(args[i + 1]); break; case 3: - this.areas[i].locked = safe_tags(args[i + 1]); + this.areas[i].locked = safeTags(args[i + 1]); break; } @@ -1613,7 +1607,7 @@ class Client extends EventEmitter { iniedit_select.innerHTML = ''; function addIniswap(value) { - iniedit_select.add(new Option(safe_tags(value))); + iniedit_select.add(new Option(safeTags(value))); } addIniswap(me.name); @@ -1931,13 +1925,19 @@ class Viewport { * silently fail and return 0 instead. * @param {string} filename the animation file name */ - async getAnimLength(filename) { - try { - const file = await requestBuffer(filename); - return this.calculateGifLength(file); - } catch (err) { - return 0; - } + + async getAnimLength(url) { + const extensions = ['.gif','.webp'] + for (const extension of extensions) { + const urlWithExtension = url+extension + const exists = await fileExists(urlWithExtension) + if (exists) { + const fileBuffer = await requestBuffer(urlWithExtension) + const length = calculatorHandler[extension](fileBuffer) + return length + } + }; + return 0 } oneSuccess(promises) { @@ -2041,19 +2041,30 @@ class Viewport { const png_s = document.getElementById(`client_${position}${pairID}_png`); const apng_s = document.getElementById(`client_${position}${pairID}_apng`); const webp_s = document.getElementById(`client_${position}${pairID}_webp`); - - if (this.lastChar !== this.chatmsg.name) { - // hide the last sprite - gif_s.src = transparentPNG; - png_s.src = transparentPNG; - apng_s.src = transparentPNG; - webp_s.src = transparentPNG; + const extensionsMap = { + '.gif': gif_s, + '.png': png_s, + '.apng': apng_s, + '.webp': webp_s } - gif_s.src = `${characterFolder}${encodeURI(charactername)}/${encodeURI(prefix)}${encodeURI(emotename)}.gif`; - png_s.src = `${characterFolder}${encodeURI(charactername)}/${encodeURI(emotename)}.png`; - apng_s.src = `${characterFolder}${encodeURI(charactername)}/${encodeURI(prefix)}${encodeURI(emotename)}.apng`; - webp_s.src = `${characterFolder}${encodeURI(charactername)}/${encodeURI(prefix)}${encodeURI(emotename)}.webp`; + for (const [extension, htmlElement] of Object.entries(extensionsMap)) { + // Hides all sprites before creating a new sprite + if (this.lastChar !== this.chatmsg.name) { + htmlElement.src = transparentPNG + } + let url; + if (extension === '.png') { + url = `${characterFolder}${encodeURI(charactername)}/${encodeURI(emotename)}${extension}` + } else { + url = `${characterFolder}${encodeURI(charactername)}/${encodeURI(prefix)}${encodeURI(emotename)}${extension}` + } + const exists = fileExistsSync(url) + if (exists) { + htmlElement.src = url + return; + } + } } /** @@ -2156,7 +2167,7 @@ class Viewport { // Hide message box chatContainerBox.style.opacity = 0; // If preanim existed then determine the length - gifLength = await this.getAnimLength(`${AO_HOST}characters/${encodeURI(this.chatmsg.name.toLowerCase())}/${encodeURI(this.chatmsg.preanim)}.gif`); + gifLength = await this.getAnimLength(`${AO_HOST}characters/${encodeURI(this.chatmsg.name.toLowerCase())}/${encodeURI(this.chatmsg.preanim)}`); this.chatmsg.startspeaking = false; break; // case 5: @@ -2269,7 +2280,7 @@ class Viewport { const effectlayer = document.getElementById('client_fg'); let charLayers = document.getElementById('client_char'); let pairLayers = document.getElementById('client_pair_char'); - + if ('def,pro,wit'.includes(this.chatmsg.side)) { charLayers = document.getElementById(`client_${this.chatmsg.side}_char`); pairLayers = document.getElementById(`client_${this.chatmsg.side}_pair_char`); @@ -2314,9 +2325,10 @@ class Viewport { this.chatmsg.startspeaking = true; } else if (this.textTimer >= this.shoutTimer + this.chatmsg.preanimdelay && !this.chatmsg.startpreanim) { if (this.chatmsg.startspeaking) { + // Evidence Bullshit if (this.chatmsg.evidence > 0) { // Prepare evidence - eviBox.src = safe_tags(client.evidences[this.chatmsg.evidence - 1].icon); + eviBox.src = safeTags(client.evidences[this.chatmsg.evidence - 1].icon); eviBox.style.width = 'auto'; eviBox.style.height = '36.5%'; @@ -2399,41 +2411,6 @@ class Viewport { } } -/** - * read a cookie from storage - * got this from w3schools - * https://www.w3schools.com/js/js_cookies.asp - * @param {String} cname The name of the cookie to return - */ -function getCookie(cname) { - try { - const name = `${cname}=`; - const decodedCookie = decodeURIComponent(document.cookie); - const ca = decodedCookie.split(';'); - for (let i = 0; i < ca.length; i++) { - let c = ca[i]; - while (c.charAt(0) === ' ') { - c = c.substring(1); - } - if (c.indexOf(name) === 0) { - return c.substring(name.length, c.length); - } - } - return ''; - } catch (error) { - return ''; - } -} - -/** - * set a cookie - * the version from w3schools expects these to expire - * @param {String} cname The name of the cookie to return - * @param {String} value The value of that cookie option - */ -function setCookie(cname, value) { - document.cookie = `${cname}=${value}`; -} /** * Triggered when the Return key is pressed on the out-of-character chat input box. @@ -2802,7 +2779,6 @@ window.imgError = imgError; * @param {HTMLImageElement} image the element containing the missing sound */ export function opusCheck(channel) { - console.info(channel); console.info(`failed to load sound ${channel.src}`); let oldsrc = ''; oldsrc = channel.src; @@ -2848,39 +2824,7 @@ async function requestBuffer(url) { }); } -/** - * Make a GET request for a specific URI. - * @param {string} url the URI to be requested - * @returns response data - * @throws {Error} if status code is not 2xx, or a network error occurs - */ -async function request(url) { - return new Promise((resolve, reject) => { - const xhr = new XMLHttpRequest(); - xhr.responseType = 'text'; - xhr.addEventListener('error', () => { - const err = new Error(`Request for ${url} failed: ${xhr.statusText}`); - err.code = xhr.status; - reject(err); - }); - xhr.addEventListener('abort', () => { - const err = new Error(`Request for ${url} was aborted!`); - err.code = xhr.status; - reject(err); - }); - xhr.addEventListener('load', () => { - if (xhr.status < 200 || xhr.status >= 300) { - const err = new Error(`Request for ${url} failed with status code ${xhr.status}`); - err.code = xhr.status; - reject(err); - } else { - resolve(xhr.response); - } - }); - xhr.open('GET', url, true); - xhr.send(); - }); -} + /** * Checks if a file exists at the specified URI. @@ -2894,6 +2838,17 @@ async function fileExists(url) { return false; } } +const fileExistsSync = (url) => { + try { + var http = new XMLHttpRequest(); + http.open('HEAD', url, false); + http.send(); + return http.status!=404; + } catch (e) { + return false + } +} + /** * Triggered when the reconnect button is pushed. diff --git a/webAO/encoding.js b/webAO/encoding.js index 21b5aa7..e6cc3ae 100644 --- a/webAO/encoding.js +++ b/webAO/encoding.js @@ -28,7 +28,7 @@ export function unescapeChat(estring) { * XXX: This is unnecessary if we use `createTextNode` instead! * @param {string} unsafe an unsanitized string */ -export function safe_tags(unsafe) { +export function safeTags(unsafe) { if (unsafe) { return unsafe .replace(/>/g, '>') diff --git a/webAO/master.js b/webAO/master.js index 4ff6402..d03f0d7 100644 --- a/webAO/master.js +++ b/webAO/master.js @@ -1,6 +1,6 @@ import FingerprintJS from '@fingerprintjs/fingerprintjs' -import { unescapeChat, safe_tags } from './encoding.js'; +import { unescapeChat, safeTags } from './encoding.js'; const version = process.env.npm_package_version; @@ -50,7 +50,7 @@ export function setServ(ID) { if (document.getElementById(`server${ID}`).className === '') { checkOnline(ID, `${servers[ID].ip}:${servers[ID].port}`); } if (servers[ID].description !== undefined) { - document.getElementById('serverdescription_content').innerHTML = `<b>${servers[ID].online}</b><br>${safe_tags(servers[ID].description)}`; + document.getElementById('serverdescription_content').innerHTML = `<b>${servers[ID].online}</b><br>${safeTags(servers[ID].description)}`; } else { document.getElementById('serverdescription_content').innerHTML = ''; } @@ -107,7 +107,7 @@ function checkOnline(serverID, coIP) { oserv.close(); return; } - if (serverID === selectedServer) { document.getElementById('serverdescription_content').innerHTML = `<b>${servers[serverID].online}</b><br>${safe_tags(servers[serverID].description)}`; } + if (serverID === selectedServer) { document.getElementById('serverdescription_content').innerHTML = `<b>${servers[serverID].online}</b><br>${safeTags(servers[serverID].description)}`; } } // assign the callbacks @@ -145,7 +145,7 @@ function onMessage(e) { const asset = args[4] ? `&asset=${args[4]}` : ''; document.getElementById('masterlist').innerHTML - += `<li id="server${i}" onmouseover="setServ(${i})"><p>${safe_tags(servers[i].name)}</p>` + += `<li id="server${i}" onmouseover="setServ(${i})"><p>${safeTags(servers[i].name)}</p>` + `<a class="button" href="client.html?mode=watch&ip=${ipport}${asset}">Watch</a>` + `<a class="button" href="client.html?mode=join&ip=${ipport}${asset}">Join</a></li>`; } diff --git a/webAO/services/request.js b/webAO/services/request.js new file mode 100644 index 0000000..f21f881 --- /dev/null +++ b/webAO/services/request.js @@ -0,0 +1,34 @@ +/** + * Make a GET request for a specific URI. + * @param {string} url the URI to be requested + * @returns response data + * @throws {Error} if status code is not 2xx, or a network error occurs + */ + const request = async url => { + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest(); + xhr.responseType = 'text'; + xhr.addEventListener('error', () => { + const err = new Error(`Request for ${url} failed: ${xhr.statusText}`); + err.code = xhr.status; + reject(err); + }); + xhr.addEventListener('abort', () => { + const err = new Error(`Request for ${url} was aborted!`); + err.code = xhr.status; + reject(err); + }); + xhr.addEventListener('load', () => { + if (xhr.status < 200 || xhr.status >= 300) { + const err = new Error(`Request for ${url} failed with status code ${xhr.status}`); + err.code = xhr.status; + reject(err); + } else { + resolve(xhr.response); + } + }); + xhr.open('GET', url, true); + xhr.send(); + }); + } + export default request
\ No newline at end of file diff --git a/webAO/utils/calculateGifLength.js b/webAO/utils/calculateGifLength.js new file mode 100644 index 0000000..a57264d --- /dev/null +++ b/webAO/utils/calculateGifLength.js @@ -0,0 +1,27 @@ + /** + * Adds up the frame delays to find out how long a GIF is + * I totally didn't steal this + * @param {data} gifFile the GIF data + */ +const calculateGifLength = (gifFile) => { + const d = new Uint8Array(gifFile); + // Thanks to http://justinsomnia.org/2006/10/gif-animation-duration-calculation/ + // And http://www.w3.org/Graphics/GIF/spec-gif89a.txt + let duration = 0; + for (let i = 0; i < d.length; i++) { + // Find a Graphic Control Extension hex(21F904__ ____ __00) + if (d[i] === 0x21 + && d[i + 1] === 0xF9 + && d[i + 2] === 0x04 + && d[i + 7] === 0x00) { + // Swap 5th and 6th bytes to get the delay per frame + const delay = (d[i + 5] << 8) | (d[i + 4] & 0xFF); + + // Should be aware browsers have a minimum frame delay + // e.g. 6ms for IE, 2ms modern browsers (50fps) + duration += delay < 2 ? 10 : delay; + } + } + return duration * 10; + } + export default calculateGifLength
\ No newline at end of file diff --git a/webAO/utils/calculateWebpLength.js b/webAO/utils/calculateWebpLength.js new file mode 100644 index 0000000..38da7c1 --- /dev/null +++ b/webAO/utils/calculateWebpLength.js @@ -0,0 +1,22 @@ +const calculateWebpLength = (webpFile) => { + let d = new Uint8Array(webpFile); + // https://developers.google.com/speed/webp/docs/riff_container#animation + let duration = 0; + for (var i = 0; i < d.length; i++) { + // Find ANMF header (41 4E 4D 46) + if (d[i] === 0x41 + && d[i + 1] === 0x4E + && d[i + 2] === 0x4D + && d[i + 3] === 0x46) { + // Swap 5th and 6th bytes to get the delay per frame + let delay = (d[i + 21] << 8) | (d[i + 20] & 0xFF); + + // Should be aware browsers have a minimum frame delay + // e.g. 6ms for IE, 2ms modern browsers (50fps) + duration += delay < 2 ? 10 : delay; + } + } + return duration; +} + +export default calculateWebpLength
\ No newline at end of file diff --git a/webAO/utils/calculatorHandler.js b/webAO/utils/calculatorHandler.js new file mode 100644 index 0000000..5ed8a43 --- /dev/null +++ b/webAO/utils/calculatorHandler.js @@ -0,0 +1,7 @@ +import calculateGifLength from "./calculateGifLength"; +import calculateWebpLength from "./calculateWebpLength"; + +export default { + '.gif': calculateGifLength, + '.webp': calculateWebpLength +}
\ No newline at end of file diff --git a/webAO/utils/getCookie.js b/webAO/utils/getCookie.js new file mode 100644 index 0000000..000c870 --- /dev/null +++ b/webAO/utils/getCookie.js @@ -0,0 +1,26 @@ +/** + * read a cookie from storage + * got this from w3schools + * https://www.w3schools.com/js/js_cookies.asp + * @param {String} cname The name of the cookie to return + */ +const getCookie = (cname) => { + try { + const name = `${cname}=`; + const decodedCookie = decodeURIComponent(document.cookie); + const ca = decodedCookie.split(';'); + for (let i = 0; i < ca.length; i++) { + let c = ca[i]; + while (c.charAt(0) === ' ') { + c = c.substring(1); + } + if (c.indexOf(name) === 0) { + return c.substring(name.length, c.length); + } + } + return ''; + } catch (error) { + return ''; + } + } + export default getCookie;
\ No newline at end of file diff --git a/webAO/utils/setCookie.js b/webAO/utils/setCookie.js new file mode 100644 index 0000000..d3699a2 --- /dev/null +++ b/webAO/utils/setCookie.js @@ -0,0 +1,10 @@ +/** + * set a cookie + * the version from w3schools expects these to expire + * @param {String} cname The name of the cookie to return + * @param {String} value The value of that cookie option + */ + const setCookie = (cname, value) => { + document.cookie = `${cname}=${value}`; + } + export default setCookie
\ No newline at end of file |
