Source: light-query.js

/* exported $ */
class _${
	/**
	 * 
	 * @param {Element|NodeList|Array|String|Function} parameter The parameter to initialize the _$ object with
	 * @param {Element} [context] 
	 */
	constructor(parameter, context){
		/**
		 * Elements holder
		 * @type {Element[]}
		 * @private
		 */
		this.items = [];

		if(parameter instanceof Function){
			window.addEventListener('load', () => {
				Reflect.apply(parameter, null, []);
			});
		}else{
			this.items.push(..._$._STD(parameter, context));
		}
	}

	/**
	 * Standardizes any input to an Element array
	 * @param {Element|NodeList|Array|String} parameter Element to standardize
	 * @param {Element} [context]                       Potential query context
	 * @private
	 */
	static _STD(parameter, context){
		const result = [];

		switch (parameter.constructor){
			// Element passed as a parameter
			case Element:
				result.push(parameter);

				break;
			// Array-like passed as a parameter
			case NodeList:
			case Array:
				result.push(...parameter);

				break;
			// String passed as a parameter
			case String:
				// Valid selector
				try {
					// Check the context before querying globally
					const elements = (context || document).querySelectorAll(parameter);

					result.push(...elements);
				// Invalid selector
				} catch (error) {
					const template = document.createElement('template');

					// Try to create a DOM from the string
					template.innerHTML = parameter;

					result.push(...template.content.childNodes);
				}

				break;
			default:
		}

		return result;
	}

	/**
	 * Add elements to the current _$ elements
	 * @param {Element|NodeList|Array|String} parameter Element(s) to add
	 * @param {Element}                       [context] Context of the potential query
	 * @returns {_$} The current object
	 */
	add(parameter, context){
		if(parameter instanceof _$){
			this.items.push(...parameter.items);
		}else{
			this.items.push(..._$._STD(parameter, context));
		}

		return this;
	}

	/**
	 * Add class(es) to each element
	 * @param {String|Function} parameter Space separated classes to add
	 * @returns {_$} The current object
	 */
	addClass(parameter){
		this.items.forEach(item => {
			item.classList.add(...parameter.split(/\s+/));
		});

		return this;
	}

	/**
	 * Insert content after each element
	 * @param {Element[]|NodeList[]|Array[]|String[]|_$[]} elements Elements to be inserted
	 * @returns {_$} The current object
	 */
	after(...elements){
		this.items.forEach(item => {
			elements.forEach(newElement => {
				let previousElement = item;

				_$._STD(newElement).forEach(newSingleElement => {
					previousElement.insertAdjacentElement('afterend', newSingleElement);
					previousElement = newSingleElement;
				});
			});
		});

		return this;
	}

	/**
	 * Append content to the end of each element
	 * @param {Element[]|NodeList[]|Array[]|String[]|_$[]} elements Elements to be appended
	 * @returns {_$} The current object
	 */
	append(...elements){
		this.items.forEach(item => {
			elements.forEach(newElement => {
				item.append(..._$._STD(newElement));
			});
		});

		return this;
	}

	/**
	 * Append each element to the end of the targets
	 * @param {Element|NodeList|Array|String|_$} targets Elements to be appended to
	 * @returns {_$} The current object
	 */
	appendTo(targets){
		_$._STD(targets).forEach(target => {
			target.append(...this.items);
		});

		return this;
	}

	/**
	 * Set/Get an attribute for each element
	 * @param {String}              name    The attribute name
	 * @param {String|Number|null}  [value] The attribute value
	 * @returns {_$|String} The current object or the value of the attribute
	 */
	attr(name, value){
		// Getter
		if(typeof value == 'undefined'){
			return this.items[0].getAttribute(name);
		}

		// Setter
		this.items.forEach(item => {
			item.setAttribute(name, value);
		});

		return this;
	}

	/**
	 * Insert content before each element
	 * @param {Element[]|NodeList[]|Array[]|String[]|_$[]} elements Elements to be inserted
	 * @returns {_$} The current object
	 */
	before(...elements){
		// For each current element
		this.items.forEach(item => {
			// For each new element
			elements.forEach(newElement => {
				// For each single node from the new element
				_$._STD(newElement).forEach(newSingleElement => {
					item.insertAdjacentElement('beforebegin', newSingleElement);
				});
			});
		});

		return this;
	}

	/**
	 * Force the focus out of each element
	 * @returns {_$} The current object
	 */
	blur(){
		this.items.forEach(item => {
			item.blur();
		});

		return this;
	}

	/**
	 * Get the children of each element
	 * @param {String}       [selector] An optional filter
	 * @returns {_$} The current object's children
	 */
	children(selector){
		const children = [];

		this.items.forEach(item => {
			[...item.children].forEach(child => {
				if(!selector || child.matches(selector)) children.push(child);
			});
		});

		return new _$(children);
	}

	/**
	 * Clone each element
	 * @param {Boolean}      [deep=true] Deep clone the elements ?
	 * @returns {_$} A clone from each current element
	 */
	clone(deep){
		return new _$(this.items.map(item => item.cloneNode(!!deep)));
	}
}

const $ = parameter => new _$(parameter);