Move Add-on SDK source to toolkit/jetpack

This commit is contained in:
NTD 2018-02-09 06:46:43 -05:00 • committed by Roy Tam
commit 23b67db438
993 changed files with 484 additions and 100663 deletions

View file

@ -0,0 +1,123 @@
/* 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": "experimental"
};
/**
* Returns `true` if given `array` contain given `element` or `false`
* otherwise.
* @param {Array} array
* Target array.
* @param {Object|String|Number|Boolean} element
* Element being looked up.
* @returns {Boolean}
*/
var has = exports.has = function has(array, element) {
// shorter and faster equivalent of `array.indexOf(element) >= 0`
return !!~array.indexOf(element);
};
var hasAny = exports.hasAny = function hasAny(array, elements) {
if (arguments.length < 2)
return false;
if (!Array.isArray(elements))
elements = [ elements ];
return array.some(function (element) {
return has(elements, element);
});
};
/**
* Adds given `element` to the given `array` if it does not contain it yet.
* `true` is returned if element was added otherwise `false` is returned.
* @param {Array} array
* Target array.
* @param {Object|String|Number|Boolean} element
* Element to be added.
* @returns {Boolean}
*/
var add = exports.add = function add(array, element) {
var result;
if ((result = !has(array, element)))
array.push(element);
return result;
};
/**
* Removes first occurrence of the given `element` from the given `array`. If
* `array` does not contain given `element` `false` is returned otherwise
* `true` is returned.
* @param {Array} array
* Target array.
* @param {Object|String|Number|Boolean} element
* Element to be removed.
* @returns {Boolean}
*/
exports.remove = function remove(array, element) {
var result;
if ((result = has(array, element)))
array.splice(array.indexOf(element), 1);
return result;
};
/**
* Produces a duplicate-free version of the given `array`.
* @param {Array} array
* Source array.
* @returns {Array}
*/
function unique(array) {
return array.reduce(function(result, item) {
add(result, item);
return result;
}, []);
};
exports.unique = unique;
/**
* Produce an array that contains the union: each distinct element from all
* of the passed-in arrays.
*/
function union() {
return unique(Array.concat.apply(null, arguments));
};
exports.union = union;
exports.flatten = function flatten(array){
var flat = [];
for (var i = 0, l = array.length; i < l; i++) {
flat = flat.concat(Array.isArray(array[i]) ? flatten(array[i]) : array[i]);
}
return flat;
};
function fromIterator(iterator) {
let array = [];
if (iterator.__iterator__) {
for (let item of iterator)
array.push(item);
}
else {
for (let item of iterator)
array.push(item);
}
return array;
}
exports.fromIterator = fromIterator;
function find(array, predicate, fallback) {
var index = 0;
var count = array.length;
while (index < count) {
var value = array[index];
if (predicate(value)) return value;
else index = index + 1;
}
return fallback;
}
exports.find = find;

View file

@ -0,0 +1,115 @@
/* 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": "experimental"
};
exports.Collection = Collection;
/**
* Adds a collection property to the given object. Setting the property to a
* scalar value empties the collection and adds the value. Setting it to an
* array empties the collection and adds all the items in the array.
*
* @param obj
* The property will be defined on this object.
* @param propName
* The name of the property.
* @param array
* If given, this will be used as the collection's backing array.
*/
exports.addCollectionProperty = function addCollProperty(obj, propName, array) {
array = array || [];
let publicIface = new Collection(array);
Object.defineProperty(obj, propName, {
configurable: true,
enumerable: true,
set: function set(itemOrItems) {
array.splice(0, array.length);
publicIface.add(itemOrItems);
},
get: function get() {
return publicIface;
}
});
};
/**
* A collection is ordered, like an array, but its items are unique, like a set.
*
* @param array
* The collection is backed by an array. If this is given, it will be
* used as the backing array. This way the caller can fully control the
* collection. Otherwise a new empty array will be used, and no one but
* the collection will have access to it.
*/
function Collection(array) {
array = array || [];
/**
* Provides iteration over the collection. Items are yielded in the order
* they were added.
*/
this.__iterator__ = function Collection___iterator__() {
let items = array.slice();
for (let i = 0; i < items.length; i++)
yield items[i];
};
/**
* The number of items in the collection.
*/
this.__defineGetter__("length", function Collection_get_length() {
return array.length;
});
/**
* Adds a single item or an array of items to the collection. Any items
* already contained in the collection are ignored.
*
* @param itemOrItems
* An item or array of items.
* @return The collection.
*/
this.add = function Collection_add(itemOrItems) {
let items = toArray(itemOrItems);
for (let i = 0; i < items.length; i++) {
let item = items[i];
if (array.indexOf(item) < 0)
array.push(item);
}
return this;
};
/**
* Removes a single item or an array of items from the collection. Any items
* not contained in the collection are ignored.
*
* @param itemOrItems
* An item or array of items.
* @return The collection.
*/
this.remove = function Collection_remove(itemOrItems) {
let items = toArray(itemOrItems);
for (let i = 0; i < items.length; i++) {
let idx = array.indexOf(items[i]);
if (idx >= 0)
array.splice(idx, 1);
}
return this;
};
};
function toArray(itemOrItems) {
let isArr = itemOrItems &&
itemOrItems.constructor &&
itemOrItems.constructor.name === "Array";
return isArr ? itemOrItems : [itemOrItems];
}

