Spaces:
Build error
Build error
File size: 9,613 Bytes
6a49f21 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 |
/**
* "Mini-jquery" - monkey-patches the element identified by the selector with some convenience methods.
* @param {string} selector
* @returns {Element}
*/
export function $(selector) {
const node = document.querySelector(selector)
if (!node) {
throw new Error(`Selector "${selector} does not find any element"`)
}
return node
}
/**
* Given a selector, return all matching DOM nodes in an array
* @param {string} selector The DOM selector
* @returns {Array}
*/
export function $$(selector) {
return Array.from(document.querySelectorAll(selector))
}
export class CookieStorage {
/**
* Constructor for CookieStorage.
* @param {object} [config={}] - Configuration for cookies.
* @param {string} [config.path='/'] - The default path for cookies.
* @param {boolean} [config.secure=true] - Whether cookies should be secure.
* @param {string} [config.sameSite='Strict'] - The SameSite attribute (e.g., 'Strict', 'Lax', or 'None').
* @param {number} [config.maxAge=604800] - The default max-age for cookies (in seconds, default is 7 days).
*/
constructor(config = {}) {
this.config = {
path: config.path || '/',
secure: config.secure !== undefined ? config.secure : true,
sameSite: config.sameSite || 'Strict',
maxAge: config.maxAge !== undefined ? config.maxAge : 7 * 24 * 60 * 60 // Default: 7 days
};
}
/**
* Retrieves a value from cookies by key. If it can be converted from a
* JSON string, return the converted value.
* @param {string} key - The key of the cookie to retrieve.
* @returns {string|Object|null} The cookie value, or null if not found.
*/
get(key) {
const cookie = document.cookie
.split('; ')
.find(row => row.startsWith(`${key}=`));
let value = cookie ? decodeURIComponent(cookie.split('=')[1]) : null;
if (value && typeof value === "string") {
try {
value = JSON.parse(value);
} catch (e) {
// just keep the old value
}
}
return value;
}
/**
* Returns true if the key exists in the cookies or false if not.
* @param {string} key The key of the hash parameter to retrieve.
* @returns {boolean}
*/
has(key) {
return this.get(key) !== null;
}
/**
* Sets a cookie with a specified key and value. If it is not a string,
* JSON-encode it before storing it.
* @param {string} key - The key of the cookie.
* @param {string|Object} value - The value of the cookie.
* @param {object} [options={}] - Optional overrides for the configuration.
*/
set(key, value, options = {}) {
if (typeof value != "string") {
value = JSON.stringify(value);
}
const { path, secure, sameSite, maxAge } = { ...this.config, ...options };
let cookie = `${key}=${encodeURIPart(value)}; path=${path}; samesite=${sameSite}`;
if (secure) cookie += '; secure';
if (maxAge !== undefined) cookie += `; max-age=${maxAge}`;
document.cookie = cookie;
}
/**
* Removes a cookie by key.
* @param {string} key - The key of the cookie to remove.
* @param {object} [options={}] - Optional overrides for the configuration.
*/
remove(key, options = {}) {
const { path, sameSite } = { ...this.config, ...options };
const cookie = `${key}=; path=${path}; samesite=${sameSite}; max-age=0`;
document.cookie = cookie;
}
}
export class UrlHash {
/**
* Sets or updates a hash parameter in the URL without reloading the page and ensures browser history is updated.
* @param {string|Object} keyOrObj - The key of the hash parameter to set, or an object with several key-value pairs
* @param {string?} value - The value of the hash parameter to set. Not used if first parameter is an object
* @param {boolean} [dispatchEvent=true] If a 'hashchange' event should be dispatched. Defaults to true
*/
static set(keyOrObj, value, dispatchEvent = true) {
const hash = new URLSearchParams(window.location.hash.slice(1));
if (typeof keyOrObj === "string" && typeof value === "string") {
hash.set(keyOrObj, value)
} else if (keyOrObj && typeof keyOrObj === "object") {
Object.entries(keyOrObj).forEach(([key, value]) => hash.set(key, value))
} else {
throw new TypeError(`Invalid parameters: ${keyOrObj}, ${value}`)
}
// Use history.replaceState to update the current history entry
history.replaceState(null, '', '#' + hash.toString());
if (dispatchEvent) {
window.dispatchEvent(new HashChangeEvent('hashchange'));
}
}
/**
* Retrieves the value of a hash parameter from the URL.
* @param {string} key - The key of the hash parameter to retrieve.
* @returns {string|null} The value of the hash parameter, or null if not found.
*/
static get(key) {
const hash = new URLSearchParams(window.location.hash.slice(1));
return hash.get(key);
}
/**
* Returns true if the key exists in the URL hash or false if not
* @param {string} key The key of the hash parameter to retrieve.
* @returns {boolean}
*/
static has(key) {
return UrlHash.get(key) !== null;
}
/**
* Removes a hash parameter from the URL without reloading the page.
* @param {string} key - The key of the hash parameter to remove.
* @param {boolean} [dispatchEvent=true] If a 'hashchange' event should be dispatched. Defaults to true
*/
static remove(key, dispatchEvent = true) {
if (!UrlHash.has(key)) return; // Do nothing if the key does not exist
const hash = new URLSearchParams(window.location.hash.slice(1));
hash.delete(key); // Remove the specified key
const updatedHash = hash.toString();
// Use history.replaceState to update the current history entry
history.replaceState(null, '', '#' + updatedHash);
if (dispatchEvent) {
window.dispatchEvent(new HashChangeEvent('hashchange'));
}
}
}
/**
* Selects the option in the selectbox of which the value property matches the given value
* @param {HTMLSelectElement} selectbox The selectbox
* @param {string} value The value to select
*/
export function selectByValue(selectbox, value) {
const index = Array.from(selectbox.options).findIndex(o => o.value === value)
if (index == -1) {
throw new Error(`No matching option with value '${value}' in selectbox with name '${selectbox.name}'`)
}
selectbox.selectedIndex = index;
}
/**
* Selects the option in the selectbox of which the given data property matches the given value
* @param {HTMLSelectElement} selectbox The selectbox
* @param {string} key The key of the dataset property
* @param {string} value The value to select
*/
export function selectByData(selectbox, key, value) {
const index = Array.from(selectbox.options).findIndex(o => o.dataset[key] === value)
if (index == -1) {
throw new Error(`No matching option with dataset.${key} value '${value}' in selectbox with name '${selectbox.name}'`)
}
selectbox.selectedIndex = index;
}
/**
* Returns the first descendant having that name. Throws an error if none can be found unless you pass noError = true
* @param {Element} node The ancestor node
* @param {string} name The name to look for
* @param {Boolean} noError If true, return null instead of throwing an error if no ancestor with that name exists
* @returns {Element|null}
*/
export function getDescendantByName(node, name, noError) {
const descendant = node.querySelector(`[name="${name}]`)
if (!descendant) {
if (noError) return null
throw new Error(`No descendant with name "${name} exists`)
}
return descendant
}
/**
* Escapes the given text to valid html
* @param {string} text
* @returns {string} The escaped text
*/
export function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Function to serialize the XML DOM back to a string (for demonstration)
export function serializeXmlToString(xmlDoc) {
// Check if xmlDoc is actually a document node before trying to serialize
if (!xmlDoc || typeof xmlDoc.serializeToString !== 'function') {
console.error("Invalid document object passed to serializeXmlToString");
// Attempt to return a string representation if possible, or indicate error
try {
if (xmlDoc.documentElement) {
return new XMLSerializer().serializeToString(xmlDoc.documentElement);
}
} catch (e) {
console.error("Could not serialize even documentElement:", e);
}
return "[Invalid XML Document]";
}
const serializer = new XMLSerializer();
return serializer.serializeToString(xmlDoc);
}
/**
* Tests if an XPath is valid
* @param {string} xpathExpression
* @param {Document} xmlDom The DOM document to test the expression on
* @param {XPathNSResolver|null} namespaceResolver
* @returns {Boolean}
*/
export function isValidXPath(xpathExpression, xmlDom, namespaceResolver = null) {
try {
// Check if the XML DOM is valid
if (!xmlDom || typeof xmlDom !== 'object' || !xmlDom.evaluate) {
console.error("Invalid XML DOM provided.");
return false;
}
// Try to evaluate the XPath expression
xmlDom.evaluate(
xpathExpression,
xmlDom,
namespaceResolver,
XPathResult.ANY_TYPE, // resultType - ANY_TYPE is generally fine for validation
null // result - reuse existing result, optional
);
return true; // If no error thrown, the XPath is valid
} catch (error) {
// An error indicates an invalid XPath expression
console.error("Invalid XPath:", String(error)); // Optionally log the error
return false;
}
}
|