125 lines
4.2 KiB
JavaScript
125 lines
4.2 KiB
JavaScript
/**
|
|
* ISC License
|
|
*
|
|
* Copyright (c) 2018, Andrea Giammarchi, @WebReflection
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
class QueryResult extends Array {}
|
|
const {create, defineProperty} = Object;
|
|
const AP = Array.prototype;
|
|
const DOM_CONTENT_LOADED = 'DOMContentLoaded';
|
|
const LOAD = 'load';
|
|
const NO_TRANSPILER_ISSUES = (new QueryResult) instanceof QueryResult;
|
|
const QRP = QueryResult.prototype;
|
|
// fixes methods returning non QueryResult
|
|
/* istanbul ignore if */
|
|
if (!NO_TRANSPILER_ISSUES)
|
|
Object.getOwnPropertyNames(AP).forEach(name => {
|
|
const desc = Object.getOwnPropertyDescriptor(AP, name);
|
|
if (typeof desc.value === 'function') {
|
|
const fn = desc.value;
|
|
desc.value = function () {
|
|
const result = fn.apply(this, arguments);
|
|
return result instanceof Array ? patch(result) : result;
|
|
};
|
|
}
|
|
defineProperty(QRP, name, desc);
|
|
});
|
|
// fixes badly transpiled classes
|
|
const patch = NO_TRANSPILER_ISSUES ?
|
|
qr => qr :
|
|
/* istanbul ignore next */
|
|
qr => {
|
|
const nqr = create(QRP);
|
|
push.apply(nqr, slice(qr));
|
|
return nqr;
|
|
};
|
|
const push = AP.push;
|
|
const search = (list, el) => {
|
|
const nodes = [];
|
|
const length = list.length;
|
|
for (let i = 0; i < length; i++) {
|
|
const css = list[i].trim();
|
|
if (css.slice(-6) === ':first') {
|
|
const node = el.querySelector(css.slice(0, -6));
|
|
if (node) push.call(nodes, node);
|
|
} else
|
|
push.apply(nodes, slice(el.querySelectorAll(css)));
|
|
}
|
|
return new QueryResult(...nodes);
|
|
};
|
|
const slice = NO_TRANSPILER_ISSUES ?
|
|
patch :
|
|
/* istanbul ignore next */
|
|
all => {
|
|
// do not use slice.call(...) due old IE gotcha
|
|
const nodes = [];
|
|
const length = all.length;
|
|
for (let i = 0; i < length; i++)
|
|
nodes[i] = all[i];
|
|
return nodes;
|
|
}
|
|
// use function to avoid usage of Symbol.hasInstance
|
|
// (broken in older browsers anyway)
|
|
const $ = function $(CSS, parent = document) {
|
|
switch (typeof CSS) {
|
|
case 'string': return patch(search(CSS.split(','), parent));
|
|
case 'object':
|
|
// needed to avoid iterator dance (breaks in older IEs)
|
|
const nodes = [];
|
|
const all = ('nodeType' in CSS || 'postMessage' in CSS) ? [CSS] : CSS;
|
|
push.apply(nodes, slice(all));
|
|
return patch(new QueryResult(...nodes));
|
|
case 'function':
|
|
const $parent = $(parent);
|
|
const $window = $(parent.defaultView);
|
|
const handler = {handleEvent(event) {
|
|
$parent.off(DOM_CONTENT_LOADED, handler);
|
|
$window.off(LOAD, handler);
|
|
CSS(event);
|
|
}};
|
|
$parent.on(DOM_CONTENT_LOADED, handler);
|
|
$window.on(LOAD, handler);
|
|
const rs = parent.readyState;
|
|
if (rs == 'complete' || (rs != 'loading' && !parent.documentElement.doScroll))
|
|
setTimeout(() => $parent.dispatch(DOM_CONTENT_LOADED));
|
|
return $;
|
|
}
|
|
};
|
|
$.prototype = QRP;
|
|
$.extend = (key, value) =>
|
|
(defineProperty(QRP, key, {configurable: true, value}), $);
|
|
// dropped usage of for-of to avoid broken iteration dance in older IEs
|
|
$.extend('dispatch', function dispatch(type, init = {}) {
|
|
const event = new CustomEvent(type, init);
|
|
const length = this.length;
|
|
for (let i = 0; i < length; i++)
|
|
this[i].dispatchEvent(event);
|
|
return this;
|
|
})
|
|
.extend('off', function off(type, handler, options = false) {
|
|
const length = this.length;
|
|
for (let i = 0; i < length; i++)
|
|
this[i].removeEventListener(type, handler, options);
|
|
return this;
|
|
})
|
|
.extend('on', function on(type, handler, options = false) {
|
|
const length = this.length;
|
|
for (let i = 0; i < length; i++)
|
|
this[i].addEventListener(type, handler, options);
|
|
return this;
|
|
});
|
|
|
|
export default $;
|