mirror of
https://github.com/DominikDoom/a1111-sd-webui-tagcomplete.git
synced 2026-01-26 19:19:57 +00:00
96 lines
3.4 KiB
JavaScript
96 lines
3.4 KiB
JavaScript
// 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) {
|
|
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;
|
|
} |