addon-sdk/source/lib/sdk/event/core.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/addon-sdk/source/lib/sdk/event/core.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,172 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +"use strict";
     1.9 +
    1.10 +module.metadata = {
    1.11 +  "stability": "unstable"
    1.12 +};
    1.13 +
    1.14 +const UNCAUGHT_ERROR = 'An error event was emitted for which there was no listener.';
    1.15 +const BAD_LISTENER = 'The event listener must be a function.';
    1.16 +
    1.17 +const { ns } = require('../core/namespace');
    1.18 +
    1.19 +const event = ns();
    1.20 +
    1.21 +const EVENT_TYPE_PATTERN = /^on([A-Z]\w+$)/;
    1.22 +exports.EVENT_TYPE_PATTERN = EVENT_TYPE_PATTERN;
    1.23 +
    1.24 +// Utility function to access given event `target` object's event listeners for
    1.25 +// the specific event `type`. If listeners for this type does not exists they
    1.26 +// will be created.
    1.27 +const observers = function observers(target, type) {
    1.28 +  if (!target) throw TypeError("Event target must be an object");
    1.29 +  let listeners = event(target);
    1.30 +  return type in listeners ? listeners[type] : listeners[type] = [];
    1.31 +};
    1.32 +
    1.33 +/**
    1.34 + * Registers an event `listener` that is called every time events of
    1.35 + * specified `type` is emitted on the given event `target`.
    1.36 + * @param {Object} target
    1.37 + *    Event target object.
    1.38 + * @param {String} type
    1.39 + *    The type of event.
    1.40 + * @param {Function} listener
    1.41 + *    The listener function that processes the event.
    1.42 + */
    1.43 +function on(target, type, listener) {
    1.44 +  if (typeof(listener) !== 'function')
    1.45 +    throw new Error(BAD_LISTENER);
    1.46 +
    1.47 +  let listeners = observers(target, type);
    1.48 +  if (!~listeners.indexOf(listener))
    1.49 +    listeners.push(listener);
    1.50 +}
    1.51 +exports.on = on;
    1.52 +
    1.53 +/**
    1.54 + * Registers an event `listener` that is called only the next time an event
    1.55 + * of the specified `type` is emitted on the given event `target`.
    1.56 + * @param {Object} target
    1.57 + *    Event target object.
    1.58 + * @param {String} type
    1.59 + *    The type of the event.
    1.60 + * @param {Function} listener
    1.61 + *    The listener function that processes the event.
    1.62 + */
    1.63 +function once(target, type, listener) {
    1.64 +  on(target, type, function observer(...args) {
    1.65 +    off(target, type, observer);
    1.66 +    listener.apply(target, args);
    1.67 +  });
    1.68 +}
    1.69 +exports.once = once;
    1.70 +
    1.71 +/**
    1.72 + * Execute each of the listeners in order with the supplied arguments.
    1.73 + * All the exceptions that are thrown by listeners during the emit
    1.74 + * are caught and can be handled by listeners of 'error' event. Thrown
    1.75 + * exceptions are passed as an argument to an 'error' event listener.
    1.76 + * If no 'error' listener is registered exception will be logged into an
    1.77 + * error console.
    1.78 + * @param {Object} target
    1.79 + *    Event target object.
    1.80 + * @param {String} type
    1.81 + *    The type of event.
    1.82 + * @params {Object|Number|String|Boolean} args
    1.83 + *    Arguments that will be passed to listeners.
    1.84 + */
    1.85 +function emit (target, type, ...args) {
    1.86 +  let state = observers(target, type);
    1.87 +  let listeners = state.slice();
    1.88 +  let count = listeners.length;
    1.89 +  let index = 0;
    1.90 +
    1.91 +  // If error event and there are no handlers then print error message
    1.92 +  // into a console.
    1.93 +  if (count === 0 && type === 'error') console.exception(args[0]);
    1.94 +  while (index < count) {
    1.95 +    try {
    1.96 +      let listener = listeners[index];
    1.97 +      // Dispatch only if listener is still registered.
    1.98 +      if (~state.indexOf(listener))
    1.99 +        listener.apply(target, args);
   1.100 +    }
   1.101 +    catch (error) {
   1.102 +      // If exception is not thrown by a error listener and error listener is
   1.103 +      // registered emit `error` event. Otherwise dump exception to the console.
   1.104 +      if (type !== 'error') emit(target, 'error', error);
   1.105 +      else console.exception(error);
   1.106 +    }
   1.107 +    index++;
   1.108 +  }
   1.109 +   // Also emit on `"*"` so that one could listen for all events.
   1.110 +  if (type !== '*') emit(target, '*', type, ...args);
   1.111 +}
   1.112 +exports.emit = emit;
   1.113 +
   1.114 +/**
   1.115 + * Removes an event `listener` for the given event `type` on the given event
   1.116 + * `target`. If no `listener` is passed removes all listeners of the given
   1.117 + * `type`. If `type` is not passed removes all the listeners of the given
   1.118 + * event `target`.
   1.119 + * @param {Object} target
   1.120 + *    The event target object.
   1.121 + * @param {String} type
   1.122 + *    The type of event.
   1.123 + * @param {Function} listener
   1.124 + *    The listener function that processes the event.
   1.125 + */
   1.126 +function off(target, type, listener) {
   1.127 +  let length = arguments.length;
   1.128 +  if (length === 3) {
   1.129 +    let listeners = observers(target, type);
   1.130 +    let index = listeners.indexOf(listener);
   1.131 +    if (~index)
   1.132 +      listeners.splice(index, 1);
   1.133 +  }
   1.134 +  else if (length === 2) {
   1.135 +    observers(target, type).splice(0);
   1.136 +  }
   1.137 +  else if (length === 1) {
   1.138 +    let listeners = event(target);
   1.139 +    Object.keys(listeners).forEach(type => delete listeners[type]);
   1.140 +  }
   1.141 +}
   1.142 +exports.off = off;
   1.143 +
   1.144 +/**
   1.145 + * Returns a number of event listeners registered for the given event `type`
   1.146 + * on the given event `target`.
   1.147 + */
   1.148 +function count(target, type) {
   1.149 +  return observers(target, type).length;
   1.150 +}
   1.151 +exports.count = count;
   1.152 +
   1.153 +/**
   1.154 + * Registers listeners on the given event `target` from the given `listeners`
   1.155 + * dictionary. Iterates over the listeners and if property name matches name
   1.156 + * pattern `onEventType` and property is a function, then registers it as
   1.157 + * an `eventType` listener on `target`.
   1.158 + *
   1.159 + * @param {Object} target
   1.160 + *    The type of event.
   1.161 + * @param {Object} listeners
   1.162 + *    Dictionary of listeners.
   1.163 + */
   1.164 +function setListeners(target, listeners) {
   1.165 +  Object.keys(listeners || {}).forEach(key => {
   1.166 +    let match = EVENT_TYPE_PATTERN.exec(key);
   1.167 +    let type = match && match[1].toLowerCase();
   1.168 +    if (!type) return;
   1.169 +
   1.170 +    let listener = listeners[key];
   1.171 +    if (typeof(listener) === 'function')
   1.172 +      on(target, type, listener);
   1.173 +  });
   1.174 +}
   1.175 +exports.setListeners = setListeners;

mercurial