/** * 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 $;