Skip to content
Browse files

Extracting registry logic into separate more generic module.

  • Loading branch information...
1 parent c00739c commit 3138ec0cc3d8d341e7bbe81fd8c1171e700ae93c @Gozala committed Aug 10, 2011
View
27 packages/api-utils/lib/addon/notifications.js
@@ -40,31 +40,14 @@
// TODO: Tweak linker and loader to use following instead:
// require('env!api-utils/chrome/notifications')
const channel = require('api-utils/env!')('api-utils/chrome/notifications');
+const { distribute } = require('../stream-utils');
+const { compose } = require("../functional");
-const registry = new function registry() {
- let GUID = 0;
- let map = {};
-
- this.push = function push(data) {
- map[++GUID] = data
- return GUID
- }
-
- this.pop = function pop(index) {
- let data = map[index]
- delete map[index]
- return data
- }
-};
-
-channel.input(function(id) {
- console.log('notification was clicked: ' + id)
- let listener = registry.pop(id)
- if (listener) listener.call(listener.data);
-});
+const listen = distributed(channel.input);
+const False = Boolean.bind(null, false);
exports.notify = function notify({ data, iconURL, text, title, onClick }) {
- let id = onClick ? registry.push({ data: data, call: onClick }) : null
+ let id = onClick ? listen(compose(False, onClick.bind(null, data))) : null;
channel.output({
id: id,
title: title,
View
11 packages/api-utils/lib/chrome/notifications.js
@@ -41,10 +41,13 @@ const { Cc, Ci, Cr, CC } = require("chrome");
const notify = CC('@mozilla.org/alerts-service;1', 'nsIAlertsService')().
showAlertNotification;
+function observer(output, id, subject, topic) {
+ if (topic === 'alertclickcallback' && id)
+ output({ id: id });
+}
+
exports.initialize = function({ input, output }) {
- input(function({ id, title, text, iconURL, data }) {
- notify(iconURL, title, text, !id, data, function onClick(subject, topic) {
- if (topic === 'alertclickcallback') output(id);
- });
+ input(function({ id, title, text, iconURL }) {
+ notify(iconURL, title, text, !id, null, observer.bind(null, output, id));
});
};
View
75 packages/api-utils/lib/functional.js
@@ -0,0 +1,75 @@
+/* vim:set ts=2 sw=2 sts=2 expandtab */
+/*jshint asi: true undef: true es5: true node: true devel: true
+ forin: true latedef: false supernew: true browser: true */
+/*global define: true port: true */
+!(typeof define === "undefined" ? function ($) { $(require, exports, module); } : define)(function (require, exports, module, undefined) {
+
+"use strict";
+
+/**
+ * Returns curried version of the given function.
+ * @param {Function} target
+ * Function to curry.
+ * @param {Number} [length=target.length||Infinity]
+ * Number of argument to curry. If not provided length of target function
+ * is used by default unless it's 0, in such case length will be infinity
+ * and function curry will stop once currier is executed without arguments.
+ * @examples
+ *
+ * var sum = curry(function(a, b) {
+ * return a + b
+ * })
+ * console.log(sum(2, 2)) // 4
+ * console.log(sum(2)(4)) // 6
+ *
+ * var sum = curry(function() {
+ * return Array.prototype.reduce.call(arguments, function(sum, number) {
+ * return sum + number
+ * }, 0)
+ * })
+ * console.log(sum(2, 2)()) // 4
+ * console.log(sum(2, 4, 5)(-3)(1)()) // 9
+ */
+exports.curry = function curry(target, length) {
+ length = length || target.length || Infinity
+ return function curried() {
+ var self = this, args = [];
+ return (function currier() {
+ // If no argument is being curried (`currier` is called with 0
+ // arguments) and infinite number of arguments is expected we stop
+ // currying. Else if number of expected arguments is already collected
+ // we stop currying. One we stop currying we apply collected arguments
+ // to the target `funciton` and return result. If currying is not
+ // stopped we return `currier` to continue currying.
+ return ((args.length === args.push.apply(args, arguments)
+ && Infinity === length) || args.length >= length) ?
+ target.apply(self, args) : currier
+ }).apply(self, arguments);
+ }
+};
+
+/**
+ * Returns the composition of a list of functions, where each function consumes
+ * the return value of the function that follows. In math terms, composing the
+ * functions `f()`, `g()`, and `h()` produces `f(g(h()))`.
+ * @exmple
+ * var greet = function(name){ return "hi: " + name; };
+ * var exclaim = function(statement){ return statement + "!"; };
+ * var welcome = _.compose(exclaim, greet);
+ * welcome('moe');
+ * //> 'hi: moe!'
+ */
+exports.compose = (function Compose(slice) {
+ return function compose() {
+ var funcs = slice.call(arguments);
+ return function composed() {
+ var args = slice.call(arguments);
+ var i = funcs.length;
+ while (0 <= --i)
+ args = [ funcs[i].apply(this, args) ];
+ return args[0];
+ };
+ };
+})(Array.prototype.slice);
+
+});
View
79 packages/api-utils/lib/stream-utils.js
@@ -0,0 +1,79 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Jetpack.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Irakli Gozalishvili <gozala@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Takes a stream whose elements will be distributed across the elements of
+ * the returned stream. Elements are distributed by the unique ID, which
+ * optionally may be specified as a second argument, otherwise it defaults
+ * to `'id'`.
+ */
+exports.distributed = function distributed(source, attribute) {
+ let id = 0;
+ let nexts = {};
+ let stops = [];
+ attribute = attribute || 'id';
+
+ source(function onElement(element) {
+ // For each element of the stream we find an associated element from the
+ // listeners stream by looking at the `attribute` property.
+ let address = element[attribute];
+ let next = nexts[address];
+ // If associated listeners is in registry we invoke it. If it no longer
+ // wishes to be called (returns `false`), we remove it from the registry.
+ if (next && false === next(element)) {
+ delete nexts[address];
+ delete stops[address];
+ }
+ }, function onStop(reason) {
+ // If source stream is stopped we notify all registered handlers and clean
+ // up registry.
+ Object.keys(stops).forEach(function(address) {
+ stops[address](reason);
+ });
+ stops.splice(0);
+ nexts = {};
+ });
+
+ // Return stream that can be used to register listeners on the distributed
+ // stream.
+ return function stream(next, stop) {
+ let address = ++id;
+ nexts[address] = next;
+ if (stop)
+ stops[address] = stop;
+ return address;
+ };
+};

0 comments on commit 3138ec0

Please sign in to comment.
Something went wrong with that request. Please try again.