diff options
Diffstat (limited to 'toolkit/jetpack/sdk/dom/events.js')
-rw-r--r-- | toolkit/jetpack/sdk/dom/events.js | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/toolkit/jetpack/sdk/dom/events.js b/toolkit/jetpack/sdk/dom/events.js new file mode 100644 index 000000000..502d2350f --- /dev/null +++ b/toolkit/jetpack/sdk/dom/events.js @@ -0,0 +1,192 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "unstable" +}; + +const { Cu } = require("chrome"); +const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm"); + +// Utility function that returns copy of the given `text` with last character +// removed if it is `"s"`. +function singularify(text) { + return text[text.length - 1] === "s" ? text.substr(0, text.length - 1) : text; +} + +// Utility function that takes event type, argument is passed to +// `document.createEvent` and returns name of the initializer method of the +// given event. Please note that there are some event types whose initializer +// methods can't be guessed by this function. For more details see following +// link: https://developer.mozilla.org/En/DOM/Document.createEvent +function getInitializerName(category) { + return "init" + singularify(category); +} + +/** + * Registers an event `listener` on a given `element`, that will be called + * when events of specified `type` is dispatched on the `element`. + * @param {Element} element + * Dom element to register listener on. + * @param {String} type + * A string representing the + * [event type](https://developer.mozilla.org/en/DOM/event.type) to + * listen for. + * @param {Function} listener + * Function that is called whenever an event of the specified `type` + * occurs. + * @param {Boolean} capture + * If true, indicates that the user wishes to initiate capture. After + * initiating capture, all events of the specified type will be dispatched + * to the registered listener before being dispatched to any `EventTarget`s + * beneath it in the DOM tree. Events which are bubbling upward through + * the tree will not trigger a listener designated to use capture. + * See [DOM Level 3 Events](http://www.w3.org/TR/DOM-Level-3-Events/#event-flow) + * for a detailed explanation. + */ +function on(element, type, listener, capture, shimmed = false) { + // `capture` defaults to `false`. + capture = capture || false; + if (shimmed) { + element.addEventListener(type, listener, capture); + } else { + ShimWaiver.getProperty(element, "addEventListener")(type, listener, capture); + } +} +exports.on = on; + +/** + * Registers an event `listener` on a given `element`, that will be called + * only once, next time event of specified `type` is dispatched on the + * `element`. + * @param {Element} element + * Dom element to register listener on. + * @param {String} type + * A string representing the + * [event type](https://developer.mozilla.org/en/DOM/event.type) to + * listen for. + * @param {Function} listener + * Function that is called whenever an event of the specified `type` + * occurs. + * @param {Boolean} capture + * If true, indicates that the user wishes to initiate capture. After + * initiating capture, all events of the specified type will be dispatched + * to the registered listener before being dispatched to any `EventTarget`s + * beneath it in the DOM tree. Events which are bubbling upward through + * the tree will not trigger a listener designated to use capture. + * See [DOM Level 3 Events](http://www.w3.org/TR/DOM-Level-3-Events/#event-flow) + * for a detailed explanation. + */ +function once(element, type, listener, capture, shimmed = false) { + on(element, type, function selfRemovableListener(event) { + removeListener(element, type, selfRemovableListener, capture, shimmed); + listener.apply(this, arguments); + }, capture, shimmed); +} +exports.once = once; + +/** + * Unregisters an event `listener` on a given `element` for the events of the + * specified `type`. + * + * @param {Element} element + * Dom element to unregister listener from. + * @param {String} type + * A string representing the + * [event type](https://developer.mozilla.org/en/DOM/event.type) to + * listen for. + * @param {Function} listener + * Function that is called whenever an event of the specified `type` + * occurs. + * @param {Boolean} capture + * If true, indicates that the user wishes to initiate capture. After + * initiating capture, all events of the specified type will be dispatched + * to the registered listener before being dispatched to any `EventTarget`s + * beneath it in the DOM tree. Events which are bubbling upward through + * the tree will not trigger a listener designated to use capture. + * See [DOM Level 3 Events](http://www.w3.org/TR/DOM-Level-3-Events/#event-flow) + * for a detailed explanation. + */ +function removeListener(element, type, listener, capture, shimmed = false) { + if (shimmed) { + element.removeEventListener(type, listener, capture); + } else { + ShimWaiver.getProperty(element, "removeEventListener")(type, listener, capture); + } +} +exports.removeListener = removeListener; + +/** + * Emits event of the specified `type` and `category` on the given `element`. + * Specified `settings` are used to initialize event before dispatching it. + * @param {Element} element + * Dom element to dispatch event on. + * @param {String} type + * A string representing the + * [event type](https://developer.mozilla.org/en/DOM/event.type). + * @param {Object} options + * Options object containing following properties: + * - `category`: String passed to the `document.createEvent`. Option is + * optional and defaults to "UIEvents". + * - `initializer`: If passed it will be used as name of the method used + * to initialize event. If omitted name will be generated from the + * `category` field by prefixing it with `"init"` and removing last + * character if it matches `"s"`. + * - `settings`: Array of settings that are forwarded to the event + * initializer after firs `type` argument. + * @see https://developer.mozilla.org/En/DOM/Document.createEvent + */ +function emit(element, type, { category, initializer, settings }, shimmed = false) { + category = category || "UIEvents"; + initializer = initializer || getInitializerName(category); + let document = element.ownerDocument; + let event = document.createEvent(category); + event[initializer].apply(event, [type].concat(settings)); + if (shimmed) { + element.dispatchEvent(event); + } else { + ShimWaiver.getProperty(element, "dispatchEvent")(event); + } +}; +exports.emit = emit; + +// Takes DOM `element` and returns promise which is resolved +// when given element is removed from it's parent node. +const removed = element => { + return new Promise(resolve => { + const { MutationObserver } = element.ownerDocument.defaultView; + const observer = new MutationObserver(mutations => { + for (let mutation of mutations) { + for (let node of mutation.removedNodes || []) { + if (node === element) { + observer.disconnect(); + resolve(element); + } + } + } + }); + observer.observe(element.parentNode, {childList: true}); + }); +}; +exports.removed = removed; + +const when = (element, eventName, capture=false, shimmed=false) => new Promise(resolve => { + const listener = event => { + if (shimmed) { + element.removeEventListener(eventName, listener, capture); + } else { + ShimWaiver.getProperty(element, "removeEventListener")(eventName, listener, capture); + } + resolve(event); + }; + + if (shimmed) { + element.addEventListener(eventName, listener, capture); + } else { + ShimWaiver.getProperty(element, "addEventListener")(eventName, listener, capture); + } +}); +exports.when = when; |