mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-18 05:07:31 +09:00
Move Add-on SDK source to toolkit/jetpack
This commit is contained in:
parent
0a03067ee3
commit
23b67db438
993 changed files with 484 additions and 100663 deletions
123
toolkit/jetpack/sdk/util/array.js
Normal file
123
toolkit/jetpack/sdk/util/array.js
Normal 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;
|
||||
115
toolkit/jetpack/sdk/util/collection.js
Normal file
115
toolkit/jetpack/sdk/util/collection.js
Normal 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];
|
||||
}
|
||||
55
toolkit/jetpack/sdk/util/contract.js
Normal file
55
toolkit/jetpack/sdk/util/contract.js
Normal 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;
|
||||
40
toolkit/jetpack/sdk/util/deprecate.js
Normal file
40
toolkit/jetpack/sdk/util/deprecate.js
Normal 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;
|
||||
54
toolkit/jetpack/sdk/util/dispatcher.js
Normal file
54
toolkit/jetpack/sdk/util/dispatcher.js
Normal 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;
|
||||
90
toolkit/jetpack/sdk/util/list.js
Normal file
90
toolkit/jetpack/sdk/util/list.js
Normal 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;
|
||||
113
toolkit/jetpack/sdk/util/match-pattern.js
Normal file
113
toolkit/jetpack/sdk/util/match-pattern.js
Normal 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;
|
||||
104
toolkit/jetpack/sdk/util/object.js
Normal file
104
toolkit/jetpack/sdk/util/object.js
Normal 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;
|
||||
53
toolkit/jetpack/sdk/util/rules.js
Normal file
53
toolkit/jetpack/sdk/util/rules.js
Normal 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;
|
||||
}
|
||||
593
toolkit/jetpack/sdk/util/sequence.js
Normal file
593
toolkit/jetpack/sdk/util/sequence.js
Normal 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;
|
||||
19
toolkit/jetpack/sdk/util/uuid.js
Normal file
19
toolkit/jetpack/sdk/util/uuid.js
Normal 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();
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue