mathjs
Version:
Math.js is an extensive math library for JavaScript and Node.js. It features a flexible expression parser with support for symbolic computation, comes with a large set of built-in functions and constants, and offers an integrated solution to work with dif
216 lines (203 loc) • 6.3 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createChainClass = void 0;
var _is = require("../../utils/is.js");
var _string = require("../../utils/string.js");
var _object = require("../../utils/object.js");
var _factory = require("../../utils/factory.js");
const name = 'Chain';
const dependencies = ['?on', 'math', 'typed'];
const createChainClass = exports.createChainClass = /* #__PURE__ */(0, _factory.factory)(name, dependencies, _ref => {
let {
on,
math,
typed
} = _ref;
/**
* @constructor Chain
* Wrap any value in a chain, allowing to perform chained operations on
* the value.
*
* All methods available in the math.js library can be called upon the chain,
* and then will be evaluated with the value itself as first argument.
* The chain can be closed by executing chain.done(), which will return
* the final value.
*
* The Chain has a number of special functions:
* - done() Finalize the chained operation and return the
* chain's value.
* - valueOf() The same as done()
* - toString() Returns a string representation of the chain's value.
*
* @param {*} [value]
*/
function Chain(value) {
if (!(this instanceof Chain)) {
throw new SyntaxError('Constructor must be called with the new operator');
}
if ((0, _is.isChain)(value)) {
this.value = value.value;
} else {
this.value = value;
}
}
/**
* Attach type information
*/
Chain.prototype.type = 'Chain';
Chain.prototype.isChain = true;
/**
* Close the chain. Returns the final value.
* Does the same as method valueOf()
* @returns {*} value
*/
Chain.prototype.done = function () {
return this.value;
};
/**
* Close the chain. Returns the final value.
* Does the same as method done()
* @returns {*} value
*/
Chain.prototype.valueOf = function () {
return this.value;
};
/**
* Get a string representation of the value in the chain
* @returns {string}
*/
Chain.prototype.toString = function () {
return (0, _string.format)(this.value);
};
/**
* Get a JSON representation of the chain
* @returns {Object}
*/
Chain.prototype.toJSON = function () {
return {
mathjs: 'Chain',
value: this.value
};
};
/**
* Instantiate a Chain from its JSON representation
* @param {Object} json An object structured like
* `{"mathjs": "Chain", value: ...}`,
* where mathjs is optional
* @returns {Chain}
*/
Chain.fromJSON = function (json) {
return new Chain(json.value);
};
/**
* Create a proxy method for the chain
* @param {string} name
* @param {Function} fn The function to be proxied
* If fn is no function, it is silently ignored.
* @private
*/
function createProxy(name, fn) {
if (typeof fn === 'function') {
Chain.prototype[name] = chainify(fn);
}
}
/**
* Create a proxy method for the chain
* @param {string} name
* @param {function} resolver The function resolving with the
* function to be proxied
* @private
*/
function createLazyProxy(name, resolver) {
(0, _object.lazy)(Chain.prototype, name, function outerResolver() {
const fn = resolver();
if (typeof fn === 'function') {
return chainify(fn);
}
return undefined; // if not a function, ignore
});
}
/**
* Make a function chainable
* @param {function} fn
* @return {Function} chain function
* @private
*/
function chainify(fn) {
return function () {
// Here, `this` will be the context of a Chain instance
if (arguments.length === 0) {
return new Chain(fn(this.value));
}
const args = [this.value];
for (let i = 0; i < arguments.length; i++) {
args[i + 1] = arguments[i];
}
if (typed.isTypedFunction(fn)) {
const sigObject = typed.resolve(fn, args);
// We want to detect if a rest parameter has matched across the
// value in the chain and the current arguments of this call.
// That is the case if and only if the matching signature has
// exactly one parameter (which then must be a rest parameter
// as it is matching at least two actual arguments).
if (sigObject.params.length === 1) {
throw new Error('chain function ' + fn.name + ' cannot match rest parameter between chain value and additional arguments.');
}
return new Chain(sigObject.implementation.apply(fn, args));
}
return new Chain(fn.apply(fn, args));
};
}
/**
* Create a proxy for a single method, or an object with multiple methods.
* Example usage:
*
* Chain.createProxy('add', function add (x, y) {...})
* Chain.createProxy({
* add: function add (x, y) {...},
* subtract: function subtract (x, y) {...}
* }
*
* @param {string | Object} arg0 A name (string), or an object with
* functions
* @param {*} [arg1] A function, when arg0 is a name
*/
Chain.createProxy = function (arg0, arg1) {
if (typeof arg0 === 'string') {
// createProxy(name, value)
createProxy(arg0, arg1);
} else {
// createProxy(values)
for (const name in arg0) {
if ((0, _object.hasOwnProperty)(arg0, name) && excludedNames[name] === undefined) {
createLazyProxy(name, () => arg0[name]);
}
}
}
};
const excludedNames = {
expression: true,
docs: true,
type: true,
classes: true,
json: true,
error: true,
isChain: true // conflicts with the property isChain of a Chain instance
};
// create proxy for everything that is in math.js
Chain.createProxy(math);
// register on the import event, automatically add a proxy for every imported function.
if (on) {
on('import', function (name, resolver, path) {
if (!path) {
// an imported function (not a data type or something special)
createLazyProxy(name, resolver);
}
});
}
return Chain;
}, {
isClass: true
});
;