View file

@ -0,0 +1,55 @@
/* 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 { validateOptions: valid } = require("../deprecated/api-utils");
const method = require("method/core");
// Function takes property validation rules and returns function that given
// an `options` object will return validated / normalized options back. If
// option(s) are invalid validator will throw exception described by rules.
// Returned will also have contain `rules` property with a given validation
// rules and `properties` function that can be used to generate validated
// property getter and setters can be mixed into prototype. For more details
// see `properties` function below.
function contract(rules) {
const validator = (instance, options) => {
return valid(options || instance || {}, rules);
};
validator.rules = rules
validator.properties = function(modelFor) {
return properties(modelFor, rules);
}
return validator;
}
exports.contract = contract
// Function takes `modelFor` instance state model accessor functions and
// a property validation rules and generates object with getters and setters
// that can be mixed into prototype. Property accessors update model for the
// given instance. If you wish to react to property updates you can always
// override setters to put specific logic.
function properties(modelFor, rules) {
let descriptor = Object.keys(rules).reduce(function(descriptor, name) {
descriptor[name] = {
get: function() { return modelFor(this)[name] },
set: function(value) {
let change = {};
change[name] = value;
modelFor(this)[name] = valid(change, rules)[name];
}
}
return descriptor
}, {});
return Object.create(Object.prototype, descriptor);
}
exports.properties = properties;
const validate = method("contract/validate");
exports.validate = validate;

View file

@ -0,0 +1,40 @@
/* 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": "experimental"
};
const { get, format } = require("../console/traceback");
const { get: getPref } = require("../preferences/service");
const PREFERENCE = "devtools.errorconsole.deprecation_warnings";
function deprecateUsage(msg) {
// Print caller stacktrace in order to help figuring out which code
// does use deprecated thing
let stack = get().slice(2);
if (getPref(PREFERENCE))
console.error("DEPRECATED: " + msg + "\n" + format(stack));
}
exports.deprecateUsage = deprecateUsage;
function deprecateFunction(fun, msg) {
return function deprecated() {
deprecateUsage(msg);
return fun.apply(this, arguments);
};
}
exports.deprecateFunction = deprecateFunction;
function deprecateEvent(fun, msg, evtTypes) {
return function deprecateEvent(evtType) {
if (evtTypes.indexOf(evtType) >= 0)
deprecateUsage(msg);
return fun.apply(this, arguments);
};
}
exports.deprecateEvent = deprecateEvent;

View file

@ -0,0 +1,54 @@
/* 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": "experimental"
};
const method = require("method/core");
// Utility function that is just an enhancement over `method` to
// allow predicate based dispatch in addition to polymorphic
// dispatch. Unfortunately polymorphic dispatch does not quite
// cuts it in the world of XPCOM where no types / classes exist
// and all the XUL nodes share same type / prototype.
// Probably this is more generic and belongs some place else, but
// we can move it later once this will be relevant.
var dispatcher = hint => {
const base = method(hint);
// Make a map for storing predicate, implementation mappings.
let implementations = new Map();
// Dispatcher function goes through `predicate, implementation`
// pairs to find predicate that matches first argument and
// returns application of arguments on the associated
// `implementation`. If no matching predicate is found delegates
// to a `base` polymorphic function.
let dispatch = (value, ...rest) => {
for (let [predicate, implementation] of implementations) {
if (predicate(value))
return implementation(value, ...rest);
}
return base(value, ...rest);
};
// Expose base API.
dispatch.define = base.define;
dispatch.implement = base.implement;
dispatch.toString = base.toString;
// Add a `when` function to allow extending function via
// predicates.
dispatch.when = (predicate, implementation) => {
if (implementations.has(predicate))
throw TypeError("Already implemented for the given predicate");
implementations.set(predicate, implementation);
};
return dispatch;
};
exports.dispatcher = dispatcher;

View file

@ -0,0 +1,90 @@
/* 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": "experimental"
};
const { Class } = require('../core/heritage');
const listNS = require('../core/namespace').ns();
const listOptions = {
/**
* List constructor can take any number of element to populate itself.
* @params {Object|String|Number} element
* @example
* List(1,2,3).length == 3 // true
*/
initialize: function List() {
listNS(this).keyValueMap = [];
for (let i = 0, ii = arguments.length; i < ii; i++)
addListItem(this, arguments[i]);
},
/**
* Number of elements in this list.
* @type {Number}
*/
get length() {
return listNS(this).keyValueMap.length;
},
/**
* Returns a string representing this list.
* @returns {String}
*/
toString: function toString() {
return 'List(' + listNS(this).keyValueMap + ')';
},
/**
* Custom iterator providing `List`s enumeration behavior.
* We cant reuse `_iterator` that is defined by `Iterable` since it provides
* iteration in an arbitrary order.
* @see https://developer.mozilla.org/en/JavaScript/Reference/Statements/for...in
* @param {Boolean} onKeys
*/
__iterator__: function __iterator__(onKeys, onKeyValue) {
let array = listNS(this).keyValueMap.slice(0),
i = -1;
for (let element of array)
yield onKeyValue ? [++i, element] : onKeys ? ++i : element;
},
};
listOptions[Symbol.iterator] = function iterator() {
return listNS(this).keyValueMap.slice(0)[Symbol.iterator]();
};
const List = Class(listOptions);
exports.List = List;
function addListItem(that, value) {
let list = listNS(that).keyValueMap,
index = list.indexOf(value);
if (-1 === index) {
try {
that[that.length] = value;
}
catch (e) {}
list.push(value);
}
}
exports.addListItem = addListItem;
function removeListItem(that, element) {
let list = listNS(that).keyValueMap,
index = list.indexOf(element);
if (0 <= index) {
list.splice(index, 1);
try {
for (let length = list.length; index < length; index++)
that[index] = list[index];
that[list.length] = undefined;
}
catch(e){}
}
}
exports.removeListItem = removeListItem;
exports.listNS = listNS;

