almostnojs
Version:
A minimalist, dependency-free JavaScript framework for DOM manipulation, event handling, animations, state management, and HTTP requests.
1,035 lines (1,019 loc) • 38.5 kB
JavaScript
/* AlmostNo.js v1.1.3 Full */
(() => {
var __defProp = Object.defineProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// src/core.js
var globalScope, core_default;
var init_core = __esm({
"src/core.js"() {
globalScope = typeof window !== "undefined" ? window : global;
if (!globalScope.__AnJS__) {
class AnJS extends Array {
/**
* Initialize AnJS
*
* @param {string | HTMLElement | NodeList} query - CSS selector or element.
*/
constructor(query) {
super();
if (!query) return;
if (query instanceof HTMLElement || query.nodeType === 1) this.push(query);
else if (query instanceof NodeList || Array.isArray(query)) this.push(...query);
else if (typeof query === "string") this.push(...document.querySelectorAll(query));
}
/**
* Iterate through elements
*
* @param {Function} fn - Callback function.
* @returns {AnJS} - Returns self for chaining.
*/
each(fn) {
this.forEach(fn);
return this;
}
/**
* Get elements by index or return all
*
* @param {number} [index] - The index of the element to retrieve.
* @returns {HTMLElement | Array} - The specific element or an array of elements.
*/
get(index) {
return index === void 0 ? this : this.at(index);
}
/**
* Clone the first selected element
*
* @param {boolean} [deep=true] - Clone children.
* @returns {HTMLElement | null} - Cloned element.
*/
clone(deep = true) {
return this[0] ? this[0].cloneNode(deep) : null;
}
}
globalScope.__AnJS__ = AnJS;
}
core_default = globalScope.__AnJS__;
}
});
// src/filtering.js
var filtering_exports = {};
var init_filtering = __esm({
"src/filtering.js"() {
init_core();
Object.assign(core_default.prototype, {
/**
* Filter elements based on a callback function or CSS selector
*
* @param {Function | string} callbackOrSelector - Callback function or CSS selector.
* @returns {AnJS} - Returns a new instance of AnJS.
*/
filter(callbackOrSelector) {
if (typeof callbackOrSelector === "function") return new core_default([...this].filter(callbackOrSelector));
return new core_default([...this].filter((el) => el.matches(callbackOrSelector)));
},
/**
* Find child elements by a CSS selector
*
* @param {string} selector - CSS selector to find child elements.
* @returns {AnJS} - Returns a new instance of AnJS.
*/
find(selector) {
return new core_default(this.flatMap((el) => [...el.querySelectorAll(selector)]));
},
/**
* Select the first element from the current selection
*
* @returns {AnJS} - Returns a new instance of AnJS.
*/
first() {
return new core_default(this.length ? [this[0]] : []);
},
/**
* Select the last element from the current selection
*
* @returns {AnJS} - Returns a new instance of AnJS.
*/
last() {
return new core_default(this.length ? [this[this.length - 1]] : []);
},
/**
* Select only elements with an even index
*
* @returns {AnJS} - Returns a new instance of AnJS.
*/
even() {
return new core_default(this.filter((_, index) => !(index % 2)));
},
/**
* Select only elements with an odd index
*
* @returns {AnJS} - Returns a new instance of AnJS.
*/
odd() {
return new core_default(this.filter((_, index) => index % 2));
}
});
}
});
// src/traversal.js
var traversal_exports = {};
var init_traversal = __esm({
"src/traversal.js"() {
init_core();
Object.assign(core_default.prototype, {
/**
* Select the next sibling element
*
* @returns {AnJS} - New AnJS instance with the next sibling.
*/
next() {
return new core_default(this[0]?.nextElementSibling ? [this[0].nextElementSibling] : []);
},
/**
* Select the previous sibling element
*
* @returns {AnJS} - New AnJS instance with the previous sibling.
*/
prev() {
return new core_default(this[0]?.previousElementSibling ? [this[0].previousElementSibling] : []);
},
/**
* Select the parent element
*
* @returns {AnJS} - New AnJS instance with the parent.
*/
parent() {
return new core_default(this[0]?.parentElement ? [this[0].parentElement] : []);
},
/**
* Select child elements
*
* @returns {AnJS} - New AnJS instance with children.
*/
children() {
return new core_default(this[0] ? [...this[0].children] : []);
},
/**
* Select all sibling elements
*
* @returns {AnJS} - New AnJS instance with all siblings except the current element.
*/
siblings() {
const parent = this[0]?.parentElement;
return new core_default(parent ? [...parent.children].filter((el) => el !== this[0]) : []);
},
/**
* Select the closest ancestor matching a selector
*
* @param {string} selector - CSS selector to match.
* @returns {AnJS} - New AnJS instance with the closest matching ancestor.
*/
closest(selector) {
return new core_default(this[0]?.closest(selector) ? [this[0].closest(selector)] : []);
}
});
}
});
// src/state.js
var state_exports = {};
__export(state_exports, {
bindings: () => bindings,
localBindings: () => localBindings
});
function persistent(name, initial, persist) {
return new Proxy(initial, {
// Retrieve property
get: (target, prop) => target[prop],
// Update property & auto-save if persistence is enabled
set: (target, prop, value) => {
target[prop] = value;
if (persist) {
const storage = persist === "session" ? sessionStorage : localStorage;
storage.setItem(name, JSON.stringify(target));
}
return true;
}
});
}
var bindings, localBindings, globalStates, attrBindings, boolAttrs;
var init_state = __esm({
"src/state.js"() {
init_core();
bindings = {};
localBindings = /* @__PURE__ */ new Map();
globalStates = typeof window !== "undefined" && (window.__AnJS_GLOBAL_STATES__ || (window.__AnJS_GLOBAL_STATES__ = {})) || {};
attrBindings = {};
boolAttrs = /* @__PURE__ */ new Set([
"disabled",
"checked",
"selected",
"readonly",
"multiple",
"hidden",
"autoplay",
"controls",
"loop",
"muted"
]);
core_default.prototype.global = function(name, initial, options = {}) {
if (!name || typeof name !== "string") throw new Error("Global state must have a unique name.");
if (!globalStates[name] && initial === void 0) throw new Error(`Global state "${name}" does not exist. Provide an initial state.`);
if (options.persist) {
const storage = options.persist === "session" ? sessionStorage : localStorage;
const saved = storage.getItem(name);
if (saved) initial = JSON.parse(saved);
}
if (!globalStates[name] && initial !== void 0) {
globalStates[name] = $.state(persistent(name, initial, options.persist));
}
return globalStates[name];
};
core_default.prototype.clearGlobal = function(name) {
if (globalStates[name]) {
delete globalStates[name];
localStorage.removeItem(name);
sessionStorage.removeItem(name);
}
};
core_default.prototype.hasGlobal = function(name) {
return !!globalStates[name];
};
core_default.prototype.state = function(initial = {}, options = {}) {
var _a;
if (options.global) {
if (!options.name) throw new Error("Global state must have a name.");
initial = globalStates[_a = options.name] ?? (globalStates[_a] = initial);
}
const proxy = new Proxy(initial, {
// Retrieve state property
get: (target, prop) => target[prop],
// Update state property & trigger UI updates
set: (target, prop, value) => {
target[prop] = value;
Object.keys(bindings).forEach((bindKey) => {
let [root, ...rest] = bindKey.split(".");
if (!bindings[bindKey]) return;
let nestedValue = rest.reduce((obj, key) => obj?.[key], globalStates[root]);
bindings[bindKey].forEach((el) => el.textContent = nestedValue ?? "");
});
bindings[prop]?.forEach((el) => el.textContent = value ?? "");
localBindings.get(proxy)?.[prop]?.forEach((el) => el.textContent = value ?? "");
attrBindings[prop]?.forEach(({ el, attr }) => {
value == null ? el.removeAttribute(attr) : boolAttrs.has(attr.toLowerCase()) ? el.toggleAttribute(attr, !!value) : el.setAttribute(attr, value);
});
return true;
}
});
localBindings.set(proxy, {});
this.bind(proxy);
return proxy;
};
core_default.prototype.bind = function(state, context = document) {
context.querySelectorAll("[data-bind], [data-bind-this], [data-bind-attr]").forEach((el) => {
var _a;
if (el.dataset.bound) return;
el.dataset.bound = "true";
const [attr, prop] = el.getAttribute("data-bind-attr")?.split(":") || [null, el.getAttribute("data-bind") || el.getAttribute("data-bind-this")];
const parts = prop?.split(".");
const value = parts?.length > 1 ? parts.slice(1).reduce((o, k) => o?.[k], globalStates[parts[0]] ?? state) : state[prop] ?? globalStates[prop];
if (el.hasAttribute("data-bind")) {
(bindings[prop] || (bindings[prop] = [])).push(el);
el.textContent = value ?? "";
} else if (el.hasAttribute("data-bind-this")) {
((_a = localBindings.get(state))[prop] || (_a[prop] = [])).push(el);
el.textContent = value ?? "";
} else {
(attrBindings[prop] || (attrBindings[prop] = [])).push({ el, attr });
boolAttrs.has(attr.toLowerCase()) ? el.toggleAttribute(attr, !!value) : el.setAttribute(attr, value ?? "");
if (attr === "value" && ["INPUT", "TEXTAREA"].includes(el.tagName)) el.addEventListener("input", () => state[prop] = el.value);
}
});
this.autoEvents(state, context);
};
core_default.prototype.autoEvents = function(state, context = document) {
context.querySelectorAll("[data-on]").forEach((el) => {
var _a;
const [event, method] = el.getAttribute("data-on")?.split(":");
if (!el.dataset.boundEvent && typeof state[method] === "function") {
el.dataset.boundEvent = "true";
const handler = (e) => state[method]?.(e, state);
el.addEventListener(event, handler);
if (!localBindings.has(state)) {
localBindings.set(state, {});
}
((_a = localBindings.get(state))[event] || (_a[event] = [])).push({ el, event, handler });
}
});
context.querySelectorAll("[data-action]").forEach((el) => {
let action = el.dataset.action;
if (!el.dataset.boundAction) {
el.dataset.boundAction = "true";
el.addEventListener("click", (e) => {
if (typeof state[action] === "function") return state[action](e, state);
let [globalName, globalMethod] = (action || "").split(".");
if (typeof globalStates[globalName]?.[globalMethod] === "function") {
globalStates[globalName][globalMethod](e, globalStates[globalName]);
}
});
}
});
};
core_default.prototype.unbind = function(state) {
Object.values(localBindings.get(state)).forEach(
(bindings2) => (
// Remove each event listener
bindings2.forEach(({ el, event, handler }) => el.removeEventListener(event, handler))
)
);
[bindings, attrBindings].forEach((obj) => Object.keys(state).forEach((prop) => delete obj[prop]));
localBindings.delete(state);
};
}
});
// src/component.js
var component_exports = {};
__export(component_exports, {
default: () => component_default,
startObserver: () => startObserver
});
function startObserver() {
document.readyState !== "loading" ? components.observer() : document.addEventListener("DOMContentLoaded", () => components.observer());
}
var components, component_default;
var init_component = __esm({
"src/component.js"() {
init_core();
components = {
// Registry for components
registry: {},
/**
* Start observing the DOM for dynamically added components.
*
* @returns {void}
*/
observer() {
new MutationObserver((mutations) => {
mutations.forEach(({ addedNodes }) => {
addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE && this.registry[node.tagName.toLowerCase()]) this.mount(node, node.tagName.toLowerCase());
});
});
}).observe(document.body, { childList: true, subtree: true });
},
/**
* Register a new component.
*
* @param {string} name - Component tag name.
* @param {Function} template - Rendering function.
* @param {Function | Object} [stateOrHandlers] - State function or event handlers.
* @param {Function} [handlers] - Optional event handlers.
*/
register(name, template, stateOrHandlers, handlers) {
if (!stateOrHandlers && !handlers) return;
let state = () => $.state({});
let finalHandlers = () => {
};
if (typeof stateOrHandlers === "function") {
try {
state = typeof stateOrHandlers() === "object" ? stateOrHandlers : state;
} catch {
finalHandlers = stateOrHandlers;
}
}
if (typeof handlers === "function") finalHandlers = handlers;
this.registry[name.toLowerCase()] = { template, state, handlers: finalHandlers };
document.querySelectorAll(name.toLowerCase()).forEach((el) => this.mount(el, name));
},
/**
* Mount a component dynamically.
*
* @param {HTMLElement} el - Element to replace.
* @param {string} name - Component name.
*/
mount(el, name) {
const { template, state, handlers } = this.registry[name.toLowerCase()];
if (el.dataset.__mounted) return;
el.dataset.__mounted = "true";
const props = Object.fromEntries([...el.attributes].map((attr) => [attr.name, attr.value]));
const componentState = core_default.prototype.state({ ...state(), ...props });
const rendered = this.render(template({ state: componentState, props }), componentState);
el.replaceWith(rendered);
$(rendered).bind(componentState);
rendered.querySelectorAll(Object.keys(this.registry).join(",")).forEach((child) => this.mount(child, child.tagName.toLowerCase()));
this.bind(rendered, handlers, componentState);
},
/**
* Render an HTML string into a DOM element.
*
* @param {string} html - Component HTML.
* @param {Object} [state={}] - Optional state to bind.
* @returns {HTMLElement} - Rendered DOM element.
*/
render(html, state = {}) {
const container = document.createElement("div");
container.innerHTML = html.trim();
const element2 = container.firstElementChild || null;
if (!element2) return null;
if (state) $(element2).bind(state);
return element2;
},
/**
* Attach event handlers to a mounted component.
*
* @param {HTMLElement} rendered - Rendered component element.
* @param {Function | Object} handlers - Event handlers.
* @param {Object} componentState - Component state.
*/
bind(rendered, handlers, componentState) {
if (typeof handlers === "function") return handlers($(rendered), componentState);
Object.entries(handlers).forEach(([event, actions]) => {
$(rendered).on(event, "[data-action]", (e) => {
const action = e.target.dataset.action;
if (actions[action]) {
actions[action](componentState, e);
$(rendered).bind(componentState);
}
});
});
}
};
core_default.prototype.component = function(name, template, stateOrHandlers, handlers) {
components.register(name, template, stateOrHandlers, handlers);
};
core_default.prototype.render = components.render;
startObserver();
component_default = components;
}
});
// src/request.js
var request_exports = {};
__export(request_exports, {
default: () => request_default,
http: () => http,
request: () => request
});
function mergeHeaders(customHeaders = {}, hasBody) {
const headers = {
"Accept": "application/json",
...customHeaders
};
if (hasBody && !headers["Content-Type"]) headers["Content-Type"] = "application/json";
return headers;
}
function processBody(data, headers) {
if (data instanceof FormData) {
delete headers["Content-Type"];
return data;
}
const isFormEncoded = headers["Content-Type"] === "application/x-www-form-urlencoded";
return isFormEncoded ? new URLSearchParams(data).toString() : JSON.stringify(data);
}
function withTimeout(fetchPromise, timeout, url) {
if (timeout === 0 || timeout == null) return fetchPromise;
return Promise.race([
// Fetch promise
fetchPromise,
// Timeout promise
new Promise(
(_, reject) => (
// Reject with timeout error
setTimeout(() => reject(new Error(`Request timed out: ${url}`)), timeout)
)
)
]);
}
function request(url, method = "GET", data = null, options = {}) {
const { timeout = 5e3, signal } = options;
const urlObj = new URL(url, window.location.origin);
const hasBody = data && !["GET", "DELETE"].includes(method);
const headers = mergeHeaders(options.headers, hasBody);
const body = hasBody ? processBody(data, headers) : void 0;
const fetchOptions = { method, headers, ...body && { body }, ...signal && { signal } };
const fetchPromise = fetch(urlObj.toString(), fetchOptions).then((response) => {
if (!response.ok) throw new Error(`HTTP ${response.status} at ${url}`);
const contentType = response.headers?.get("Content-Type") || "";
return contentType.includes("application/json") ? response.json() : response.text();
});
return withTimeout(fetchPromise, timeout, url);
}
var http, request_default;
var init_request = __esm({
"src/request.js"() {
init_core();
core_default.prototype.request = function(url, method = "GET", data = null, options = {}) {
return request(url, method, data, options);
};
http = {
// Read operations (safe, idempotent)
head: (url, options = {}) => request(url, "HEAD", null, options),
get: (url, options = {}) => request(url, "GET", null, options),
options: (url, options = {}) => request(url, "OPTIONS", null, options),
// Write operations (modifying data)
post: (url, data, options = {}) => request(url, "POST", data, options),
put: (url, data, options = {}) => request(url, "PUT", data, options),
patch: (url, data, options = {}) => request(url, "PATCH", data, options),
delete: (url, options = {}) => request(url, "DELETE", null, options),
// Utilities
abortController: () => new AbortController()
};
request_default = http;
}
});
// src/animate.js
var animate_exports = {};
var init_animate = __esm({
"src/animate.js"() {
init_core();
Object.assign(core_default.prototype, {
/**
* Animate elements with CSS transitions.
*
* @param {Object} styles - CSS properties to animate.
* @param {number} [duration=400] - Duration in milliseconds.
* @param {string} [easing="ease"] - Easing function.
* @returns {AnJS} - The current AnJS instance for chaining.
*/
animate(styles, duration = 400, easing = "ease") {
return this.each((el) => {
el.style.transition = `all ${duration}ms ${easing}`;
Object.assign(el.style, styles);
if (duration > 0) setTimeout(() => el.style.transition = "", duration);
});
},
/**
* Fade elements to a specific opacity.
*
* @param {number} [opacity] - Target opacity (0 to 1).
* @param {number} [duration=400] - Duration in milliseconds.
* @returns {AnJS} - The current AnJS instance for chaining.
*/
fade(opacity = +(this[0]?.style.opacity === "0"), duration = 400) {
return this.animate({ opacity }, duration);
},
/**
* Fade elements in or out.
*
* @param {number} [duration=400] - Duration in milliseconds.
* @return {AnJS} - The current AnJS instance for chaining.
*/
fadeIn(duration) {
return this.fade(1, duration);
},
/**
* Fade elements out.
*
* @param {number} [duration=400] - Duration in milliseconds.
* @return {AnJS} - The current AnJS instance for chaining.
*/
fadeOut(duration) {
return this.fade(0, duration);
}
});
}
});
// src/prebuilt.js
init_core();
// src/dom.js
init_core();
Object.assign(core_default.prototype, {
/**
* Get or set text or HTML content
*
* @param {string} [value] - Content to set.
* @param {boolean} [html=false] - Whether to set/get as HTML (true) or text (false).
* @returns {string | AnJS} - Content if getting, or self for chaining if setting.
*/
content(value, html = false) {
if (value === void 0) {
if (!this[0]) return "";
return html ? this[0].innerHTML : this[0].textContent;
}
return this.each((el) => html ? el.innerHTML = value : el.textContent = value);
},
/**
* Get or set text content
*
* @param {string} [value] - Text content to set.
* @return {string | AnJS} - Text content if getting, or self for chaining if setting.
*/
text(value) {
return this.content(value, false);
},
/**
* Get or set HTML content
*
* @param {string} [value] - HTML content to set.
* @return {string | AnJS} - HTML content if getting, or self for chaining if setting.
*/
html(value) {
return this.content(value, true);
},
/**
* Get or set CSS styles
*
* @param {string} name - CSS property name.
* @param {string} [value] - CSS value to set.
* @returns {string | AnJS} - CSS value if getting, or self for chaining if setting.
*/
css(name, value) {
if (value === void 0) return this[0]?.style.getPropertyValue(name) || "";
return this.each((el) => el.style[name] = value);
},
/**
* Add, remove, or toggle a class
*
* @param {string} name - Class name.
* @param {boolean} [add] - Add (true), Remove (false), Toggle (undefined).
* @returns {AnJS} - Self for chaining.
*/
class(name, add) {
return this.each((el) => el.classList[add === void 0 ? "toggle" : add ? "add" : "remove"](name));
},
/**
* Show or hide elements
*
* @param {boolean} show - Show (true), Hide (false).
* @returns {AnJS} - Self for chaining.
*/
display(show) {
return this.each((el) => el.style.display = show ? "" : "none");
},
/**
* Show or hide elements
*
* @returns {AnJS} - Self for chaining.
*/
hide() {
return this.display(false);
},
/**
* Show elements
*
* @returns {AnJS} - Self for chaining.
*/
show() {
return this.display(true);
},
/**
* Remove elements from the DOM
*
* @returns {AnJS} - Self for chaining.
*/
remove() {
return this.each((el) => el.remove());
},
/**
* Empty elements (remove all children)
*
* @returns {AnJS} - Self for chaining.
*/
empty() {
return this.each((el) => el.innerHTML = "");
},
/**
* Insert content relative to the selected element(s)
*
* @param {string | HTMLElement | HTMLElement[]} content - HTML string, element, or array of elements.
* @param {string} position - 'before', 'prepend', 'append', 'after'.
* @returns {AnJS} - Chainable instance.
*/
insert(content, position = "before") {
const positions = { before: "beforeBegin", prepend: "afterBegin", append: "beforeEnd", after: "afterEnd" };
if (!positions[position]) return this;
return this.each((target) => {
if (typeof content === "string") return target.insertAdjacentHTML(positions[position], content);
(Array.isArray(content) ? content : [content]).forEach(
(el) => (
// Insert element at specified position
target.insertAdjacentElement(positions[position], el.cloneNode(true))
)
);
});
},
/**
* Get or set a property
*
* @param {string} name - Property name.
* @param {any} value - Property value (optional).
* @returns {any | AnJS} - Property value if getting, or self for chaining if setting.
*/
prop(name, value) {
if (arguments.length === 1) return this[0]?.[name];
return this.each((el) => el[name] = value);
},
/**
* Get or set the value of form elements
*
* @param {string} [value] - The value to set (if provided).
* @returns {string | AnJS} - The current value if getting, or the AnJS instance if setting.
*/
val(value) {
if (arguments.length === 0) return this[0]?.value;
return this.each((el) => el.value = value);
},
/**
* Check if the first selected element has a class
*
* @param {string} className - Class name.
* @returns {boolean} - True if the class exists, otherwise false.
*/
has(className) {
return this[0]?.classList.contains(className) ?? false;
},
/**
* Focus on the first selected element
*
* @returns {AnJS} - Self for chaining.
*/
focus() {
this[0]?.focus();
return this;
},
/**
* Remove focus from the first selected element
*
* @returns {AnJS} - Self for chaining.
*/
blur() {
this[0]?.blur();
return this;
}
});
// src/attributes.js
init_core();
Object.assign(core_default.prototype, {
/**
* Get or set an attribute on selected elements
*
* @param {string} name - Attribute name.
* @param {string} [value] - Attribute value (if setting).
* @returns {string | AnJS} - Attribute value if getting, AnJS instance if setting.
*/
attr(name, value) {
if (value === void 0) return this[0]?.getAttribute(name);
if (value === null) return this.each((el) => el.removeAttribute(name));
return this.each((el) => el.setAttribute(name, value));
},
/**
* Get or set the id attribute
*
* @param {string} [value] - The id to set.
* @returns {string | AnJS} - The id if getting, otherwise chainable.
*/
id(value) {
return value === void 0 ? this.attr("id") : this.attr("id", value);
},
/**
* Remove an attribute from selected elements
*
* @param {string} name - Attribute name to remove.
* @returns {AnJS} - Returns self for chaining.
*/
removeAttr(name) {
return this.attr(name, null);
},
/**
* Serialize form data from the first selected element
*
* @returns {string} - URL-encoded form data string or an empty string if not a form.
*/
serialize() {
return this[0] instanceof HTMLFormElement ? new URLSearchParams(new FormData(this[0])).toString() : "";
}
});
// src/events.js
init_core();
var eventStore = /* @__PURE__ */ new WeakMap();
Object.assign(core_default.prototype, {
/**
* Attach an event listener (direct or delegated)
*
* @param {string} event - Event type (e.g., 'click')
* @param {string | Function} selector - Selector for delegation or event handler
* @param {Function} [handler] - Event handler if delegation is used
* @returns {AnJS} - Chainable instance
*/
on(event, selector, handler) {
return typeof selector === "function" ? this.delegate(event, null, selector) : this.delegate(event, selector, handler);
},
/**
* Remove an event listener (direct or delegated)
*
* @param {string} event - Event type (e.g., 'click')
* @param {string | Function} selector - Selector for delegation or event handler
* @param {Function} [handler] - Event handler if delegation is used
* @returns {AnJS} - Chainable instance
*/
off(event, selector, handler) {
return typeof selector === "function" ? this.undelegate(event, null, selector) : this.undelegate(event, selector, handler);
},
/**
* Attach a delegated event listener using WeakMap for storage
*
* @param {string} event - Event type (e.g., 'click')
* @param {string | null} selector - Selector to match (e.g., '.btn') or `null` for direct binding
* @param {Function} handler - Event callback function
* @returns {AnJS} - Chainable instance
*/
delegate(event, selector, handler) {
return this.each((el) => {
if (!eventStore.has(el)) eventStore.set(el, {});
const events = eventStore.get(el);
if (!events[event]) events[event] = [];
const delegateHandler = (e) => {
const target = selector ? e.target.closest(selector) : el;
if (target && el.contains(target)) handler.call(target, e);
};
events[event].push({ selector, handler, delegateHandler });
el.addEventListener(event, delegateHandler);
});
},
/**
* Remove a delegated event listener using WeakMap
*
* @param {string} event - Event type (e.g., 'click')
* @param {string | null} selector - Selector for delegation or `null` for direct binding
* @param {Function} [handler] - Specific handler to remove (optional)
* @returns {AnJS} - Chainable instance
*/
undelegate(event, selector, handler) {
return this.each((el) => {
if (!eventStore.has(el)) return;
const events = eventStore.get(el);
if (!events[event]) return;
if (!handler) {
events[event].forEach((item) => el.removeEventListener(event, item.delegateHandler));
delete events[event];
} else {
events[event] = events[event].filter((item) => {
if (item.selector === selector && item.handler === handler) {
el.removeEventListener(event, item.delegateHandler);
return false;
}
return true;
});
if (events[event].length === 0) delete events[event];
}
if (Object.keys(events).length === 0) eventStore.delete(el);
});
},
/**
* Trigger a custom event on elements
*
* @param {string} event - Event type to trigger (e.g., 'click')
* @returns {AnJS} - Chainable instance
*/
trigger(event) {
return this.each((el) => el.dispatchEvent(new Event(event, { bubbles: true })));
}
});
var eventBus = {};
var bus = {
/**
* Emit a global event
*
* @param {string} event - The event name
* @param {any} [data] - Optional data to send
*/
emit(event, data) {
eventBus[event]?.forEach((handler) => handler(data));
},
/**
* Listen for a global event
*
* @param {string} event - The event name
* @param {Function} handler - The callback function
*/
listen(event, handler) {
if (!eventBus[event]) eventBus[event] = [];
eventBus[event].push(handler);
},
/**
* Remove a global event listener
*
* @param {string} event - The event name
* @param {Function} handler - The callback function to remove
*/
forget(event, handler) {
if (eventBus[event]) {
eventBus[event] = eventBus[event].filter((h) => h !== handler);
if (eventBus[event].length === 0) delete eventBus[event];
}
}
};
// src/alias.js
init_core();
["append", "prepend", "before", "after"].forEach(
(method) => (
// Create alias method
core_default.prototype[method] = function(content) {
return this.insert(content, method);
}
)
);
["click", "change", "submit", "keydown", "keyup", "mouseover", "mouseout"].forEach((event) => {
core_default.prototype[event] = function(callback) {
return callback ? this.on(event, callback) : this.trigger(event);
};
});
// src/utilities.js
var trim = (string) => string.trim();
var json = (string) => {
try {
return JSON.parse(string);
} catch {
return null;
}
};
var range = (x, min, max) => (x - min) * (x - max) <= 0;
var isFunction = (obj) => typeof obj === "function";
var isObject = (obj) => obj !== null && typeof obj === "object";
var isString = (obj) => typeof obj === "string";
var isNumber = (obj) => typeof obj === "number" && !isNaN(obj);
var contains = (parent, child) => parent !== child && parent.contains(child);
var debounce = (fn, delay) => {
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(() => fn(...args), delay);
};
};
var throttle = (fn, limit) => {
let lastCall = 0;
return (...args) => {
const now = Date.now();
if (now - lastCall >= limit) {
lastCall = now;
fn(...args);
}
};
};
var element = (tag, attrs = {}, children = []) => {
const el = document.createElement(tag);
Object.entries(attrs).forEach(([key, value]) => el.setAttribute(key, value));
children.forEach((child) => el.append(child instanceof Node ? child : document.createTextNode(child)));
return el;
};
var utilities_default = { trim, json, range, isFunction, isObject, isString, isNumber, contains, debounce, throttle, element };
// src/extend.js
init_core();
var extend = function(name, func, force = false) {
if (typeof name === "object") {
if (typeof func === "boolean") force = func;
return Object.keys(name).forEach((key) => extend(key, name[key], force));
}
if (!force && core_default.prototype.hasOwnProperty(name)) return;
core_default.prototype[name] = func;
};
var extend_default = { extend };
// src/prebuilt.js
if (typeof FEATURE_HTTP === "undefined") globalThis.FEATURE_HTTP = true;
if (typeof FEATURE_ANIMATE === "undefined") globalThis.FEATURE_ANIMATE = true;
if (typeof FEATURE_SELECTION === "undefined") globalThis.FEATURE_SELECTION = true;
if (typeof FEATURE_FILTERING === "undefined") globalThis.FEATURE_FILTERING = true;
if (typeof FEATURE_TRAVERSAL === "undefined") globalThis.FEATURE_TRAVERSAL = true;
if (false) globalThis.FEATURE_STATE = true;
if (false) globalThis.FEATURE_COMPONENTS = true;
if (false) globalThis.FEATURE_ELEMENTS = true;
function $2(selector) {
return new core_default(selector);
}
["on", "off", "trigger"].forEach((method) => {
$2[method] = (...args) => core_default.prototype[method].apply($2(), args);
});
if (FEATURE_SELECTION) {
Promise.resolve().then(() => (init_filtering(), filtering_exports)).then((mod) => Object.assign($2, mod));
Promise.resolve().then(() => (init_traversal(), traversal_exports)).then((mod) => Object.assign($2, mod));
}
if (true) Promise.resolve().then(() => (init_state(), state_exports)).then(() => {
["state", "global"].forEach((module) => {
$2[module] = (...args) => core_default.prototype[module].apply($2(), args);
});
});
if (true) {
Promise.resolve().then(() => (init_component(), component_exports)).then((mod) => {
["component"].forEach((module) => {
$2[module] = (...args) => core_default.prototype[module].apply($2(), args);
});
$2.define = (name, componentClass) => customElements.define(name, componentClass);
});
}
if (false) ;
if (FEATURE_HTTP) Promise.resolve().then(() => (init_request(), request_exports)).then((mod) => {
Object.assign($2, mod.http);
});
if (FEATURE_ANIMATE) Promise.resolve().then(() => init_animate());
Object.assign($2, bus, utilities_default, extend_default);
$2["define"] = (name, componentClass) => customElements.define(name, componentClass);
if (typeof window !== "undefined") window.$ = $2;
var prebuilt_default = $2;
})();
window.$ = $;
//# sourceMappingURL=almostno.full.js.map