Skip to content

Instantly share code, notes, and snippets.

@abe33
Created February 10, 2017 09:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save abe33/38af1237ceae52ee7661b2289eaf4b19 to your computer and use it in GitHub Desktop.
Save abe33/38af1237ceae52ee7661b2289eaf4b19 to your computer and use it in GitHub Desktop.
Pautay's gist
class Disposable {
constructor (block) {
if (!block) {
throw new Error('A Disposable must be created with a dispose callback')
}
this.block = block
}
dispose () {
if (this.block) {
this.block()
delete this.block
}
}
}
class CompositeDisposable extends Disposable {
constructor (disposables = []) {
super(() => {
for (let i = 0; i < this.disposables.length; i++) {
const disposable = this.disposables[i]
disposable.dispose()
}
})
this.disposables = disposables
}
add (disposable) { this.disposables.push(disposable) }
remove (disposable) {
const index = this.disposables.indexOf(disposable)
if (index !== -1) { this.disposables.splice(index, 1) }
}
}
class DisposableEvent extends Disposable {
constructor (target, event, listener) {
const events = event.split(/\s+/g)
if (typeof target.addEventListener === 'function') {
super(() => events.forEach(e => target.removeEventListener(e, listener)))
events.forEach(e => target.addEventListener(e, listener))
} else if (typeof target.on === 'function') {
super(() => events.forEach(e => target.off(e, listener)))
events.forEach(e => target.on(e, listener))
} else {
throw new Error('The passed-in source must have either a addEventListener or on method')
}
}
}
function addDelegatedEventListener(object, event, selector, callback) {
if (typeof selector === 'function') {
callback = selector;
selector = '*';
}
return new DisposableEvent(object, event, listener);
function listener(e) {
if (e.isPropagationStopped) { return; }
let {target} = e;
decorateEvent(e);
nodeAndParents(target).forEach((node) => {
const matched = node.matches(selector);
if (e.isImmediatePropagationStopped || !matched) { return; }
e.matchedTarget = node;
callback(e);
});
}
function decorateEvent(e) {
const overriddenStop = window.Event.prototype.stopPropagation;
e.stopPropagation = function() {
this.isPropagationStopped = true;
overriddenStop.apply(this, arguments);
};
const overriddenStopImmediate = window.Event.prototype.stopImmediatePropagation;
e.stopImmediatePropagation = function() {
this.isImmediatePropagationStopped = true;
overriddenStopImmediate.apply(this, arguments);
};
}
}
function eachParent(node, block) {
let parent = node.parentNode;
while (parent) {
block(parent);
if (parent.nodeName === 'HTML') { break; }
parent = parent.parentNode;
}
}
function parents(node, selector = '*') {
const parentNodes = [];
eachParent(node, (parent) => {
if (parent.matches && parent.matches(selector)) { parentNodes.push(parent); }
});
return parentNodes;
}
function parent(node, selector = '*') {
return parents(node, selector)[0];
}
function nodeAndParents(node, selector = '*') {
return [node].concat(parents(node, selector));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment