123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 |
- // Utility functions for tag autocomplete
- // 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, cache = false) {
- if (!cache)
- filePath += `?${new Date().getTime()}`;
-
- 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;
- }
- // Queue calling function to process global queues
- async function processQueue(queue, context, ...args) {
- for (let i = 0; i < queue.length; i++) {
- await queue[i].call(context, ...args);
- }
- }
- // The same but with return values
- async function processQueueReturn(queue, context, ...args)
- {
- let qeueueReturns = [];
- for (let i = 0; i < queue.length; i++) {
- let returnValue = await queue[i].call(context, ...args);
- if (returnValue)
- qeueueReturns.push(returnValue);
- }
- return qeueueReturns;
- }
- // Specific to tag completion parsers
- async function processParsers(textArea, prompt) {
- // Get all parsers that have a successful trigger condition
- let matchingParsers = PARSERS.filter(parser => parser.triggerCondition());
- // Guard condition
- if (matchingParsers.length === 0) {
- return null;
- }
- let parseFunctions = matchingParsers.map(parser => parser.parse);
- // Process them and return the results
- return await processQueueReturn(parseFunctions, null, textArea, prompt);
- }
|