diff options
Diffstat (limited to 'src/js/dom.js')
-rw-r--r-- | src/js/dom.js | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/src/js/dom.js b/src/js/dom.js new file mode 100644 index 0000000..3d2f517 --- /dev/null +++ b/src/js/dom.js @@ -0,0 +1,213 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2014-present Raymond Hill + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://github.com/gorhill/uBlock +*/ + +/* jshint esversion:11 */ + +'use strict'; + +/******************************************************************************/ + +const normalizeTarget = target => { + if ( typeof target === 'string' ) { return Array.from(qsa$(target)); } + if ( target instanceof Element ) { return [ target ]; } + if ( target === null ) { return []; } + if ( Array.isArray(target) ) { return target; } + return Array.from(target); +}; + +const makeEventHandler = (selector, callback) => { + return function(event) { + const dispatcher = event.currentTarget; + if ( + dispatcher instanceof HTMLElement === false || + typeof dispatcher.querySelectorAll !== 'function' + ) { + return; + } + const receiver = event.target; + const ancestor = receiver.closest(selector); + if ( + ancestor === receiver && + ancestor !== dispatcher && + dispatcher.contains(ancestor) + ) { + callback.call(receiver, event); + } + }; +}; + +/******************************************************************************/ + +class dom { + static attr(target, attr, value = undefined) { + for ( const elem of normalizeTarget(target) ) { + if ( value === undefined ) { + return elem.getAttribute(attr); + } + if ( value === null ) { + elem.removeAttribute(attr); + } else { + elem.setAttribute(attr, value); + } + } + } + + static clear(target) { + for ( const elem of normalizeTarget(target) ) { + while ( elem.firstChild !== null ) { + elem.removeChild(elem.firstChild); + } + } + } + + static clone(target) { + const elements = normalizeTarget(target); + if ( elements.length === 0 ) { return null; } + return elements[0].cloneNode(true); + } + + static create(a) { + if ( typeof a === 'string' ) { + return document.createElement(a); + } + } + + static prop(target, prop, value = undefined) { + for ( const elem of normalizeTarget(target) ) { + if ( value === undefined ) { return elem[prop]; } + elem[prop] = value; + } + } + + static text(target, text) { + const targets = normalizeTarget(target); + if ( text === undefined ) { + return targets.length !== 0 ? targets[0].textContent : undefined; + } + for ( const elem of targets ) { + elem.textContent = text; + } + } + + static remove(target) { + for ( const elem of normalizeTarget(target) ) { + elem.remove(); + } + } + + // target, type, callback, [options] + // target, type, subtarget, callback, [options] + + static on(target, type, subtarget, callback, options) { + if ( typeof subtarget === 'function' ) { + options = callback; + callback = subtarget; + subtarget = undefined; + if ( typeof options === 'boolean' ) { + options = { capture: true }; + } + } else { + callback = makeEventHandler(subtarget, callback); + if ( options === undefined || typeof options === 'boolean' ) { + options = { capture: true }; + } else { + options.capture = true; + } + } + const targets = target instanceof Window || target instanceof Document + ? [ target ] + : normalizeTarget(target); + for ( const elem of targets ) { + elem.addEventListener(type, callback, options); + } + } + + static off(target, type, callback, options) { + if ( typeof callback !== 'function' ) { return; } + if ( typeof options === 'boolean' ) { + options = { capture: true }; + } + const targets = target instanceof Window || target instanceof Document + ? [ target ] + : normalizeTarget(target); + for ( const elem of targets ) { + elem.removeEventListener(type, callback, options); + } + } +} + +dom.cl = class { + static add(target, name) { + for ( const elem of normalizeTarget(target) ) { + elem.classList.add(name); + } + } + + static remove(target, name) { + for ( const elem of normalizeTarget(target) ) { + elem.classList.remove(name); + } + } + + static toggle(target, name, state) { + let r; + for ( const elem of normalizeTarget(target) ) { + r = elem.classList.toggle(name, state); + } + return r; + } + + static has(target, name) { + for ( const elem of normalizeTarget(target) ) { + if ( elem.classList.contains(name) ) { + return true; + } + } + return false; + } +}; + +/******************************************************************************/ + +function qs$(a, b) { + if ( typeof a === 'string') { + return document.querySelector(a); + } + if ( a === null ) { return null; } + return a.querySelector(b); +} + +function qsa$(a, b) { + if ( typeof a === 'string') { + return document.querySelectorAll(a); + } + if ( a === null ) { return []; } + return a.querySelectorAll(b); +} + +dom.root = qs$(':root'); +dom.html = document.documentElement; +dom.head = document.head; +dom.body = document.body; + +/******************************************************************************/ + +export { dom, qs$, qsa$ }; |