View file

@ -0,0 +1,113 @@
/* 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 { URL } = require('../url');
const cache = {};
function MatchPattern(pattern) {
if (cache[pattern]) return cache[pattern];
if (typeof pattern.test == "function") {
// For compatibility with -moz-document rules, we require the RegExp's
// global, ignoreCase, and multiline flags to be set to false.
if (pattern.global) {
throw new Error("A RegExp match pattern cannot be set to `global` " +
"(i.e. //g).");
}
if (pattern.multiline) {
throw new Error("A RegExp match pattern cannot be set to `multiline` " +
"(i.e. //m).");
}
this.regexp = pattern;
}
else {
let firstWildcardPosition = pattern.indexOf("*");
let lastWildcardPosition = pattern.lastIndexOf("*");
if (firstWildcardPosition != lastWildcardPosition)
throw new Error("There can be at most one '*' character in a wildcard.");
if (firstWildcardPosition == 0) {
if (pattern.length == 1)
this.anyWebPage = true;
else if (pattern[1] != ".")
throw new Error("Expected a *.<domain name> string, got: " + pattern);
else
this.domain = pattern.substr(2);
}
else {
if (pattern.indexOf(":") == -1) {
throw new Error("When not using *.example.org wildcard, the string " +
"supplied is expected to be either an exact URL to " +
"match or a URL prefix. The provided string ('" +
pattern + "') is unlikely to match any pages.");
}
if (firstWildcardPosition == -1)
this.exactURL = pattern;
else if (firstWildcardPosition == pattern.length - 1)
this.urlPrefix = pattern.substr(0, pattern.length - 1);
else {
throw new Error("The provided wildcard ('" + pattern + "') has a '*' " +
"in an unexpected position. It is expected to be the " +
"first or the last character in the wildcard.");
}
}
}
cache[pattern] = this;
}
MatchPattern.prototype = {
test: function MatchPattern_test(urlStr) {
try {
var url = URL(urlStr);
}
catch (err) {
return false;
}
// Test the URL against a RegExp pattern. For compatibility with
// -moz-document rules, we require the RegExp to match the entire URL,
// so we not only test for a match, we also make sure the matched string
// is the entire URL string.
//
// Assuming most URLs don't match most match patterns, we call `test` for
// speed when determining whether or not the URL matches, then call `exec`
// for the small subset that match to make sure the entire URL matches.
if (this.regexp && this.regexp.test(urlStr) &&
this.regexp.exec(urlStr)[0] == urlStr)
return true;
if (this.anyWebPage && /^(https?|ftp)$/.test(url.scheme))
return true;
if (this.exactURL && this.exactURL == urlStr)
return true;
// Tests the urlStr against domain and check if
// wildcard submitted (*.domain.com), it only allows
// subdomains (sub.domain.com) or from the root (http://domain.com)
// and reject non-matching domains (otherdomain.com)
// bug 856913
if (this.domain && url.host &&
(url.host === this.domain ||
url.host.slice(-this.domain.length - 1) === "." + this.domain))
return true;
if (this.urlPrefix && 0 == urlStr.indexOf(this.urlPrefix))
return true;
return false;
},
toString: () => '[object MatchPattern]'
};
exports.MatchPattern = MatchPattern;

View file

@ -0,0 +1,104 @@
/* 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 { flatten } = require('./array');
/**
* Merges all the properties of all arguments into first argument. If two or
* more argument objects have own properties with the same name, the property
* is overridden, with precedence from right to left, implying, that properties
* of the object on the left are overridden by a same named property of the
* object on the right.
*
* Any argument given with "falsy" value - commonly `null` and `undefined` in
* case of objects - are skipped.
*
* @examples
* var a = { bar: 0, a: 'a' }
* var b = merge(a, { foo: 'foo', bar: 1 }, { foo: 'bar', name: 'b' });
* b === a // true
* b.a // 'a'
* b.foo // 'bar'
* b.bar // 1
* b.name // 'b'
*/
function merge(source) {
let descriptor = {};
// `Boolean` converts the first parameter to a boolean value. Any object is
// converted to `true` where `null` and `undefined` becames `false`. Therefore
// the `filter` method will keep only objects that are defined and not null.
Array.slice(arguments, 1).filter(Boolean).forEach(function onEach(properties) {
getOwnPropertyIdentifiers(properties).forEach(function(name) {
descriptor[name] = Object.getOwnPropertyDescriptor(properties, name);
});
});
return Object.defineProperties(source, descriptor);
}
exports.merge = merge;
/**
* Returns an object that inherits from the first argument and contains all the
* properties from all following arguments.
* `extend(source1, source2, source3)` is equivalent of
* `merge(Object.create(source1), source2, source3)`.
*/
function extend(source) {
let rest = Array.slice(arguments, 1);
rest.unshift(Object.create(source));
return merge.apply(null, rest);
}
exports.extend = extend;
function has(obj, key) {
return obj.hasOwnProperty(key);
}
exports.has = has;
function each(obj, fn) {
for (let key in obj) has(obj, key) && fn(obj[key], key, obj);
}
exports.each = each;
/**
* Like `merge`, except no property descriptors are manipulated, for use
* with platform objects. Identical to underscore's `extend`. Useful for
* merging XPCOM objects
*/
function safeMerge(source) {
Array.slice(arguments, 1).forEach(function onEach (obj) {
for (let prop in obj) source[prop] = obj[prop];
});
return source;
}
exports.safeMerge = safeMerge;
/*
* Returns a copy of the object without omitted properties
*/
function omit(source, ...values) {
let copy = {};
let keys = flatten(values);
for (let prop in source)
if (!~keys.indexOf(prop))
copy[prop] = source[prop];
return copy;
}
exports.omit = omit;
// get object's own property Symbols and/or Names, including nonEnumerables by default
function getOwnPropertyIdentifiers(object, options = { names: true, symbols: true, nonEnumerables: true }) {
const symbols = !options.symbols ? [] :
Object.getOwnPropertySymbols(object);
const names = !options.names ? [] :
options.nonEnumerables ? Object.getOwnPropertyNames(object) :
Object.keys(object);
return [...names, ...symbols];
}
exports.getOwnPropertyIdentifiers = getOwnPropertyIdentifiers;

