stable-diffusion-webui/javascript/utils.js

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

441 lines
12 KiB
JavaScript
Raw Normal View History

2024-04-09 21:26:56 +00:00
/** Collators used for sorting. */
const INT_COLLATOR = new Intl.Collator([], {numeric: true});
const STR_COLLATOR = new Intl.Collator("en", {numeric: true, sensitivity: "base"});
2024-04-09 21:26:56 +00:00
/** Helper functions for checking types and simplifying logging/error handling. */
2024-04-14 18:52:17 +00:00
function isNumber(x) {
return typeof x === "number" && isFinite(x);
}
2024-04-09 21:26:56 +00:00
function isNumberLogError(x) {
if (isNumber(x)) {
return true;
}
2024-04-09 11:19:07 +00:00
console.error(`expected number, got: ${typeof x}`);
return false;
2024-04-09 21:26:56 +00:00
}
function isNumberThrowError(x) {
if (isNumber(x)) {
return;
}
2024-04-09 11:19:07 +00:00
throw new Error(`expected number, got: ${typeof x}`);
2024-04-09 21:26:56 +00:00
}
2024-04-14 18:52:17 +00:00
function isString(x) {
return typeof x === "string" || x instanceof String;
}
2024-04-09 21:26:56 +00:00
function isStringLogError(x) {
if (isString(x)) {
return true;
}
2024-04-09 11:19:07 +00:00
console.error(`expected string, got: ${typeof x}`);
return false;
2024-04-09 21:26:56 +00:00
}
function isStringThrowError(x) {
if (isString(x)) {
return;
}
2024-04-09 11:19:07 +00:00
throw new Error(`expected string, got: ${typeof x}`);
2024-04-09 21:26:56 +00:00
}
2024-04-14 18:52:17 +00:00
function isNull(x) {
return x === null;
}
function isUndefined(x) {
return typeof x === "undefined" || x === undefined;
}
// checks both null and undefined for simplicity sake.
2024-04-14 18:52:17 +00:00
function isNullOrUndefined(x) {
return isNull(x) || isUndefined(x);
}
2024-04-09 21:26:56 +00:00
function isNullOrUndefinedLogError(x) {
if (isNullOrUndefined(x)) {
console.error("Variable is null/undefined.");
return true;
}
return false;
2024-04-09 21:26:56 +00:00
}
function isNullOrUndefinedThrowError(x) {
if (!isNullOrUndefined(x)) {
return;
}
throw new Error("Variable is null/undefined.");
2024-04-09 21:26:56 +00:00
}
2024-04-14 18:52:17 +00:00
function isElement(x) {
return x instanceof Element;
}
2024-04-09 21:26:56 +00:00
function isElementLogError(x) {
if (isElement(x)) {
return true;
}
2024-04-09 11:19:07 +00:00
console.error(`expected element type, got: ${typeof x}`);
return false;
2024-04-09 21:26:56 +00:00
}
function isElementThrowError(x) {
if (isElement(x)) {
return;
}
2024-04-09 11:19:07 +00:00
throw new Error(`expected element type, got: ${typeof x}`);
2024-04-09 21:26:56 +00:00
}
2024-04-14 18:52:17 +00:00
function isFunction(x) {
return typeof x === "function";
}
2024-04-09 21:26:56 +00:00
function isFunctionLogError(x) {
if (isFunction(x)) {
return true;
}
2024-04-09 11:19:07 +00:00
console.error(`expected function type, got: ${typeof x}`);
return false;
2024-04-09 21:26:56 +00:00
}
function isFunctionThrowError(x) {
if (isFunction(x)) {
return;
}
2024-04-09 11:19:07 +00:00
throw new Error(`expected function type, got: ${typeof x}`);
2024-04-09 21:26:56 +00:00
}
2024-04-09 11:19:07 +00:00
2024-04-14 18:52:17 +00:00
function isObject(x) {
return typeof x === "object" && !Array.isArray(x);
}
2024-04-09 21:26:56 +00:00
function isObjectLogError(x) {
2024-04-09 11:19:07 +00:00
if (isObject(x)) {
return true;
}
console.error(`expected object type, got: ${typeof x}`);
return false;
2024-04-09 21:26:56 +00:00
}
function isObjectThrowError(x) {
2024-04-09 11:19:07 +00:00
if (isObject(x)) {
return;
}
throw new Error(`expected object type, got: ${typeof x}`);
2024-04-09 21:26:56 +00:00
}
2024-04-09 11:19:07 +00:00
2024-04-09 21:26:56 +00:00
function keyExists(obj, k) {
return isObject(obj) && isString(k) && k in obj;
}
function keyExistsLogError(obj, k) {
2024-04-09 11:19:07 +00:00
if (keyExists(obj, k)) {
return true;
}
console.error(`key does not exist in object: ${k}`);
return false;
2024-04-09 21:26:56 +00:00
}
function keyExistsThrowError(obj, k) {
2024-04-09 11:19:07 +00:00
if (keyExists(obj, k)) {
return;
}
2024-04-14 18:52:17 +00:00
throw new Error(`key does not exist in object: ${k}`);
2024-04-09 21:26:56 +00:00
}
2024-04-09 11:19:07 +00:00
2024-04-09 21:26:56 +00:00
function getValue(obj, k) {
2024-04-09 11:19:07 +00:00
/** Returns value of object for given key if it exists, otherwise returns null. */
if (keyExists(obj, k)) {
return obj[k];
}
return null;
2024-04-09 21:26:56 +00:00
}
function getValueLogError(obj, k) {
2024-04-09 11:19:07 +00:00
if (keyExistsLogError(obj, k)) {
return obj[k];
}
return null;
2024-04-09 21:26:56 +00:00
}
function getValueThrowError(obj, k) {
2024-04-09 11:19:07 +00:00
keyExistsThrowError(obj, k);
return obj[k];
2024-04-09 21:26:56 +00:00
}
2024-04-09 21:26:56 +00:00
function getElementByIdLogError(selector) {
const elem = gradioApp().getElementById(selector);
isElementLogError(elem);
return elem;
2024-04-09 21:26:56 +00:00
}
function getElementByIdThrowError(selector) {
const elem = gradioApp().getElementById(selector);
isElementThrowError(elem);
return elem;
2024-04-09 21:26:56 +00:00
}
2024-04-09 21:26:56 +00:00
function querySelectorLogError(selector) {
const elem = gradioApp().querySelector(selector);
isElementLogError(elem);
return elem;
2024-04-09 21:26:56 +00:00
}
function querySelectorThrowError(selector) {
const elem = gradioApp().querySelector(selector);
isElementThrowError(elem);
return elem;
2024-04-09 21:26:56 +00:00
}
/** Functions for getting dimensions of elements. */
function getStyle(elem) {
return window.getComputedStyle ? window.getComputedStyle(elem) : elem.currentStyle;
}
function getComputedProperty(elem, prop) {
return getStyle(elem)[prop];
}
2024-04-09 21:26:56 +00:00
function getComputedPropertyDims(elem, prop) {
/** Returns the top/left/bottom/right float dimensions of an element for the specified property. */
const style = getStyle(elem);
return {
top: parseFloat(style.getPropertyValue(`${prop}-top`)),
left: parseFloat(style.getPropertyValue(`${prop}-left`)),
bottom: parseFloat(style.getPropertyValue(`${prop}-bottom`)),
right: parseFloat(style.getPropertyValue(`${prop}-right`)),
};
2024-04-09 21:26:56 +00:00
}
2024-04-09 21:26:56 +00:00
function getComputedMarginDims(elem) {
/** Returns the width/height of the computed margin of an element. */
const dims = getComputedPropertyDims(elem, "margin");
return {
width: dims.left + dims.right,
height: dims.top + dims.bottom,
};
2024-04-09 21:26:56 +00:00
}
2024-04-09 21:26:56 +00:00
function getComputedPaddingDims(elem) {
/** Returns the width/height of the computed padding of an element. */
const dims = getComputedPropertyDims(elem, "padding");
return {
width: dims.left + dims.right,
height: dims.top + dims.bottom,
};
2024-04-09 21:26:56 +00:00
}
2024-04-09 21:26:56 +00:00
function getComputedBorderDims(elem) {
/** Returns the width/height of the computed border of an element. */
// computed border will always start with the pixel width so thankfully
// the parseFloat() conversion will just give us the width and ignore the rest.
// Otherwise we'd have to use border-<pos>-width instead.
const dims = getComputedPropertyDims(elem, "border");
return {
width: dims.left + dims.right,
height: dims.top + dims.bottom,
};
2024-04-09 21:26:56 +00:00
}
2024-04-09 21:26:56 +00:00
function getComputedDims(elem) {
/** Returns the full width and height of an element including its margin, padding, and border. */
const width = elem.scrollWidth;
const height = elem.scrollHeight;
const margin = getComputedMarginDims(elem);
const padding = getComputedPaddingDims(elem);
const border = getComputedBorderDims(elem);
return {
width: width + margin.width + padding.width + border.width,
height: height + margin.height + padding.height + border.height,
};
2024-04-09 21:26:56 +00:00
}
/** Functions for asynchronous operations. */
2024-04-09 21:26:56 +00:00
function debounce(handler, timeout_ms) {
/** Debounces a function call.
*
* NOTE: This will NOT work if called from within a class.
* It will drop `this` from scope.
*
* Repeated calls to the debounce handler will not call the handler until there are
* no new calls to the debounce handler for timeout_ms time.
*
* Example:
* function add(x, y) { return x + y; }
* let debounce_handler = debounce(add, 5000);
* let res;
* for (let i = 0; i < 10; i++) {
* res = debounce_handler(i, 100);
* }
* console.log("Result:", res);
*
* This example will print "Result: 109".
*/
let timer = null;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => handler(...args), timeout_ms);
};
2024-04-09 21:26:56 +00:00
}
2024-04-09 21:26:56 +00:00
function waitForElement(selector) {
/** Promise that waits for an element to exist in DOM. */
return new Promise(resolve => {
if (document.querySelector(selector)) {
return resolve(document.querySelector(selector));
}
const observer = new MutationObserver(mutations => {
if (document.querySelector(selector)) {
observer.disconnect();
resolve(document.querySelector(selector));
}
});
observer.observe(document.documentElement, {
childList: true,
subtree: true
});
});
2024-04-09 21:26:56 +00:00
}
2024-04-09 21:26:56 +00:00
function waitForBool(o) {
/** Promise that waits for a boolean to be true.
*
* `o` must be an Object of the form:
* { state: <bool value> }
*
* Resolves when (state === true)
*/
return new Promise(resolve => {
(function _waitForBool() {
if (o.state) {
return resolve();
}
setTimeout(_waitForBool, 100);
})();
});
2024-04-09 21:26:56 +00:00
}
2024-04-09 21:26:56 +00:00
function waitForKeyInObject(o) {
/** Promise that waits for a key to exist in an object.
*
* `o` must be an Object of the form:
* {
* obj: <object to watch for key>,
* k: <key to watch for>,
* }
*
* Resolves when (k in obj)
*/
return new Promise(resolve => {
(function _waitForKeyInObject() {
if (o.k in o.obj) {
return resolve();
}
setTimeout(_waitForKeyInObject, 100);
})();
});
2024-04-09 21:26:56 +00:00
}
2024-04-09 21:26:56 +00:00
function waitForValueInObject(o) {
/** Promise that waits for a key value pair in an Object.
*
* `o` must be an Object of the form:
* {
* obj: <object containing value>,
* k: <key in object>,
* v: <value at key for comparison>
* }
*
* Resolves when obj[k] == v
*/
return new Promise(resolve => {
2024-04-14 18:52:17 +00:00
waitForKeyInObject({k: o.k, obj: o.obj}).then(() => {
(function _waitForValueInObject() {
if (o.k in o.obj && o.obj[o.k] == o.v) {
return resolve();
}
setTimeout(_waitForValueInObject, 100);
})();
});
});
2024-04-09 21:26:56 +00:00
}
2024-04-09 11:19:07 +00:00
/** Requests */
2024-04-09 21:26:56 +00:00
function requestGet(url, data, handler, errorHandler) {
2024-04-09 11:19:07 +00:00
var xhr = new XMLHttpRequest();
2024-04-14 18:52:17 +00:00
var args = Object.keys(data).map(function(k) {
2024-04-09 11:19:07 +00:00
return encodeURIComponent(k) + '=' + encodeURIComponent(data[k]);
}).join('&');
xhr.open("GET", url + "?" + args, true);
2024-04-14 18:52:17 +00:00
xhr.onreadystatechange = function() {
2024-04-09 11:19:07 +00:00
if (xhr.readyState === 4) {
if (xhr.status === 200) {
try {
var js = JSON.parse(xhr.responseText);
handler(js);
} catch (error) {
console.error(error);
errorHandler();
}
} else {
errorHandler();
}
}
};
var js = JSON.stringify(data);
xhr.send(js);
2024-04-09 21:26:56 +00:00
}
2024-04-09 11:19:07 +00:00
2024-04-09 21:26:56 +00:00
function requestGetPromise(url, data) {
2024-04-09 11:19:07 +00:00
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
let args = Object.keys(data).map(k => {
return encodeURIComponent(k) + "=" + encodeURIComponent(data[k]);
}).join("&");
xhr.open("GET", url + "?" + args, true);
2024-04-09 21:26:56 +00:00
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.responseText);
} else {
reject({status: xhr.status, response: xhr.responseText});
2024-04-09 11:19:07 +00:00
}
};
2024-04-09 21:26:56 +00:00
xhr.onerror = () => {
reject({status: xhr.status, response: xhr.responseText});
};
const payload = JSON.stringify(data);
xhr.send(payload);
2024-04-09 11:19:07 +00:00
});
2024-04-09 21:26:56 +00:00
}
2024-04-09 11:19:07 +00:00
/** Misc helper functions. */
2024-04-09 21:26:56 +00:00
function clamp(x, min, max) {
return Math.max(min, Math.min(x, max));
}
2024-04-09 21:26:56 +00:00
function htmlStringToElement(s) {
/** Converts an HTML string into an Element type. */
let parser = new DOMParser();
2024-04-09 21:26:56 +00:00
let tmp = parser.parseFromString(s, "text/html");
return tmp.body.firstElementChild;
2024-04-09 21:26:56 +00:00
}
2024-04-09 11:19:07 +00:00
2024-04-15 13:31:30 +00:00
function htmlStringToFragment(s) {
/** Converts an HTML string into a DocumentFragment. */
return document.createRange().createContextualFragment(s);
}
2024-04-09 21:26:56 +00:00
function toggleCss(key, css, enable) {
2024-04-09 11:19:07 +00:00
var style = document.getElementById(key);
if (enable && !style) {
style = document.createElement('style');
style.id = key;
style.type = 'text/css';
document.head.appendChild(style);
}
if (style && !enable) {
document.head.removeChild(style);
}
if (style) {
style.innerHTML == '';
style.appendChild(document.createTextNode(css));
}
2024-04-09 21:26:56 +00:00
}
2024-04-09 11:19:07 +00:00
2024-04-09 21:26:56 +00:00
function copyToClipboard(s) {
2024-04-09 11:19:07 +00:00
/** Copies the passed string to the clipboard. */
isStringThrowError(s);
navigator.clipboard.writeText(s);
2024-04-09 21:26:56 +00:00
}