216 lines
7.0 KiB
TypeScript
216 lines
7.0 KiB
TypeScript
|
// Fake jQuery because jQuery is for faggots
|
||
|
|
||
|
let document: Document | null = null;
|
||
|
|
||
|
export class SelectorError extends Error {
|
||
|
constructor(...args: any[]) {
|
||
|
super(...args);
|
||
|
this.name = 'SelectorError';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export const $ = function(queryString: string): HTMLElement {
|
||
|
if (document === null) {
|
||
|
throw new SelectorError('document not specified');
|
||
|
}
|
||
|
let element = document.querySelector<HTMLElement>(queryString);
|
||
|
if (element === null) {
|
||
|
throw new SelectorError(`unable to find [${queryString}] in document`);
|
||
|
}
|
||
|
return element;
|
||
|
}
|
||
|
export const $_ = function(queryString: string): HTMLElement | null {
|
||
|
try {
|
||
|
return $(queryString);
|
||
|
} catch (e) {
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
export const $$ = function(queryString: string): HTMLElement[] { // Because ElementList Objects are cancerous
|
||
|
if (document === null) {
|
||
|
throw new SelectorError('document not specified');
|
||
|
}
|
||
|
return Array.from(document.querySelectorAll(queryString));
|
||
|
}
|
||
|
export const $$$ = function(element: HTMLElement, queryString: string): HTMLElement {
|
||
|
let foundElement = element.querySelector<HTMLElement>(queryString);
|
||
|
if (foundElement === null) {
|
||
|
throw new SelectorError(`unable to find [${queryString}] in element`);
|
||
|
}
|
||
|
return foundElement;
|
||
|
}
|
||
|
export const $$$$ = function(element: HTMLElement, queryString: string): HTMLElement[] {
|
||
|
return Array.from(element.querySelectorAll(queryString));
|
||
|
}
|
||
|
|
||
|
// Set the document for all q.js functions to use
|
||
|
$.setDocument = function(doc: Document) {
|
||
|
document = doc;
|
||
|
}
|
||
|
|
||
|
// General javascript functions
|
||
|
|
||
|
/**
|
||
|
* @function $.zip
|
||
|
* @description Zip arrays together
|
||
|
* $.zip([2, 3, 4], [5, 6]) -> [ [2, 5], [3, 6], [4, undefined] ]
|
||
|
* $.zip([3, 4], [5, 6, 7]) -> [ [3, 5], [4, 6], [undefined, 7] ]
|
||
|
* @param {...any} arrays Lists to zip together
|
||
|
* @return The arrays zipped together
|
||
|
*/
|
||
|
$.zip = function(...arrays: any[]): any[] {
|
||
|
let n = 0;
|
||
|
let result: any[] = [];
|
||
|
for (let array of arrays) {
|
||
|
$.assert(Array.isArray(array), 'all parameters must be arrays');
|
||
|
for (let i = 0; i < array.length; ++i) {
|
||
|
if (i == result.length) {
|
||
|
result.push([]);
|
||
|
for (let j = 0; j < n; ++j) {
|
||
|
result[i].push(undefined);
|
||
|
}
|
||
|
}
|
||
|
result[i].push(array[i]);
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @function $.previousElement
|
||
|
* @description gets the previous HTMLElement in the DOM. Useful to skip any text nodes
|
||
|
* @param element HTMLElement to get the previous element of
|
||
|
* @returns the previous HTMLElement in the DOM or null if there are no HTMLElements before the specified element.
|
||
|
*/
|
||
|
$.previousElement = function(element: HTMLElement): HTMLElement | null {
|
||
|
let current: ChildNode | HTMLElement = element;
|
||
|
while (current.previousSibling) {
|
||
|
if (current.previousSibling instanceof HTMLElement) {
|
||
|
return current.previousSibling;
|
||
|
}
|
||
|
current = current.previousSibling;
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @function $.nextElement
|
||
|
* @description gets the next HTMLElement in the DOM. Useful to skip any text nodes
|
||
|
* @param element HTMLElement to get the next element of
|
||
|
* @returns the next HTMLElement in the DOM or null if there are no HTMLElements after the specified element.
|
||
|
*/
|
||
|
$.nextElement = function(element: HTMLElement): HTMLElement | null {
|
||
|
let current: ChildNode | HTMLElement = element;
|
||
|
while (current.nextSibling) {
|
||
|
if (current.nextSibling instanceof HTMLElement) {
|
||
|
return current.nextSibling;
|
||
|
}
|
||
|
current = current.nextSibling;
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
// From elipzer.com q.js, custom HTMLElement generation
|
||
|
|
||
|
/**
|
||
|
* @function $.assert
|
||
|
* @description Asserts that the value is true, throwing an error with specified
|
||
|
* message otherwise
|
||
|
* @param value The value of the assertion
|
||
|
* @param message The message to be put in the error message if the value is falsy
|
||
|
*/
|
||
|
$.assert = function(value: boolean, message: string): void {
|
||
|
if (!value) {
|
||
|
throw new Error(message);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @function $.addContent
|
||
|
* @description Adds to the internal content of an HTMLElement
|
||
|
* @param {HTMLElement} element The element to add content to
|
||
|
* @param content The content to be added to the element
|
||
|
*/
|
||
|
$.addContent = function(element: HTMLElement, content: any) {
|
||
|
content = Array.isArray(content) ? content : [ content ];
|
||
|
for (let e of content) {
|
||
|
element.appendChild($.create(e));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @function $.clearChildren
|
||
|
* @description removes all children from an element
|
||
|
* @param {HTMLElement} element The element to remove all children from
|
||
|
*/
|
||
|
$.clearChildren = function(element: HTMLElement): void {
|
||
|
while (element.firstChild) {
|
||
|
element.removeChild(element.firstChild);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @function $.create
|
||
|
* Recursively creates an HTMLElement from the specified content
|
||
|
* @param obj An element initialization object { tag, id, class, content, ... }
|
||
|
* @var tag The HTML tag of the element (default: 'div')
|
||
|
* @var id The id of the elemnt
|
||
|
* @var class The class of the element (string or list of strings)
|
||
|
* @var content The internal content of the element. This is handled recursively.
|
||
|
* Can be a string, HTMLElement, obj, list of objs, or list of HTMLElements
|
||
|
* @var ... All other attributes will be applied using element.setAttribute(key, value)
|
||
|
*/
|
||
|
$.create = function(obj: HTMLElement | any | null): Node {
|
||
|
if (document === null) {
|
||
|
throw new Error('document not specified');
|
||
|
}
|
||
|
|
||
|
if (obj instanceof HTMLElement) {
|
||
|
return obj
|
||
|
} else if (typeof obj !== 'object') {
|
||
|
return document.createTextNode(obj.toString());
|
||
|
} else if (obj == null) {
|
||
|
return document.createTextNode('[null]');
|
||
|
}
|
||
|
|
||
|
$.assert(obj !== undefined, 'obj is undefined');
|
||
|
|
||
|
let element: HTMLElement;
|
||
|
if (obj.ns) {
|
||
|
element = document.createElementNS(obj.ns, obj.tag || 'div');
|
||
|
} else {
|
||
|
element = document.createElement(obj.tag || 'div');
|
||
|
}
|
||
|
|
||
|
if (obj.id) {
|
||
|
element.id = obj.id;
|
||
|
}
|
||
|
|
||
|
if (obj.class) {
|
||
|
if (Array.isArray(obj.class)) {
|
||
|
for (let c of obj.class) {
|
||
|
$.assert(typeof c === 'string', `Invalid obj.class[i] type: ${typeof c}`);
|
||
|
element.classList.add(c);
|
||
|
}
|
||
|
} else if (typeof obj.class === 'string') {
|
||
|
element.className = obj.class;
|
||
|
} else {
|
||
|
throw new Error(`Invalid obj.class type: ${obj.class}`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (let key in obj) {
|
||
|
if (key !== 'tag' && key !== 'id' && key != 'class' && key != 'content') {
|
||
|
if (obj.hasOwnProperty(key)) {
|
||
|
element.setAttribute(key.toString(), obj[key].toString());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (obj.content !== undefined) {
|
||
|
$.addContent(element, obj.content);
|
||
|
}
|
||
|
|
||
|
return element;
|
||
|
}
|