domtastic
Version:
Small, fast, and modular DOM and event library for modern browsers.
1,710 lines (1,512 loc) • 55.9 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = global || self, global.$ = factory());
}(this, function () { 'use strict';
/*
* @module Util
*/
/*
* Reference to the window object
* @private
*/
var win = typeof window !== 'undefined' ? window : {};
/**
* Convert `NodeList` to `Array`.
*
* @param {NodeList|Array} collection
* @return {Array}
* @private
*/
var toArray = function toArray(collection) {
var length = collection.length;
var result = new Array(length);
for (var i = 0; i < length; i++) {
result[i] = collection[i];
}
return result;
};
/**
* Faster alternative to [].forEach method
*
* @param {Node|NodeList|Array} collection
* @param {Function} callback
* @return {Node|NodeList|Array}
* @private
*/
var each = function each(collection, callback, thisArg) {
var length = collection.length;
if (length !== undefined && collection.nodeType === undefined) {
for (var i = 0; i < length; i++) {
callback.call(thisArg, collection[i], i, collection);
}
} else {
callback.call(thisArg, collection, 0, collection);
}
return collection;
};
/**
* Assign enumerable properties from source object(s) to target object
*
* @method extend
* @param {Object} target Object to extend
* @param {Object} [source] Object to extend from
* @return {Object} Extended object
* @example
* $.extend({a: 1}, {b: 2}); // {a: 1, b: 2}
* @example
* $.extend({a: 1}, {b: 2}, {a: 3}); // {a: 3, b: 2}
*/
var extend = function extend(target) {
for (var _len = arguments.length, sources = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
sources[_key - 1] = arguments[_key];
}
sources.forEach(function (src) {
for (var prop in src) {
target[prop] = src[prop];
}
});
return target;
};
/**
* Return the collection without duplicates
*
* @param collection Collection to remove duplicates from
* @return {Node|NodeList|Array}
* @private
*/
var uniq = function uniq(collection) {
return collection.filter(function (item, index) {
return collection.indexOf(item) === index;
});
};
/**
* @module Selector
*/
var isPrototypeSet = false;
var reFragment = /^\s*<(\w+|!)[^>]*>/;
var reSingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/;
var reSimpleSelector = /^[.#]?[\w-]*$/;
/*
* Versatile wrapper for `querySelectorAll`.
*
* @param {String|Node|NodeList|Array} selector Query selector, `Node`, `NodeList`, array of elements, or HTML fragment string.
* @param {String|Node|NodeList} context=document The context for the selector to query elements.
* @return {Object} The wrapped collection
* @chainable
* @example
* var $items = $(.items');
* @example
* var $element = $(domElement);
* @example
* var $list = $(nodeList, document.body);
* @example
* var $element = $('<p>evergreen</p>');
*/
var domtastic = function domtastic(selector, context) {
if (context === void 0) {
context = document;
}
var collection;
if (!selector) {
collection = document.querySelectorAll(null);
} else if (selector instanceof DOMtastic) {
return selector;
} else if (typeof selector !== 'string') {
collection = selector.nodeType || selector === window ? [selector] : selector;
} else if (reFragment.test(selector)) {
collection = createFragment(selector);
} else {
context = typeof context === 'string' ? document.querySelector(context) : context.length ? context[0] : context;
collection = querySelector(selector, context);
}
return wrap(collection);
};
var $ = domtastic;
/*
* Find descendants matching the provided `selector` for each element in the collection.
*
* @param {String|Node|NodeList|Array} selector Query selector, `Node`, `NodeList`, array of elements, or HTML fragment string.
* @return {Object} The wrapped collection
* @example
* $('.selector').find('.deep').$('.deepest');
*/
var find = function find(selector) {
var nodes = [];
each(this, function (node) {
return each(querySelector(selector, node), function (child) {
if (nodes.indexOf(child) === -1) {
nodes.push(child);
}
});
});
return $(nodes);
};
/*
* Returns `true` if the element would be selected by the specified selector string; otherwise, returns `false`.
*
* @param {Node} element Element to test
* @param {String} selector Selector to match against element
* @return {Boolean}
*
* @example
* $.matches(element, '.match');
*/
var matches = function () {
var context = typeof Element !== 'undefined' ? Element.prototype : win;
var _matches = context.matches || context.matchesSelector || context.mozMatchesSelector || context.msMatchesSelector || context.oMatchesSelector || context.webkitMatchesSelector;
return function (element, selector) {
return _matches.call(element, selector);
};
}();
/*
* Use the faster `getElementById`, `getElementsByClassName` or `getElementsByTagName` over `querySelectorAll` if possible.
*
* @private
* @param {String} selector Query selector.
* @param {Node} context The context for the selector to query elements.
* @return {Object} NodeList, HTMLCollection, or Array of matching elements (depending on method used).
*/
var querySelector = function querySelector(selector, context) {
var isSimpleSelector = reSimpleSelector.test(selector);
if (isSimpleSelector) {
if (selector[0] === '#') {
var element = (context.getElementById ? context : document).getElementById(selector.slice(1));
return element ? [element] : [];
}
if (selector[0] === '.') {
return context.getElementsByClassName(selector.slice(1));
}
return context.getElementsByTagName(selector);
}
return context.querySelectorAll(selector);
};
/*
* Create DOM fragment from an HTML string
*
* @private
* @param {String} html String representing HTML.
* @return {NodeList}
*/
var createFragment = function createFragment(html) {
if (reSingleTag.test(html)) {
return [document.createElement(RegExp.$1)];
}
var elements = [];
var container = document.createElement('div');
var children = container.childNodes;
container.innerHTML = html;
for (var i = 0, l = children.length; i < l; i++) {
elements.push(children[i]);
}
return elements;
};
/*
* Calling `$(selector)` returns a wrapped collection of elements.
*
* @private
* @param {NodeList|Array} collection Element(s) to wrap.
* @return Object) The wrapped collection
*/
var wrap = function wrap(collection) {
if (!isPrototypeSet) {
DOMtastic.prototype = $.fn;
DOMtastic.prototype.constructor = DOMtastic;
isPrototypeSet = true;
}
return new DOMtastic(collection);
};
/*
* Constructor for the Object.prototype strategy
*
* @constructor
* @private
* @param {NodeList|Array} collection Element(s) to wrap.
*/
var DOMtastic = function DOMtastic(collection) {
var i = 0;
var length = collection.length;
for (; i < length;) {
this[i] = collection[i++];
}
this.length = length;
};
var selector = /*#__PURE__*/Object.freeze({
$: $,
find: find,
matches: matches,
DOMtastic: DOMtastic
});
/**
* @module Array
*/
var ArrayProto = Array.prototype;
/**
* Checks if the given callback returns a true(-ish) value for each element in the collection.
*
* @param {Function} callback Function to execute for each element, invoked with `element` as argument.
* @param {Object} [thisArg] Value to use as `this` when executing `callback`.
* @return {Boolean} Whether each element passed the callback check.
* @example
* // Test whether every element in the collection has the "active" attribute
* $('.items').every(function(element) {
* return element.hasAttribute('active')
* });
*/
var every = ArrayProto.every;
/**
* Filter the collection by selector or function, and return a new collection with the result.
*
* @param {String|Function} selector Selector or function to filter the collection.
* @param {Object} [thisArg] Value to use as `this` when executing `callback`.
* @return {Object} A new wrapped collection
* @chainable
* @example
* $('.items').filter('.active');
* @example
* $('.items').filter(function(element) {
* return element.hasAttribute('active')
* });
*/
var filter = function filter(selector, thisArg) {
var callback = typeof selector === 'function' ? selector : function (element) {
return matches(element, selector);
};
return $(ArrayProto.filter.call(this, callback, thisArg));
};
/**
* Execute a function for each element in the collection.
*
* @param {Function} callback Function to execute for each element, invoked with `element` as argument.
* @param {Object} [thisArg] Value to use as `this` when executing `callback`.
* @return {Object} The wrapped collection
* @chainable
* @example
* $('.items').forEach(function(element) {
* element.style.color = 'evergreen';
* );
*/
var forEach = function forEach(callback, thisArg) {
return each(this, callback, thisArg);
};
var each$1 = forEach;
/**
* Returns the index of an element in the collection.
*
* @param {Node} element
* @return {Number} The zero-based index, -1 if not found.
* @example
* $('.items').indexOf(element); // 2
*/
var indexOf = ArrayProto.indexOf;
/**
* Create a new collection by executing the callback for each element in the collection.
*
* @param {Function} callback Function to execute for each element, invoked with `element` as argument.
* @param {Object} [thisArg] Value to use as `this` when executing `callback`.
* @return {Array} Collection with the return value of the executed callback for each element.
* @example
* // Create a new array with the attribute value of each element:
* $('.items').map(function(element) {
* return element.getAttribute('name')
*/
var map = ArrayProto.map;
/**
* Removes the last element from the collection, and returns that element.
*
* @return {Object} The last element from the collection.
* @example
* var lastElement = $('.items').pop();
*/
var pop = ArrayProto.pop;
/**
* Adds one or more elements to the end of the collection, and returns the new length of the collection.
*
* @param {Object} element Element(s) to add to the collection
* @return {Number} The new length of the collection
* @example
* $('.items').push(element);
*/
var push = ArrayProto.push;
/**
* Apply a function against each element in the collection, and this accumulator function has to reduce it
* to a single value.
*
* @param {Function} callback Function to execute on each value in the array, taking four arguments (see example).
* @param {Mixed} initialValue Object to use as the first argument to the first call of the callback.
* @example
* // Calculate the combined height of elements:
* $('.items').reduce(function(previousValue, element, index, collection) {
* return previousValue + element.clientHeight;
* }, 0);
*/
var reduce = ArrayProto.reduce;
/**
* Apply a function against each element in the collection (from right-to-left), and this accumulator function has
* to reduce it to a single value.
*
* @param {Function} callback Function to execute on each value in the array, taking four arguments (see example).
* @param {Mixed} initialValue Object to use as the first argument to the first call of the callback.
* @example
* // Concatenate the text of elements in reversed order:
* $('.items').reduceRight(function(previousValue, element, index, collection) {
* return previousValue + element.textContent;
* }, '');
*/
var reduceRight = ArrayProto.reduceRight;
/**
* Reverses an array in place. The first array element becomes the last and the last becomes the first.
*
* @return {Object} The wrapped collection, reversed
* @chainable
* @example
* $('.items').reverse();
*/
var reverse = function reverse() {
return $(toArray(this).reverse());
};
/**
* Removes the first element from the collection, and returns that element.
*
* @return {Object} The first element from the collection.
* @example
* var firstElement = $('.items').shift();
*/
var shift = ArrayProto.shift;
/**
* Checks if the given callback returns a true(-ish) value for any of the elements in the collection.
*
* @param {Function} callback Function to execute for each element, invoked with `element` as argument.
* @return {Boolean} Whether any element passed the callback check.
* @example
* $('.items').some(function(element) {
* return element.hasAttribute('active')
* }); // true/false
*/
var some = ArrayProto.some;
/**
* Adds one or more elements to the beginning of the collection, and returns the new length of the collection.
*
* @param {Object} element Element(s) to add to the collection
* @return {Number} The new length of the collection
* @example
* $('.items').unshift(element);
*/
var unshift = ArrayProto.unshift;
var array = /*#__PURE__*/Object.freeze({
every: every,
filter: filter,
forEach: forEach,
each: each$1,
indexOf: indexOf,
map: map,
pop: pop,
push: push,
reduce: reduce,
reduceRight: reduceRight,
reverse: reverse,
shift: shift,
some: some,
unshift: unshift
});
/**
* @module BaseClass
*/
function BaseClass (api) {
/**
* Provide subclass for classes or components to extend from.
* The opposite and successor of plugins (no need to extend `$.fn` anymore, complete control).
*
* @return {Class} The class to extend from, including all `$.fn` methods.
* @example
* import { BaseClass } from 'domtastic';
*
* class MyComponent extends BaseClass {
* doSomething() {
* return this.addClass('.foo');
* }
* }
*
* let component = new MyComponent('body');
* component.doSomething();
*
* @example
* import $ from 'domtastic';
*
* class MyComponent extends $.BaseClass {
* progress(value) {
* return this.attr('data-progress', value);
* }
* }
*
* let component = new MyComponent(document.body);
* component.progress('ive').append('<p>enhancement</p>');
*/
var BaseClass = function BaseClass() {
DOMtastic.call(this, $.apply(void 0, arguments));
};
extend(BaseClass.prototype, api);
return BaseClass;
}
/**
* @module CSS
*/
var isNumeric = function isNumeric(value) {
return !isNaN(parseFloat(value)) && isFinite(value);
};
var camelize = function camelize(value) {
return value.replace(/-([\da-z])/gi, function (matches, letter) {
return letter.toUpperCase();
});
};
var dasherize = function dasherize(value) {
return value.replace(/([a-z\d])([A-Z])/g, '$1-$2').toLowerCase();
};
/**
* Get the value of a style property for the first element, or set one or more style properties for each element in the collection.
*
* @param {String|Object} key The name of the style property to get or set. Or an object containing key-value pairs to set as style properties.
* @param {String} [value] The value of the style property to set.
* @return {Object} The wrapped collection
* @chainable
* @example
* $('.item').css('padding-left'); // get
* $('.item').css('color', '#f00'); // set
* $('.item').css({'border-width': '1px', display: 'inline-block'}); // set multiple
*/
var css = function css(key, value) {
var styleProps, prop, val;
if (typeof key === 'string') {
key = camelize(key);
if (typeof value === 'undefined') {
var element = this.nodeType ? this : this[0];
if (element) {
val = element.style[key];
return isNumeric(val) ? parseFloat(val) : val;
}
return undefined;
}
styleProps = {};
styleProps[key] = value;
} else {
styleProps = key;
for (prop in styleProps) {
val = styleProps[prop];
delete styleProps[prop];
styleProps[camelize(prop)] = val;
}
}
each(this, function (element) {
for (prop in styleProps) {
if (styleProps[prop] || styleProps[prop] === 0) {
element.style[prop] = styleProps[prop];
} else {
element.style.removeProperty(dasherize(prop));
}
}
});
return this;
};
var css$1 = /*#__PURE__*/Object.freeze({
css: css
});
/**
* @module DOM
*/
var forEach$1 = Array.prototype.forEach;
/**
* Append element(s) to each element in the collection.
*
* @param {String|Node|NodeList|Object} element What to append to the element(s).
* Clones elements as necessary.
* @return {Object} The wrapped collection
* @chainable
* @example
* $('.item').append('<p>more</p>');
*/
var append = function append(element) {
if (this instanceof Node) {
if (typeof element === 'string') {
this.insertAdjacentHTML('beforeend', element);
} else {
if (element instanceof Node) {
this.appendChild(element);
} else {
var elements = element instanceof NodeList ? toArray(element) : element;
forEach$1.call(elements, this.appendChild.bind(this));
}
}
} else {
_each(this, append, element);
}
return this;
};
/**
* Place element(s) at the beginning of each element in the collection.
*
* @param {String|Node|NodeList|Object} element What to place at the beginning of the element(s).
* Clones elements as necessary.
* @return {Object} The wrapped collection
* @chainable
* @example
* $('.item').prepend('<span>start</span>');
*/
var prepend = function prepend(element) {
if (this instanceof Node) {
if (typeof element === 'string') {
this.insertAdjacentHTML('afterbegin', element);
} else {
if (element instanceof Node) {
this.insertBefore(element, this.firstChild);
} else {
var elements = element instanceof NodeList ? toArray(element) : element;
forEach$1.call(elements.reverse(), prepend.bind(this));
}
}
} else {
_each(this, prepend, element);
}
return this;
};
/**
* Place element(s) before each element in the collection.
*
* @param {String|Node|NodeList|Object} element What to place as sibling(s) before to the element(s).
* Clones elements as necessary.
* @return {Object} The wrapped collection
* @chainable
* @example
* $('.items').before('<p>prefix</p>');
*/
var before = function before(element) {
if (this instanceof Node) {
if (typeof element === 'string') {
this.insertAdjacentHTML('beforebegin', element);
} else {
if (element instanceof Node) {
this.parentNode.insertBefore(element, this);
} else {
var elements = element instanceof NodeList ? toArray(element) : element;
forEach$1.call(elements, before.bind(this));
}
}
} else {
_each(this, before, element);
}
return this;
};
/**
* Place element(s) after each element in the collection.
*
* @param {String|Node|NodeList|Object} element What to place as sibling(s) after to the element(s). Clones elements as necessary.
* @return {Object} The wrapped collection
* @chainable
* @example
* $('.items').after('<span>suf</span><span>fix</span>');
*/
var after = function after(element) {
if (this instanceof Node) {
if (typeof element === 'string') {
this.insertAdjacentHTML('afterend', element);
} else {
if (element instanceof Node) {
this.parentNode.insertBefore(element, this.nextSibling);
} else {
var elements = element instanceof NodeList ? toArray(element) : element;
forEach$1.call(elements.reverse(), after.bind(this));
}
}
} else {
_each(this, after, element);
}
return this;
};
/**
* Clone a wrapped object.
*
* @return {Object} Wrapped collection of cloned nodes.
* @example
* $(element).clone();
*/
var clone = function clone() {
return $(_clone(this));
};
/**
* Clone an object
*
* @param {String|Node|NodeList|Array} element The element(s) to clone.
* @return {String|Node|NodeList|Array} The cloned element(s)
* @private
*/
var _clone = function _clone(element) {
if (typeof element === 'string') {
return element;
} else if (element instanceof Node) {
return element.cloneNode(true);
} else if ('length' in element) {
return [].map.call(element, function (el) {
return el.cloneNode(true);
});
}
return element;
};
/**
* Specialized iteration, applying `fn` in reversed manner to a clone of each element, but the provided one.
*
* @param {NodeList|Array} collection
* @param {Function} fn
* @param {Node} element
* @private
*/
var _each = function _each(collection, fn, element) {
var l = collection.length;
while (l--) {
var elm = l === 0 ? element : _clone(element);
fn.call(collection[l], elm);
}
};
var dom = /*#__PURE__*/Object.freeze({
append: append,
prepend: prepend,
before: before,
after: after,
clone: clone,
_clone: _clone,
_each: _each
});
/**
* @module Attr
*/
/**
* Get the value of an attribute for the first element, or set one or more attributes for each element in the collection.
*
* @param {String|Object} key The name of the attribute to get or set. Or an object containing key-value pairs to set as attributes.
* @param {String} [value] The value of the attribute to set.
* @return {Object} The wrapped collection
* @chainable
* @example
* $('.item').attr('attrName'); // get
* $('.item').attr('attrName', 'attrValue'); // set
* $('.item').attr({attr1: 'value1', 'attr-2': 'value2'}); // set multiple
*/
var attr = function attr(key, value) {
if (typeof key === 'string' && typeof value === 'undefined') {
var element = this.nodeType ? this : this[0];
return element ? element.getAttribute(key) : undefined;
}
return each(this, function (element) {
if (typeof key === 'object') {
for (var _attr in key) {
element.setAttribute(_attr, key[_attr]);
}
} else {
element.setAttribute(key, value);
}
});
};
/**
* Remove attribute from each element in the collection.
*
* @param {String} key Attribute name
* @return {Object} The wrapped collection
* @chainable
* @example
* $('.items').removeAttr('attrName');
*/
var removeAttr = function removeAttr(key) {
return each(this, function (element) {
return element.removeAttribute(key);
});
};
var dom_attr = /*#__PURE__*/Object.freeze({
attr: attr,
removeAttr: removeAttr
});
/**
* @module Class
*/
/**
* Add a class to the element(s)
*
* @param {String} value Space-separated class name(s) to add to the element(s).
* @return {Object} The wrapped collection
* @chainable
* @example
* $('.item').addClass('bar');
* $('.item').addClass('bar foo');
*/
var addClass = function addClass(value) {
if (value && value.length) {
each(value.split(' '), _each$1.bind(this, 'add'));
}
return this;
};
/**
* Remove a class from the element(s)
*
* @param {String} value Space-separated class name(s) to remove from the element(s).
* @return {Object} The wrapped collection
* @chainable
* @example
* $('.items').removeClass('bar');
* $('.items').removeClass('bar foo');
*/
var removeClass = function removeClass(value) {
if (value && value.length) {
each(value.split(' '), _each$1.bind(this, 'remove'));
}
return this;
};
/**
* Toggle a class at the element(s)
*
* @param {String} value Space-separated class name(s) to toggle at the element(s).
* @param {Boolean} [state] A Boolean value to determine whether the class should be added or removed.
* @return {Object} The wrapped collection
* @chainable
* @example
* $('.item').toggleClass('bar');
* $('.item').toggleClass('bar foo');
* $('.item').toggleClass('bar', true);
*/
var toggleClass = function toggleClass(value, state) {
if (value && value.length) {
var action = typeof state === 'boolean' ? state ? 'add' : 'remove' : 'toggle';
each(value.split(' '), _each$1.bind(this, action));
}
return this;
};
/**
* Check if the element(s) have a class.
*
* @param {String} value Check if the DOM element contains the class name. When applied to multiple elements,
* returns `true` if _any_ of them contains the class name.
* @return {Boolean} Whether the element's class attribute contains the class name.
* @example
* $('.item').hasClass('bar');
*/
var hasClass = function hasClass(value) {
return (this.nodeType ? [this] : this).some(function (element) {
return element.classList.contains(value);
});
};
/**
* Specialized iteration, applying `fn` of the classList API to each element.
*
* @param {String} fnName
* @param {String} className
* @private
*/
var _each$1 = function _each(fnName, className) {
return each(this, function (element) {
return element.classList[fnName](className);
});
};
var dom_class = /*#__PURE__*/Object.freeze({
addClass: addClass,
removeClass: removeClass,
toggleClass: toggleClass,
hasClass: hasClass
});
/**
* @module contains
*/
/**
* Test whether an element contains another element in the DOM.
*
* @param {Element} container The element that may contain the other element.
* @param {Element} element The element that may be a descendant of the other element.
* @return {Boolean} Whether the `container` element contains the `element`.
* @example
* $.contains(parentElement, childElement); // true/false
*/
var contains = function contains(container, element) {
if (!container || !element || container === element) {
return false;
} else if (container.contains) {
return container.contains(element);
} else if (container.compareDocumentPosition) {
return !(container.compareDocumentPosition(element) & Node.DOCUMENT_POSITION_DISCONNECTED);
}
return false;
};
var dom_contains = /*#__PURE__*/Object.freeze({
contains: contains
});
/**
* @module Data
*/
var isSupportsDataSet = typeof document !== 'undefined' && 'dataset' in document.documentElement;
var DATAKEYPROP = isSupportsDataSet ? 'dataset' : '__DOMTASTIC_DATA__';
var camelize$1 = function camelize(str) {
return str.replace(/-+(.)?/g, function (match, char) {
return char ? char.toUpperCase() : '';
});
};
/**
* Get data from first element, or set data for each element in the collection.
*
* @param {String} key The key for the data to get or set.
* @param {String} [value] The data to set.
* @return {Object} The wrapped collection
* @chainable
* @example
* $('.item').data('attrName'); // get
* $('.item').data('attrName', {any: 'data'}); // set
*/
var data = function data(key, value) {
if (typeof key === 'string' && typeof value === 'undefined') {
var element = this.nodeType ? this : this[0];
return element && DATAKEYPROP in element ? element[DATAKEYPROP][camelize$1(key)] : undefined;
}
return each(this, function (element) {
if (!isSupportsDataSet) {
element[DATAKEYPROP] = element[DATAKEYPROP] || {};
}
element[DATAKEYPROP][camelize$1(key)] = value;
});
};
/**
* Get property from first element, or set property on each element in the collection.
*
* @param {String} key The name of the property to get or set.
* @param {String} [value] The value of the property to set.
* @return {Object} The wrapped collection
* @chainable
* @example
* $('.item').prop('attrName'); // get
* $('.item').prop('attrName', 'attrValue'); // set
*/
var prop = function prop(key, value) {
if (typeof key === 'string' && typeof value === 'undefined') {
var element = this.nodeType ? this : this[0];
return element && element ? element[key] : undefined;
}
return each(this, function (element) {
return element[key] = value;
});
};
var dom_data = /*#__PURE__*/Object.freeze({
data: data,
prop: prop
});
/**
* @module DOM (extra)
*/
/**
* Append each element in the collection to the specified element(s).
*
* @param {Node|NodeList|Object} element What to append the element(s) to. Clones elements as necessary.
* @return {Object} The wrapped collection
* @chainable
* @example
* $('.item').appendTo(container);
*/
var appendTo = function appendTo(element) {
var context = typeof element === 'string' ? $(element) : element;
append.call(context, this);
return this;
};
/*
* Empty each element in the collection.
*
* @return {Object} The wrapped collection
* @chainable
* @example
* $('.item').empty();
*/
var empty = function empty() {
return each(this, function (element) {
return element.innerHTML = '';
});
};
/**
* Remove the collection from the DOM.
*
* @return {Array} Array containing the removed elements
* @example
* $('.item').remove();
*/
var remove = function remove() {
return each(this, function (element) {
if (element.parentNode) {
element.parentNode.removeChild(element);
}
});
};
/**
* Replace each element in the collection with the provided new content, and return the array of elements that were replaced.
*
* @return {Array} Array containing the replaced elements
*/
var replaceWith = function replaceWith() {
return before.apply(this, arguments).remove();
};
/**
* Get the `textContent` from the first, or set the `textContent` of each element in the collection.
*
* @param {String} [value]
* @return {Object} The wrapped collection
* @chainable
* @example
* $('.item').text('New content');
*/
var text = function text(value) {
if (value === undefined) {
return this[0].textContent;
}
return each(this, function (element) {
return element.textContent = '' + value;
});
};
/**
* Get the `value` from the first, or set the `value` of each element in the collection.
*
* @param {String} [value]
* @return {Object} The wrapped collection
* @chainable
* @example
* $('input.firstName').val('New value');
*/
var val = function val(value) {
if (value === undefined) {
return this.length > 0 ? this[0].value : undefined;
}
return each(this, function (element) {
return element.value = value;
});
};
var dom_extra = /*#__PURE__*/Object.freeze({
appendTo: appendTo,
empty: empty,
remove: remove,
replaceWith: replaceWith,
text: text,
val: val
});
/**
* @module HTML
*/
/*
* Get the HTML contents of the first element, or set the HTML contents for each element in the collection.
*
* @param {String} [fragment] HTML fragment to set for the element. If this argument is omitted, the HTML contents are returned.
* @return {Object} The wrapped collection
* @chainable
* @example
* $('.item').html();
* $('.item').html('<span>more</span>');
*/
var html = function html(fragment) {
if (fragment === undefined) {
var element = this.nodeType ? this : this[0];
return element ? element.innerHTML : undefined;
}
return each(this, function (element) {
return element.innerHTML = fragment;
});
};
var dom_html = /*#__PURE__*/Object.freeze({
html: html
});
/**
* @module closest
*/
/**
* Return the closest element matching the selector (starting by itself) for each element in the collection.
*
* @param {String} selector Filter
* @param {Object} [context] If provided, matching elements must be a descendant of this element
* @return {Object} New wrapped collection (containing zero or one element)
* @chainable
* @example
* $('.selector').closest('.container');
*/
var closest = function () {
var closest = function closest(selector, context) {
var nodes = [];
each(this, function (node) {
while (node && node !== context) {
if (matches(node, selector)) {
nodes.push(node);
break;
}
node = node.parentElement;
}
});
return $(uniq(nodes));
};
return typeof Element === 'undefined' || !Element.prototype.closest ? closest : function (selector, context) {
if (!context) {
var nodes = [];
each(this, function (node) {
var n = node.closest(selector);
if (n) {
nodes.push(n);
}
});
return $(uniq(nodes));
} else {
return closest.call(this, selector, context);
}
};
}();
var selector_closest = /*#__PURE__*/Object.freeze({
closest: closest
});
/**
* @module Events
*/
/**
* Shorthand for `addEventListener`. Supports event delegation if a filter (`selector`) is provided.
*
* @param {String} eventNames List of space-separated event types to be added to the element(s)
* @param {String} [selector] Selector to filter descendants that delegate the event to this element.
* @param {Function} handler Event handler
* @param {Boolean} useCapture=false
* @param {Boolean} once=false
* @return {Object} The wrapped collection
* @chainable
* @example
* $('.item').on('click', callback);
* $('.container').on('click focus', '.item', handler);
*/
var on = function on(eventNames, selector, handler, useCapture, once) {
var _this = this;
if (typeof selector === 'function') {
handler = selector;
selector = null;
}
var parts, namespace, eventListener;
eventNames.split(' ').forEach(function (eventName) {
parts = eventName.split('.');
eventName = parts[0] || null;
namespace = parts[1] || null;
eventListener = proxyHandler(handler);
each(_this, function (element) {
if (selector) {
eventListener = delegateHandler.bind(element, selector, eventListener);
}
if (once) {
var listener = eventListener;
eventListener = function eventListener(event) {
off.call(element, eventNames, selector, handler, useCapture);
listener.call(element, event);
};
}
element.addEventListener(eventName, eventListener, useCapture || false);
getHandlers(element).push({
eventName: eventName,
handler: handler,
eventListener: eventListener,
selector: selector,
namespace: namespace
});
});
}, this);
return this;
};
/**
* Shorthand for `removeEventListener`.
*
* @param {String} eventNames List of space-separated event types to be removed from the element(s)
* @param {String} [selector] Selector to filter descendants that undelegate the event to this element.
* @param {Function} handler Event handler
* @param {Boolean} useCapture=false
* @return {Object} The wrapped collection
* @chainable
* @example
* $('.item').off('click', callback);
* $('#my-element').off('myEvent myOtherEvent');
* $('.item').off();
*/
var off = function off(eventNames, selector, handler, useCapture) {
var _this2 = this;
if (eventNames === void 0) {
eventNames = '';
}
if (typeof selector === 'function') {
handler = selector;
selector = null;
}
var parts, namespace, handlers;
eventNames.split(' ').forEach(function (eventName) {
parts = eventName.split('.');
eventName = parts[0] || null;
namespace = parts[1] || null;
return each(_this2, function (element) {
handlers = getHandlers(element);
each(handlers.filter(function (item) {
return (!eventName || item.eventName === eventName) && (!namespace || item.namespace === namespace) && (!handler || item.handler === handler) && (!selector || item.selector === selector);
}), function (item) {
element.removeEventListener(item.eventName, item.eventListener, useCapture || false);
handlers.splice(handlers.indexOf(item), 1);
});
if (!eventName && !namespace && !selector && !handler) {
clearHandlers(element);
} else if (handlers.length === 0) {
clearHandlers(element);
}
});
}, this);
return this;
};
/**
* Add event listener and execute the handler at most once per element.
*
* @param eventNames
* @param selector
* @param handler
* @param useCapture
* @return {Object} The wrapped collection
* @chainable
* @example
* $('.item').one('click', callback);
*/
var one = function one(eventNames, selector, handler, useCapture) {
return on.call(this, eventNames, selector, handler, useCapture, 1);
};
/**
* Get event handlers from an element
*
* @private
* @param {Node} element
* @return {Array}
*/
var eventKeyProp = '__domtastic_event__';
var id = 1;
var handlers = {};
var unusedKeys = [];
var getHandlers = function getHandlers(element) {
if (!element[eventKeyProp]) {
element[eventKeyProp] = unusedKeys.length === 0 ? ++id : unusedKeys.pop();
}
var key = element[eventKeyProp];
return handlers[key] || (handlers[key] = []);
};
/**
* Clear event handlers for an element
*
* @private
* @param {Node} element
*/
var clearHandlers = function clearHandlers(element) {
var key = element[eventKeyProp];
if (handlers[key]) {
handlers[key] = null;
element[eventKeyProp] = null;
unusedKeys.push(key);
}
};
/**
* Function to create a handler that augments the event object with some extra methods,
* and executes the callback with the event and the event data (i.e. `event.detail`).
*
* @private
* @param handler Callback to execute as `handler(event, data)`
* @return {Function}
*/
var proxyHandler = function proxyHandler(handler) {
return function (event) {
return handler.call(this, augmentEvent(event));
};
};
var eventMethods = {
preventDefault: 'isDefaultPrevented',
stopImmediatePropagation: 'isImmediatePropagationStopped',
stopPropagation: 'isPropagationStopped'
};
var returnTrue = function returnTrue() {
return true;
};
var returnFalse = function returnFalse() {
return false;
};
/**
* Attempt to augment events and implement something closer to DOM Level 3 Events.
*
* @private
* @param {Object} event
* @return {Function}
*/
var augmentEvent = function augmentEvent(event) {
if (!event.isDefaultPrevented || event.stopImmediatePropagation || event.stopPropagation) {
for (var methodName in eventMethods) {
(function (methodName, testMethodName, originalMethod) {
event[methodName] = function () {
this[testMethodName] = returnTrue;
return originalMethod && originalMethod.apply(this, arguments);
};
event[testMethodName] = returnFalse;
})(methodName, eventMethods[methodName], event[methodName]);
}
if (event._preventDefault) {
event.preventDefault();
}
}
return event;
};
/**
* Function to test whether delegated events match the provided `selector` (filter),
* if the event propagation was stopped, and then actually call the provided event handler.
* Use `this` instead of `event.currentTarget` on the event object.
*
* @private
* @param {String} selector Selector to filter descendants that undelegate the event to this element.
* @param {Function} handler Event handler
* @param {Event} event
*/
var delegateHandler = function delegateHandler(selector, handler, event) {
var eventTarget = event._target || event.target;
var currentTarget = closest.call([eventTarget], selector, this)[0];
if (currentTarget && currentTarget !== this) {
if (currentTarget === eventTarget || !(event.isPropagationStopped && event.isPropagationStopped())) {
handler.call(currentTarget, event);
}
}
};
var bind = on;
var unbind = off;
var event = /*#__PURE__*/Object.freeze({
on: on,
off: off,
one: one,
getHandlers: getHandlers,
clearHandlers: clearHandlers,
proxyHandler: proxyHandler,
delegateHandler: delegateHandler,
bind: bind,
unbind: unbind
});
/**
* @module trigger
*/
var reMouseEvent = /^(mouse(down|up|over|out|enter|leave|move)|contextmenu|(dbl)?click)$/;
var reKeyEvent = /^key(down|press|up)$/;
/**
* Trigger event at element(s)
*
* @param {String} type Type of the event
* @param {Object} data Data to be sent with the event (`params.detail` will be set to this).
* @param {Object} [params] Event parameters (optional)
* @param {Boolean} params.bubbles=true Does the event bubble up through the DOM or not.
* @param {Boolean} params.cancelable=true Is the event cancelable or not.
* @param {Mixed} params.detail=undefined Additional information about the event.
* @return {Object} The wrapped collection
* @chainable
* @example
* $('.item').trigger('anyEventType');
*/
var trigger = function trigger(type, data, _temp) {
var _ref = _temp === void 0 ? {} : _temp,
_ref$bubbles = _ref.bubbles,
bubbles = _ref$bubbles === void 0 ? true : _ref$bubbles,
_ref$cancelable = _ref.cancelable,
cancelable = _ref$cancelable === void 0 ? true : _ref$cancelable,
_ref$preventDefault = _ref.preventDefault,
preventDefault = _ref$preventDefault === void 0 ? false : _ref$preventDefault;
var EventConstructor = getEventConstructor(type);
var event = new EventConstructor(type, {
bubbles: bubbles,
cancelable: cancelable,
preventDefault: preventDefault,
detail: data
});
event._preventDefault = preventDefault;
return each(this, function (element) {
if (!bubbles || isEventBubblingInDetachedTree || isAttachedToDocument(element)) {
dispatchEvent(element, event);
} else {
triggerForPath(element, type, {
bubbles: bubbles,
cancelable: cancelable,
preventDefault: preventDefault,
detail: data
});
}
});
};
var getEventConstructor = function getEventConstructor(type) {
return isSupportsOtherEventConstructors ? reMouseEvent.test(type) ? MouseEvent : reKeyEvent.test(type) ? KeyboardEvent : CustomEvent : CustomEvent;
};
/**
* Trigger event at first element in the collection. Similar to `trigger()`, except:
*
* - Event does not bubble
* - Default event behavior is prevented
* - Only triggers handler for first matching element
*
* @param {String} type Type of the event
* @param {Object} data Data to be sent with the event
* @example
* $('form').triggerHandler('submit');
*/
var triggerHandler = function triggerHandler(type, data) {
if (this[0]) {
trigger.call(this[0], type, data, {
bubbles: false,
preventDefault: true
});
}
};
/**
* Check whether the element is attached to or detached from) the document
*
* @private
* @param {Node} element Element to test
* @return {Boolean}
*/
var isAttachedToDocument = function isAttachedToDocument(element) {
if (element === window || element === document) {
return true;
}
return contains(element.ownerDocument.documentElement, element);
};
/**
* Dispatch the event at the element and its ancestors.
* Required to support delegated events in browsers that don't bubble events in detached DOM trees.
*
* @private
* @param {Node} element First element to dispatch the event at
* @param {String} type Type of the event
* @param {Object} [params] Event parameters (optional)
* @param {Boolean} params.bubbles=true Does the event bubble up through the DOM or not.
* Will be set to false (but shouldn't matter since events don't bubble anyway).
* @param {Boolean} params.cancelable=true Is the event cancelable or not.
* @param {Mixed} params.detail=undefined Additional information about the event.
*/
var triggerForPath = function triggerForPath(element, type, params) {
if (params === void 0) {
params = {};
}
params.bubbles = false;
var event = new CustomEvent(type, params);
event._target = element;
do {
dispatchEvent(element, event);
} while (element = element.parentNode); // eslint-disable-line no-cond-assign
};
/**
* Dispatch event to element, but call direct event methods instead if available
* (e.g. "blur()", "submit()") and if the event is non-cancelable.
*
* @private
* @param {Node} element Element to dispatch the event at
* @param {Object} event Event to dispatch
*/
var directEventMethods = ['blur', 'focus', 'select', 'submit'];
var dispatchEvent = function dispatchEvent(element, event) {
if (directEventMethods.indexOf(event.type) !== -1 && typeof element[event.type] === 'function' && !event._preventDefault && !event.cancelable) {
element[event.type]();
} else {
element.dispatchEvent(event);
}
};
/**
* Polyfill for CustomEvent, borrowed from [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill).
* Needed to support IE (9, 10, 11) & PhantomJS
*/
(function () {
var CustomEvent = function CustomEvent(event, params) {
if (params === void 0) {
params = {
bubbles: false,
cancelable: false,
detail: undefined
};
}
var customEvent = document.createEvent('CustomEvent');
customEvent.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
return customEvent;
};
CustomEvent.prototype = win.CustomEvent && win.CustomEvent.prototype;
win.CustomEvent = CustomEvent;
})();
/*
* Are events bubbling in detached DOM trees?
* @private
*/
var isEventBubblingInDetachedTree = function () {
var isBubbling = false;
var doc = win.document;
if (doc) {
var parent = doc.createElement('div');
var child = parent.cloneNode();
parent.appendChild(child);
parent.addEventListener('e', function () {
isBubbling = true;
});
child.dispatchEvent(new CustomEvent('e', {
bubbles: true
}));
}
return isBubbling;
}();
var isSupportsOtherEventConstructors = function () {
try {
new MouseEvent('click');
} catch (e) {
return false;
}
return true;
}();
var event_trigger = /*#__PURE__*/Object.freeze({
trigger: trigger,
triggerHandler: triggerHandler
});
/**
* @module Ready
*/
/**
* Execute callback when `DOMContentLoaded` fires for `document`, or immediately if called afterwards.
*
* @param handler Callback to execute when initial DOM content is loaded.
* @return {Object} The wrapped collection
* @chainable
* @example
* $(document).ready(callback);
*/
var ready = function ready(handler) {
if (/complete|loaded|interactive/.test(document.readyState) && document.body) {
handler();
} else {
document.addEventListener('DOMContentLoaded', handler, false);
}
return this;
};
var event_ready = /*#__PURE__*/Object.freeze({
ready: ready
});
/**
* @module noConflict
*/
/*
* Save the previous value of the global `$` variable, so that it can be restored later on.
* @private
*/
var previousLib = win.$;
/**
* In case another library sets the global `$` variable before DOMtastic does,
* this method can be used to return the global `$` to that other library.
*
* @return {Object} Reference to DOMtastic.
* @example
* var domtastic = $.noConflict();
*/
var noConflict = function noConflict() {
win.$ = previousLib;
return this;
};
var noconflict = /*#__PURE__*/Object.freeze({
noConflict: noConflict
});
/**
* @module Selector (extra)
*/
/**
* Return children of each element in the collection, optionally filtered by a selector.
*
* @param {String} [selector] Filter
* @return {Object} New wrapped collection
* @chainable
* @ex