Source

event.js

//#region Event
/**
 * Waits until a condition is met then resolves a promise.
 * @returns {Promise} A promise resolved when the condition returned by the function is true.
 * @memberOf event
 * @function
 * @example
 * //Waits until the current second of the current minute is 10.
 * _$.waitUntil(() => new Date().getSeconds === 10).then(() => console.log("Done"))
 * @example
 * //This DOES NOT work
 * _$.waitUntil(() => Date.now() === Date.now() + 100);
 * //Because it is evaluated many times, and the current date, is never ahead of itself. Therefore in this case the function will run infinitely.
 * //To fix this problem and cancel the function after a certain amount of time,
 * //you can pass another argument to the function
 * _$.waitUntil(() => false, 10000);//Waits 10 seconds, because the function always returns false.
 * @param {Function} condition The function which returns true when the condition is met
 * @param {Number} [wait=Infinity] The wait time in milliseconds to cancel the function and reject the promise.
 */
export let waitUntil = async (
	condition = req("function", "condition"),
	wait = Infinity,
) => {
	return new Promise(async (resolve, reject) => {
		let startTime = Date.now();
		while (!condition()) {
			if (Date.now() - startTime >= wait) {
				reject(condition());
				return;
			}
			await new Promise((res) => requestAnimationFrame(res));
		}
		resolve(condition());
		return condition();
	});
};
/**
 * Returns the callback when a a click is registered outside the selected element
 * @function
 * @memberOf event
 * @param {Element} element The element to use as the outsideclick element.
 * @param {Function} callback The function to run when a click is registered outside the specified element.
 * @example
 * _$.onOutsideClick(document.querySelector("div"), () => {alert("You clicked outside the DIV!")});
 * @returns {Promise} A promise that is resolved when the user clicks outside the specified element.
 */
export let onOutsideClick = (
	element = req("HTMLElement", "element"),
	callback = req("function", "callback"),
) => {
	node();
	return new Promise((resolve, reject) => {
		document.addEventListener("click", (e) => {
			if (!element.contains(e.target)) {
				callback();
				resolve();
			}
		});
	});
};
/**
 * @callback scrollStopCallback
 * @param {UIEvent} event The event object
 * @returns {undefined}
 */
/**
 * Returns the callback when the user stops scrolling.
 * @function
 * @memberOf event
 * @param {HTMLElement} [element=window] The HTML element to listen on for scroll stop.
 * @param {Function} callback The callback to call when the user stops scrolling.
 * @param {Number} [time=150]
 * @example
 * _$.onScrollStop(() => {alert("You stopped scrolling!")})
 * @returns {Promise} Returns a promise that is resolved when the user stops scrolling.
 */
export let onScrollStop = (
	element = window,
	callback = req("function", "callback"),
	time = 150,
) => {
	let isScrolling;
	node();
	return new Promise((resolve, reject) => {
		element.addEventListener(
			"scroll",
			(e) => {
				clearTimeout(isScrolling);
				isScrolling = setTimeout(() => {
					callback(e);
					resolve(e);
				}, time);
			},
			false,
		);
	});
};
/**
 * A lot like socket.io, this allows emit, on and off handlers. (Note that this is local, only your computer sends and recieves your data. Still useful though)
 * @memberOf event
 * @function
 * @returns {Object} The object with the emit, on and off functions in it.
 * @example
 * let thing = _$.hub();
 * // Log any new data to the console
 * thing.on("data", (data) => console.log(data));
 * setTimeout(() => {
 *   thing.emit("data", "Yay! Some data!!"); // Logs "Yay! Some data!!" to the console after 2 seconds.
 * }, 2000)
 */
export let hub = () => ({
	hub: Object.create(null),
	emit(event, data) {
		(this.hub[event] || []).forEach((handler) => handler(data));
	},
	on(event, handler) {
		if (!this.hub[event]) this.hub[event] = [];
		this.hub[event].push(handler);
	},
	off(event, handler) {
		const i = (this.hub[event] || []).findIndex((h) => h === handler);
		if (i > -1) this.hub[event].splice(i, 1);
		if (this.hub[event].length === 0) delete this.hub[event];
	},
});
/**
 * Dispatches an event of the type specified with custom arguments.
 * @memberOf event
 * @function
 * @example
 * //Dispatch a custom mouse move event to the window.
 * _$.dispatch("mousemove", {clientX: 100, clientY: 150, target: document.documentElement}, window);
 * @param {String} type The type of event to dispatch (E.g. "mousemove")
 * @param {Object} args The argument representing the event, e.g. {clientX: 100, clientY: 150}
 * @param {EventTarget} [target=window] What to dispatch the event to.
 * @returns {Event} The event object.
 */
export let dispatch = (
	args = req("object", "event properties"),
	type = req("string", "type"),
	target = window,
) => {
	let e = new Event(type);
	for (let o in args) {
		e[o] = args[o];
	}
	target.dispatchEvent(e);
	return e;
};
//#endregion Event