diff --git a/javascript/_utils.js b/javascript/_utils.js new file mode 100644 index 0000000..2550d0a --- /dev/null +++ b/javascript/_utils.js @@ -0,0 +1,94 @@ +// Parse the CSV file into a 2D array. Doesn't use regex, so it is very lightweight. +function parseCSV(str) { + var arr = []; + var quote = false; // 'true' means we're inside a quoted field + + // Iterate over each character, keep track of current row and column (of the returned array) + for (var row = 0, col = 0, c = 0; c < str.length; c++) { + var cc = str[c], nc = str[c + 1]; // Current character, next character + arr[row] = arr[row] || []; // Create a new row if necessary + arr[row][col] = arr[row][col] || ''; // Create a new column (start with empty string) if necessary + + // If the current character is a quotation mark, and we're inside a + // quoted field, and the next character is also a quotation mark, + // add a quotation mark to the current column and skip the next character + if (cc == '"' && quote && nc == '"') { arr[row][col] += cc; ++c; continue; } + + // If it's just one quotation mark, begin/end quoted field + if (cc == '"') { quote = !quote; continue; } + + // If it's a comma and we're not in a quoted field, move on to the next column + if (cc == ',' && !quote) { ++col; continue; } + + // If it's a newline (CRLF) and we're not in a quoted field, skip the next character + // and move on to the next row and move to column 0 of that new row + if (cc == '\r' && nc == '\n' && !quote) { ++row; col = 0; ++c; continue; } + + // If it's a newline (LF or CR) and we're not in a quoted field, + // move on to the next row and move to column 0 of that new row + if (cc == '\n' && !quote) { ++row; col = 0; continue; } + if (cc == '\r' && !quote) { ++row; col = 0; continue; } + + // Otherwise, append the current character to the current column + arr[row][col] += cc; + } + return arr; +} + +// Load file +async function readFile(filePath, json = false) { + let response = await fetch(`file=${filePath}`); + + if (response.status != 200) { + console.error(`Error loading file "${filePath}": ` + response.status, response.statusText); + return null; + } + + if (json) + return await response.json(); + else + return await response.text(); +} + +// Load CSV +async function loadCSV(path) { + let text = await readFile(path); + return parseCSV(text); +} + +// Debounce function to prevent spamming the autocomplete function +var dbTimeOut; +const debounce = (func, wait = 300) => { + return function (...args) { + if (dbTimeOut) { + clearTimeout(dbTimeOut); + } + + dbTimeOut = setTimeout(() => { + func.apply(this, args); + }, wait); + } +} + +// Difference function to fix duplicates not being seen as changes in normal filter +function difference(a, b) { + if (a.length == 0) { + return b; + } + if (b.length == 0) { + return a; + } + + return [...b.reduce((acc, v) => acc.set(v, (acc.get(v) || 0) - 1), + a.reduce((acc, v) => acc.set(v, (acc.get(v) || 0) + 1), new Map()) + )].reduce((acc, [v, count]) => acc.concat(Array(Math.abs(count)).fill(v)), []); +} + +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} +function escapeHTML(unsafeText) { + let div = document.createElement('div'); + div.textContent = unsafeText; + return div.innerHTML; +} \ No newline at end of file diff --git a/javascript/tagAutocomplete.js b/javascript/tagAutocomplete.js index e3e5e41..f39135a 100644 --- a/javascript/tagAutocomplete.js +++ b/javascript/tagAutocomplete.js @@ -76,64 +76,6 @@ const autocompleteCSS = ` } `; -// Parse the CSV file into a 2D array. Doesn't use regex, so it is very lightweight. -function parseCSV(str) { - var arr = []; - var quote = false; // 'true' means we're inside a quoted field - - // Iterate over each character, keep track of current row and column (of the returned array) - for (var row = 0, col = 0, c = 0; c < str.length; c++) { - var cc = str[c], nc = str[c + 1]; // Current character, next character - arr[row] = arr[row] || []; // Create a new row if necessary - arr[row][col] = arr[row][col] || ''; // Create a new column (start with empty string) if necessary - - // If the current character is a quotation mark, and we're inside a - // quoted field, and the next character is also a quotation mark, - // add a quotation mark to the current column and skip the next character - if (cc == '"' && quote && nc == '"') { arr[row][col] += cc; ++c; continue; } - - // If it's just one quotation mark, begin/end quoted field - if (cc == '"') { quote = !quote; continue; } - - // If it's a comma and we're not in a quoted field, move on to the next column - if (cc == ',' && !quote) { ++col; continue; } - - // If it's a newline (CRLF) and we're not in a quoted field, skip the next character - // and move on to the next row and move to column 0 of that new row - if (cc == '\r' && nc == '\n' && !quote) { ++row; col = 0; ++c; continue; } - - // If it's a newline (LF or CR) and we're not in a quoted field, - // move on to the next row and move to column 0 of that new row - if (cc == '\n' && !quote) { ++row; col = 0; continue; } - if (cc == '\r' && !quote) { ++row; col = 0; continue; } - - // Otherwise, append the current character to the current column - arr[row][col] += cc; - } - return arr; -} - -// Load file -async function readFile(filePath, json = false) { - let response = await fetch(`file=${filePath}`); - - if (response.status != 200) { - console.error(`Error loading file "${filePath}": ` + response.status, response.statusText); - return null; - } - - if (json) - return await response.json(); - else - return await response.text(); -} - -// Load CSV -async function loadCSV(path) { - let text = await readFile(path); - return parseCSV(text); -} - var tagBasePath = ""; var allTags = []; var translations = new Map(); @@ -274,34 +216,6 @@ async function syncOptions() { CFG = newCFG; } -// Debounce function to prevent spamming the autocomplete function -var dbTimeOut; -const debounce = (func, wait = 300) => { - return function (...args) { - if (dbTimeOut) { - clearTimeout(dbTimeOut); - } - - dbTimeOut = setTimeout(() => { - func.apply(this, args); - }, wait); - } -} - -// Difference function to fix duplicates not being seen as changes in normal filter -function difference(a, b) { - if (a.length == 0) { - return b; - } - if (b.length == 0) { - return a; - } - - return [...b.reduce((acc, v) => acc.set(v, (acc.get(v) || 0) - 1), - a.reduce((acc, v) => acc.set(v, (acc.get(v) || 0) + 1), new Map()) - )].reduce((acc, [v, count]) => acc.concat(Array(Math.abs(count)).fill(v)), []); -} - // Create the result list div and necessary styling function createResultsDiv(textArea) { let resultsDiv = document.createElement("div"); @@ -340,15 +254,6 @@ function hideResults(textArea) { selectedTag = null; } -function escapeRegExp(string) { - return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string -} -function escapeHTML(unsafeText) { - let div = document.createElement('div'); - div.textContent = unsafeText; - return div.innerHTML; -} - const WEIGHT_REGEX = /[([]([^,()[\]:| ]+)(?::(?:\d+(?:\.\d+)?|\.\d+))?[)\]]/g; const TAG_REGEX = /(<[^\t\n\r,>]+>?|[^\s,|<>]+|<)/g const WC_REGEX = /\b__([^, ]+)__([^, ]*)\b/g;