View file

@ -0,0 +1,53 @@
/* 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 { Class } = require('../core/heritage');
const { MatchPattern } = require('./match-pattern');
const { emit } = require('../event/core');
const { EventTarget } = require('../event/target');
const { List, addListItem, removeListItem } = require('./list');
// Should deprecate usage of EventEmitter/compose
const Rules = Class({
implements: [
EventTarget,
List
],
add: function(...rules) {
return [].concat(rules).forEach(function onAdd(rule) {
addListItem(this, rule);
emit(this, 'add', rule);
}, this);
},
remove: function(...rules) {
return [].concat(rules).forEach(function onRemove(rule) {
removeListItem(this, rule);
emit(this, 'remove', rule);
}, this);
},
get: function(rule) {
let found = false;
for (let i in this) if (this[i] === rule) found = true;
return found;
},
// Returns true if uri matches atleast one stored rule
matchesAny: function(uri) {
return !!filterMatches(this, uri).length;
},
toString: () => '[object Rules]'
});
exports.Rules = Rules;
function filterMatches(instance, uri) {
let matches = [];
for (let i in instance) {
if (new MatchPattern(instance[i]).test(uri)) matches.push(instance[i]);
}
return matches;
}

View file

@ -0,0 +1,593 @@
/* 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": "experimental"
};
// Disclamer:
// In this module we'll have some common argument / variable names
// to hint their type or behavior.
//
// - `f` stands for "function" that is intended to be side effect
// free.
// - `p` stands for "predicate" that is function which returns logical
// true or false and is intended to be side effect free.
// - `x` / `y` single item of the sequence.
// - `xs` / `ys` sequence of `x` / `y` items where `x` / `y` signifies
// type of the items in sequence, so sequence is not of the same item.
// - `_` used for argument(s) or variable(s) who's values are ignored.
const { complement, flip, identity } = require("../lang/functional");
const { isArray, isArguments, isMap, isSet, isGenerator,
isString, isBoolean, isNumber } = require("../lang/type");
const Sequence = function Sequence(iterator) {
if (!isGenerator(iterator)) {
throw TypeError("Expected generator argument");
}
this[Symbol.iterator] = iterator;
};
exports.Sequence = Sequence;
const polymorphic = dispatch => x =>
x === null ? dispatch.null(null) :
x === void(0) ? dispatch.void(void(0)) :
isArray(x) ? (dispatch.array || dispatch.indexed)(x) :
isString(x) ? (dispatch.string || dispatch.indexed)(x) :
isArguments(x) ? (dispatch.arguments || dispatch.indexed)(x) :
isMap(x) ? dispatch.map(x) :
isSet(x) ? dispatch.set(x) :
isNumber(x) ? dispatch.number(x) :
isBoolean(x) ? dispatch.boolean(x) :
dispatch.default(x);
const nogen = function*() {};
const empty = () => new Sequence(nogen);
exports.empty = empty;
const seq = polymorphic({
null: empty,
void: empty,
array: identity,
string: identity,
arguments: identity,
map: identity,
set: identity,
default: x => x instanceof Sequence ? x : new Sequence(x)
});
exports.seq = seq;
// Function to cast seq to string.
const string = (...etc) => "".concat(...etc);
exports.string = string;
// Function for casting seq to plain object.
const object = (...pairs) => {
let result = {};
for (let [key, value] of pairs)
result[key] = value;
return result;
};
exports.object = object;
// Takes `getEnumerator` function that returns `nsISimpleEnumerator`
// and creates lazy sequence of it's items. Note that function does
// not take `nsISimpleEnumerator` itslef because that would allow
// single iteration, which would not be consistent with rest of the
// lazy sequences.
const fromEnumerator = getEnumerator => seq(function* () {
const enumerator = getEnumerator();
while (enumerator.hasMoreElements())
yield enumerator.getNext();
});
exports.fromEnumerator = fromEnumerator;
// Takes `object` and returns lazy sequence of own `[key, value]`
// pairs (does not include inherited and non enumerable keys).
const pairs = polymorphic({
null: empty,
void: empty,
map: identity,
indexed: indexed => seq(function* () {
const count = indexed.length;
let index = 0;
while (index < count) {
yield [index, indexed[index]];
index = index + 1;
}
}),
default: object => seq(function* () {
for (let key of Object.keys(object))
yield [key, object[key]];
})
});
exports.pairs = pairs;
const names = polymorphic({
null: empty,
void: empty,
default: object => seq(function*() {
for (let name of Object.getOwnPropertyNames(object)) {
yield name;
}
})
});
exports.names = names;
const symbols = polymorphic({
null: empty,
void: empty,
default: object => seq(function* () {
for (let symbol of Object.getOwnPropertySymbols(object)) {
yield symbol;
}
})
});
exports.symbols = symbols;
const keys = polymorphic({
null: empty,
void: empty,
indexed: indexed => seq(function* () {
const count = indexed.length;
let index = 0;
while (index < count) {
yield index;
index = index + 1;
}
}),
map: map => seq(function* () {
for (let [key, _] of map)
yield key;
}),
default: object => seq(function* () {
for (let key of Object.keys(object))
yield key;
})
});
exports.keys = keys;
const values = polymorphic({
null: empty,
void: empty,
set: identity,
indexed: indexed => seq(function* () {
const count = indexed.length;
let index = 0;
while (index < count) {
yield indexed[index];
index = index + 1;
}
}),
map: map => seq(function* () {
for (let [_, value] of map) yield value;
}),
default: object => seq(function* () {
for (let key of Object.keys(object)) yield object[key];
})
});
exports.values = values;
// Returns a lazy sequence of `x`, `f(x)`, `f(f(x))` etc.
// `f` must be free of side-effects. Note that returned
// sequence is infinite so it must be consumed partially.
//
// Implements clojure iterate:
// http://clojuredocs.org/clojure_core/clojure.core/iterate
const iterate = (f, x) => seq(function* () {
let state = x;
while (true) {
yield state;
state = f(state);
}
});
exports.iterate = iterate;
// Returns a lazy sequence of the items in sequence for which `p(item)`
// returns `true`. `p` must be free of side-effects.
//
// Implements clojure filter:
// http://clojuredocs.org/clojure_core/clojure.core/filter
const filter = (p, sequence) => seq(function* () {
if (sequence !== null && sequence !== void(0)) {
for (let item of sequence) {
if (p(item))
yield item;
}
}
});
exports.filter = filter;
// Returns a lazy sequence consisting of the result of applying `f` to the
// set of first items of each sequence, followed by applying f to the set
// of second items in each sequence, until any one of the sequences is
// exhausted. Any remaining items in other sequences are ignored. Function
// `f` should accept number-of-sequences arguments.
//
// Implements clojure map:
// http://clojuredocs.org/clojure_core/clojure.core/map
const map = (f, ...sequences) => seq(function* () {
const count = sequences.length;
// Optimize a single sequence case
if (count === 1) {
let [sequence] = sequences;
if (sequence !== null && sequence !== void(0)) {
for (let item of sequence)
yield f(item);
}
}
else {
// define args array that will be recycled on each
// step to aggregate arguments to be passed to `f`.
let args = [];
// define inputs to contain started generators.
let inputs = [];
let index = 0;
while (index < count) {
inputs[index] = sequences[index][Symbol.iterator]();
index = index + 1;
}
// Run loop yielding of applying `f` to the set of
// items at each step until one of the `inputs` is
// exhausted.
let done = false;
while (!done) {
let index = 0;
let value = void(0);
while (index < count && !done) {
({ done, value } = inputs[index].next());
// If input is not exhausted yet store value in args.
if (!done) {
args[index] = value;
index = index + 1;
}
}
// If none of the inputs is exhasted yet, `args` contain items
// from each input so we yield application of `f` over them.
if (!done)
yield f(...args);
}
}
});
exports.map = map;
// Returns a lazy sequence of the intermediate values of the reduction (as
// per reduce) of sequence by `f`, starting with `initial` value if provided.
//
// Implements clojure reductions:
// http://clojuredocs.org/clojure_core/clojure.core/reductions
const reductions = (...params) => {
const count = params.length;
let hasInitial = false;
let f, initial, source;
if (count === 2) {
[f, source] = params;
}
else if (count === 3) {
[f, initial, source] = params;
hasInitial = true;
}
else {
throw Error("Invoked with wrong number of arguments: " + count);
}
const sequence = seq(source);
return seq(function* () {
let started = hasInitial;
let result = void(0);
// If initial is present yield it.
if (hasInitial)
yield (result = initial);
// For each item of the sequence accumulate new result.
for (let item of sequence) {
// If nothing has being yield yet set result to first
// item and yield it.
if (!started) {
started = true;
yield (result = item);
}
// Otherwise accumulate new result and yield it.
else {
yield (result = f(result, item));
}
}
// If nothing has being yield yet it's empty sequence and no
// `initial` was provided in which case we need to yield `f()`.
if (!started)
yield f();
});
};
exports.reductions = reductions;
// `f` should be a function of 2 arguments. If `initial` is not supplied,
// returns the result of applying `f` to the first 2 items in sequence, then
// applying `f` to that result and the 3rd item, etc. If sequence contains no
// items, `f` must accept no arguments as well, and reduce returns the
// result of calling f with no arguments. If sequence has only 1 item, it
// is returned and `f` is not called. If `initial` is supplied, returns the
// result of applying `f` to `initial` and the first item in sequence, then
// applying `f` to that result and the 2nd item, etc. If sequence contains no
// items, returns `initial` and `f` is not called.
//
// Implements clojure reduce:
// http://clojuredocs.org/clojure_core/clojure.core/reduce
const reduce = (...args) => {
const xs = reductions(...args);
let x;
for (x of xs) void(0);
return x;
};
exports.reduce = reduce;
const each = (f, sequence) => {
for (let x of seq(sequence)) void(f(x));
};
exports.each = each;
const inc = x => x + 1;
// Returns the number of items in the sequence. `count(null)` && `count()`
// returns `0`. Also works on strings, arrays, Maps & Sets.
// Implements clojure count:
// http://clojuredocs.org/clojure_core/clojure.core/count
const count = polymorphic({
null: _ => 0,
void: _ => 0,
indexed: indexed => indexed.length,
map: map => map.size,
set: set => set.size,
default: xs => reduce(inc, 0, xs)
});
exports.count = count;
// Returns `true` if sequence has no items.
// Implements clojure empty?:
// http://clojuredocs.org/clojure_core/clojure.core/empty_q
const isEmpty = sequence => {
// Treat `null` and `undefined` as empty sequences.
if (sequence === null || sequence === void(0))
return true;
// If contains any item non empty so return `false`.
for (let _ of sequence)
return false;
// If has not returned yet, there was nothing to iterate
// so it's empty.
return true;
};
exports.isEmpty = isEmpty;
const and = (a, b) => a && b;
// Returns true if `p(x)` is logical `true` for every `x` in sequence, else
// `false`.
//
// Implements clojure every?:
// http://clojuredocs.org/clojure_core/clojure.core/every_q
const isEvery = (p, sequence) => {
if (sequence !== null && sequence !== void(0)) {
for (let item of sequence) {
if (!p(item))
return false;
}
}
return true;
};
exports.isEvery = isEvery;
// Returns the first logical true value of (p x) for any x in sequence,
// else `null`.
//
// Implements clojure some:
// http://clojuredocs.org/clojure_core/clojure.core/some
const some = (p, sequence) => {
if (sequence !== null && sequence !== void(0)) {
for (let item of sequence) {
if (p(item))
return true;
}
}
return null;
};
exports.some = some;
// Returns a lazy sequence of the first `n` items in sequence, or all items if
// there are fewer than `n`.
//
// Implements clojure take:
// http://clojuredocs.org/clojure_core/clojure.core/take
const take = (n, sequence) => n <= 0 ? empty() : seq(function* () {
let count = n;
for (let item of sequence) {
yield item;
count = count - 1;
if (count === 0) break;
}
});
exports.take = take;
// Returns a lazy sequence of successive items from sequence while
// `p(item)` returns `true`. `p` must be free of side-effects.
//
// Implements clojure take-while:
// http://clojuredocs.org/clojure_core/clojure.core/take-while
const takeWhile = (p, sequence) => seq(function* () {
for (let item of sequence) {
if (!p(item))
break;
yield item;
}
});
exports.takeWhile = takeWhile;
// Returns a lazy sequence of all but the first `n` items in
// sequence.
//
// Implements clojure drop:
// http://clojuredocs.org/clojure_core/clojure.core/drop
const drop = (n, sequence) => seq(function* () {
if (sequence !== null && sequence !== void(0)) {
let count = n;
for (let item of sequence) {
if (count > 0)
count = count - 1;
else
yield item;
}
}
});
exports.drop = drop;
// Returns a lazy sequence of the items in sequence starting from the
// first item for which `p(item)` returns falsy value.
//
// Implements clojure drop-while:
// http://clojuredocs.org/clojure_core/clojure.core/drop-while
const dropWhile = (p, sequence) => seq(function* () {
let keep = false;
for (let item of sequence) {
keep = keep || !p(item);
if (keep) yield item;
}
});
exports.dropWhile = dropWhile;
// Returns a lazy sequence representing the concatenation of the
// suplied sequences.
//
// Implements clojure conact:
// http://clojuredocs.org/clojure_core/clojure.core/concat
const concat = (...sequences) => seq(function* () {
for (let sequence of sequences)
for (let item of sequence)
yield item;
});
exports.concat = concat;
// Returns the first item in the sequence.
//
// Implements clojure first:
// http://clojuredocs.org/clojure_core/clojure.core/first
const first = sequence => {
if (sequence !== null && sequence !== void(0)) {
for (let item of sequence)
return item;
}
return null;
};
exports.first = first;
// Returns a possibly empty sequence of the items after the first.
//
// Implements clojure rest:
// http://clojuredocs.org/clojure_core/clojure.core/rest
const rest = sequence => drop(1, sequence);
exports.rest = rest;
// Returns the value at the index. Returns `notFound` or `undefined`
// if index is out of bounds.
const nth = (xs, n, notFound) => {
if (n >= 0) {
if (isArray(xs) || isArguments(xs) || isString(xs)) {
return n < xs.length ? xs[n] : notFound;
}
else if (xs !== null && xs !== void(0)) {
let count = n;
for (let x of xs) {
if (count <= 0)
return x;
count = count - 1;
}
}
}
return notFound;
};
exports.nth = nth;
// Return the last item in sequence, in linear time.
// If `sequence` is an array or string or arguments
// returns in constant time.
// Implements clojure last:
// http://clojuredocs.org/clojure_core/clojure.core/last
const last = polymorphic({
null: _ => null,
void: _ => null,
indexed: indexed => indexed[indexed.length - 1],
map: xs => reduce((_, x) => x, xs),
set: xs => reduce((_, x) => x, xs),
default: xs => reduce((_, x) => x, xs)
});
exports.last = last;
// Return a lazy sequence of all but the last `n` (default 1) items
// from the give `xs`.
//
// Implements clojure drop-last:
// http://clojuredocs.org/clojure_core/clojure.core/drop-last
const dropLast = flip((xs, n=1) => seq(function* () {
let ys = [];
for (let x of xs) {
ys.push(x);
if (ys.length > n)
yield ys.shift();
}
}));
exports.dropLast = dropLast;
// Returns a lazy sequence of the elements of `xs` with duplicates
// removed
//
// Implements clojure distinct
// http://clojuredocs.org/clojure_core/clojure.core/distinct
const distinct = sequence => seq(function* () {
let items = new Set();
for (let item of sequence) {
if (!items.has(item)) {
items.add(item);
yield item;
}
}
});
exports.distinct = distinct;
// Returns a lazy sequence of the items in `xs` for which
// `p(x)` returns false. `p` must be free of side-effects.
//
// Implements clojure remove
// http://clojuredocs.org/clojure_core/clojure.core/remove
const remove = (p, xs) => filter(complement(p), xs);
exports.remove = remove;
// Returns the result of applying concat to the result of
// `map(f, xs)`. Thus function `f` should return a sequence.
//
// Implements clojure mapcat
// http://clojuredocs.org/clojure_core/clojure.core/mapcat
const mapcat = (f, sequence) => seq(function* () {
const sequences = map(f, sequence);
for (let sequence of sequences)
for (let item of sequence)
yield item;
});
exports.mapcat = mapcat;

View file

@ -0,0 +1,19 @@
/* 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 { Cc, Ci, components: { ID: parseUUID } } = require('chrome');
const { generateUUID } = Cc['@mozilla.org/uuid-generator;1'].
getService(Ci.nsIUUIDGenerator);
// Returns `uuid`. If `id` is passed then it's parsed to `uuid` and returned
// if not then new one is generated.
exports.uuid = function uuid(id) {
return id ? parseUUID(id) : generateUUID();
};