Download a1111-microsoftexcel-tagcomplete/javascript/_utils.js from toto10/extensions: direct link, hf CLI and curl.
- Browser
- Download file 4.59 kB
-
https://huggingface.co/toto10/extensions/resolve/refs%2Fpr%2F3/a1111-microsoftexcel-tagcomplete/javascript/_utils.js
- Command line
-
hf download hf://toto10/extensions@refs/pr/3/a1111-microsoftexcel-tagcomplete/javascript/_utils.js
-
curl -L -o _utils.js https://huggingface.co/toto10/extensions/resolve/refs%2Fpr%2F3/a1111-microsoftexcel-tagcomplete/javascript/_utils.js
4.59 kB
| // 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); | |
| } |