UNPKG

luhn-generator

Version:

A generator of numbers that passes the validation of Luhn algorithm or Luhn formula, also known as the 'modulus 10' or 'mod 10' algorithm

1,020 lines (913 loc) 26.8 kB
'use strict'; var typeOf = require('kind-of'); var utils = module.exports; /** * Returns true if the given value is a node. * * ```js * var Node = require('snapdragon-node'); * var node = new Node({type: 'foo'}); * console.log(utils.isNode(node)); //=> true * console.log(utils.isNode({})); //=> false * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @returns {Boolean} * @api public */ utils.isNode = function(node) { return typeOf(node) === 'object' && node.isNode === true; }; /** * Emit an empty string for the given `node`. * * ```js * // do nothing for beginning-of-string * snapdragon.compiler.set('bos', utils.noop); * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @returns {undefined} * @api public */ utils.noop = function(node) { append(this, '', node); }; /** * Appdend `node.val` to `compiler.output`, exactly as it was created * by the parser. * * ```js * snapdragon.compiler.set('text', utils.identity); * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @returns {undefined} * @api public */ utils.identity = function(node) { append(this, node.val, node); }; /** * Previously named `.emit`, this method appends the given `val` * to `compiler.output` for the given node. Useful when you know * what value should be appended advance, regardless of the actual * value of `node.val`. * * ```js * snapdragon.compiler * .set('i', function(node) { * this.mapVisit(node); * }) * .set('i.open', utils.append('<i>')) * .set('i.close', utils.append('</i>')) * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @returns {Function} Returns a compiler middleware function. * @api public */ utils.append = function(val) { return function(node) { append(this, val, node); }; }; /** * Used in compiler middleware, this onverts an AST node into * an empty `text` node and deletes `node.nodes` if it exists. * The advantage of this method is that, as opposed to completely * removing the node, indices will not need to be re-calculated * in sibling nodes, and nothing is appended to the output. * * ```js * utils.toNoop(node); * // convert `node.nodes` to the given value instead of deleting it * utils.toNoop(node, []); * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @param {Array} `nodes` Optionally pass a new `nodes` value, to replace the existing `node.nodes` array. * @api public */ utils.toNoop = function(node, nodes) { if (nodes) { node.nodes = nodes; } else { delete node.nodes; node.type = 'text'; node.val = ''; } }; /** * Visit `node` with the given `fn`. The built-in `.visit` method in snapdragon * automatically calls registered compilers, this allows you to pass a visitor * function. * * ```js * snapdragon.compiler.set('i', function(node) { * utils.visit(node, function(childNode) { * // do stuff with "childNode" * return childNode; * }); * }); * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @param {Function} `fn` * @return {Object} returns the node after recursively visiting all child nodes. * @api public */ utils.visit = function(node, fn) { assert(utils.isNode(node), 'expected node to be an instance of Node'); assert(isFunction(fn), 'expected a visitor function'); fn(node); return node.nodes ? utils.mapVisit(node, fn) : node; }; /** * Map [visit](#visit) the given `fn` over `node.nodes`. This is called by * [visit](#visit), use this method if you do not want `fn` to be called on * the first node. * * ```js * snapdragon.compiler.set('i', function(node) { * utils.mapVisit(node, function(childNode) { * // do stuff with "childNode" * return childNode; * }); * }); * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @param {Object} `options` * @param {Function} `fn` * @return {Object} returns the node * @api public */ utils.mapVisit = function(node, fn) { assert(utils.isNode(node), 'expected node to be an instance of Node'); assert(isArray(node.nodes), 'expected node.nodes to be an array'); assert(isFunction(fn), 'expected a visitor function'); for (var i = 0; i < node.nodes.length; i++) { utils.visit(node.nodes[i], fn); } return node; }; /** * Unshift an `*.open` node onto `node.nodes`. * * ```js * var Node = require('snapdragon-node'); * snapdragon.parser.set('brace', function(node) { * var match = this.match(/^{/); * if (match) { * var parent = new Node({type: 'brace'}); * utils.addOpen(parent, Node); * console.log(parent.nodes[0]): * // { type: 'brace.open', val: '' }; * * // push the parent "brace" node onto the stack * this.push(parent); * * // return the parent node, so it's also added to the AST * return brace; * } * }); * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @param {Function} `Node` (required) Node constructor function from [snapdragon-node][]. * @param {Function} `filter` Optionaly specify a filter function to exclude the node. * @return {Object} Returns the created opening node. * @api public */ utils.addOpen = function(node, Node, val, filter) { assert(utils.isNode(node), 'expected node to be an instance of Node'); assert(isFunction(Node), 'expected Node to be a constructor function'); if (typeof val === 'function') { filter = val; val = ''; } if (typeof filter === 'function' && !filter(node)) return; var open = new Node({ type: node.type + '.open', val: val}); var unshift = node.unshift || node.unshiftNode; if (typeof unshift === 'function') { unshift.call(node, open); } else { utils.unshiftNode(node, open); } return open; }; /** * Push a `*.close` node onto `node.nodes`. * * ```js * var Node = require('snapdragon-node'); * snapdragon.parser.set('brace', function(node) { * var match = this.match(/^}/); * if (match) { * var parent = this.parent(); * if (parent.type !== 'brace') { * throw new Error('missing opening: ' + '}'); * } * * utils.addClose(parent, Node); * console.log(parent.nodes[parent.nodes.length - 1]): * // { type: 'brace.close', val: '' }; * * // no need to return a node, since the parent * // was already added to the AST * return; * } * }); * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @param {Function} `Node` (required) Node constructor function from [snapdragon-node][]. * @param {Function} `filter` Optionaly specify a filter function to exclude the node. * @return {Object} Returns the created closing node. * @api public */ utils.addClose = function(node, Node, val, filter) { assert(utils.isNode(node), 'expected node to be an instance of Node'); assert(isFunction(Node), 'expected Node to be a constructor function'); if (typeof val === 'function') { filter = val; val = ''; } if (typeof filter === 'function' && !filter(node)) return; var close = new Node({ type: node.type + '.close', val: val}); var push = node.push || node.pushNode; if (typeof push === 'function') { push.call(node, close); } else { utils.pushNode(node, close); } return close; }; /** * Wraps the given `node` with `*.open` and `*.close` nodes. * * @param {Object} `node` Instance of [snapdragon-node][] * @param {Function} `Node` (required) Node constructor function from [snapdragon-node][]. * @param {Function} `filter` Optionaly specify a filter function to exclude the node. * @return {Object} Returns the node * @api public */ utils.wrapNodes = function(node, Node, filter) { assert(utils.isNode(node), 'expected node to be an instance of Node'); assert(isFunction(Node), 'expected Node to be a constructor function'); utils.addOpen(node, Node, filter); utils.addClose(node, Node, filter); return node; }; /** * Push the given `node` onto `parent.nodes`, and set `parent` as `node.parent. * * ```js * var parent = new Node({type: 'foo'}); * var node = new Node({type: 'bar'}); * utils.pushNode(parent, node); * console.log(parent.nodes[0].type) // 'bar' * console.log(node.parent.type) // 'foo' * ``` * @param {Object} `parent` * @param {Object} `node` Instance of [snapdragon-node][] * @return {Object} Returns the child node * @api public */ utils.pushNode = function(parent, node) { assert(utils.isNode(parent), 'expected parent node to be an instance of Node'); assert(utils.isNode(node), 'expected node to be an instance of Node'); node.define('parent', parent); parent.nodes = parent.nodes || []; parent.nodes.push(node); return node; }; /** * Unshift `node` onto `parent.nodes`, and set `parent` as `node.parent. * * ```js * var parent = new Node({type: 'foo'}); * var node = new Node({type: 'bar'}); * utils.unshiftNode(parent, node); * console.log(parent.nodes[0].type) // 'bar' * console.log(node.parent.type) // 'foo' * ``` * @param {Object} `parent` * @param {Object} `node` Instance of [snapdragon-node][] * @return {undefined} * @api public */ utils.unshiftNode = function(parent, node) { assert(utils.isNode(parent), 'expected parent node to be an instance of Node'); assert(utils.isNode(node), 'expected node to be an instance of Node'); node.define('parent', parent); parent.nodes = parent.nodes || []; parent.nodes.unshift(node); }; /** * Pop the last `node` off of `parent.nodes`. The advantage of * using this method is that it checks for `node.nodes` and works * with any version of `snapdragon-node`. * * ```js * var parent = new Node({type: 'foo'}); * utils.pushNode(parent, new Node({type: 'foo'})); * utils.pushNode(parent, new Node({type: 'bar'})); * utils.pushNode(parent, new Node({type: 'baz'})); * console.log(parent.nodes.length); //=> 3 * utils.popNode(parent); * console.log(parent.nodes.length); //=> 2 * ``` * @param {Object} `parent` * @param {Object} `node` Instance of [snapdragon-node][] * @return {Number|Undefined} Returns the length of `node.nodes` or undefined. * @api public */ utils.popNode = function(node) { assert(utils.isNode(node), 'expected node to be an instance of Node'); if (typeof node.pop === 'function') { return node.pop(); } return node.nodes && node.nodes.pop(); }; /** * Shift the first `node` off of `parent.nodes`. The advantage of * using this method is that it checks for `node.nodes` and works * with any version of `snapdragon-node`. * * ```js * var parent = new Node({type: 'foo'}); * utils.pushNode(parent, new Node({type: 'foo'})); * utils.pushNode(parent, new Node({type: 'bar'})); * utils.pushNode(parent, new Node({type: 'baz'})); * console.log(parent.nodes.length); //=> 3 * utils.shiftNode(parent); * console.log(parent.nodes.length); //=> 2 * ``` * @param {Object} `parent` * @param {Object} `node` Instance of [snapdragon-node][] * @return {Number|Undefined} Returns the length of `node.nodes` or undefined. * @api public */ utils.shiftNode = function(node) { assert(utils.isNode(node), 'expected node to be an instance of Node'); if (typeof node.shift === 'function') { return node.shift(); } return node.nodes && node.nodes.shift(); }; /** * Remove the specified `node` from `parent.nodes`. * * ```js * var parent = new Node({type: 'abc'}); * var foo = new Node({type: 'foo'}); * utils.pushNode(parent, foo); * utils.pushNode(parent, new Node({type: 'bar'})); * utils.pushNode(parent, new Node({type: 'baz'})); * console.log(parent.nodes.length); //=> 3 * utils.removeNode(parent, foo); * console.log(parent.nodes.length); //=> 2 * ``` * @param {Object} `parent` * @param {Object} `node` Instance of [snapdragon-node][] * @return {Object|undefined} Returns the removed node, if successful, or undefined if it does not exist on `parent.nodes`. * @api public */ utils.removeNode = function(parent, node) { assert(utils.isNode(parent), 'expected parent.node to be an instance of Node'); assert(utils.isNode(node), 'expected node to be an instance of Node'); if (!parent.nodes) { return null; } if (typeof parent.remove === 'function') { return parent.remove(node); } var idx = parent.nodes.indexOf(node); if (idx !== -1) { return parent.nodes.splice(idx, 1); } }; /** * Returns true if `node.type` matches the given `type`. Throws a * `TypeError` if `node` is not an instance of `Node`. * * ```js * var Node = require('snapdragon-node'); * var node = new Node({type: 'foo'}); * console.log(utils.isType(node, 'foo')); // false * console.log(utils.isType(node, 'bar')); // true * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @param {String} `type` * @return {Boolean} * @api public */ utils.isType = function(node, type) { assert(utils.isNode(node), 'expected node to be an instance of Node'); switch (typeOf(type)) { case 'array': var types = type.slice(); for (var i = 0; i < types.length; i++) { if (utils.isType(node, types[i])) { return true; } } return false; case 'string': return node.type === type; case 'regexp': return type.test(node.type); default: { throw new TypeError('expected "type" to be an array, string or regexp'); } } }; /** * Returns true if the given `node` has the given `type` in `node.nodes`. * Throws a `TypeError` if `node` is not an instance of `Node`. * * ```js * var Node = require('snapdragon-node'); * var node = new Node({ * type: 'foo', * nodes: [ * new Node({type: 'bar'}), * new Node({type: 'baz'}) * ] * }); * console.log(utils.hasType(node, 'xyz')); // false * console.log(utils.hasType(node, 'baz')); // true * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @param {String} `type` * @return {Boolean} * @api public */ utils.hasType = function(node, type) { assert(utils.isNode(node), 'expected node to be an instance of Node'); if (!Array.isArray(node.nodes)) return false; for (var i = 0; i < node.nodes.length; i++) { if (utils.isType(node.nodes[i], type)) { return true; } } return false; }; /** * Returns the first node from `node.nodes` of the given `type` * * ```js * var node = new Node({ * type: 'foo', * nodes: [ * new Node({type: 'text', val: 'abc'}), * new Node({type: 'text', val: 'xyz'}) * ] * }); * * var textNode = utils.firstOfType(node.nodes, 'text'); * console.log(textNode.val); * //=> 'abc' * ``` * @param {Array} `nodes` * @param {String} `type` * @return {Object|undefined} Returns the first matching node or undefined. * @api public */ utils.firstOfType = function(nodes, type) { for (var i = 0; i < nodes.length; i++) { var node = nodes[i]; if (utils.isType(node, type)) { return node; } } }; /** * Returns the node at the specified index, or the first node of the * given `type` from `node.nodes`. * * ```js * var node = new Node({ * type: 'foo', * nodes: [ * new Node({type: 'text', val: 'abc'}), * new Node({type: 'text', val: 'xyz'}) * ] * }); * * var nodeOne = utils.findNode(node.nodes, 'text'); * console.log(nodeOne.val); * //=> 'abc' * * var nodeTwo = utils.findNode(node.nodes, 1); * console.log(nodeTwo.val); * //=> 'xyz' * ``` * * @param {Array} `nodes` * @param {String|Number} `type` Node type or index. * @return {Object} Returns a node or undefined. * @api public */ utils.findNode = function(nodes, type) { if (!Array.isArray(nodes)) { return null; } if (typeof type === 'number') { return nodes[type]; } return utils.firstOfType(nodes, type); }; /** * Returns true if the given node is an "*.open" node. * * ```js * var Node = require('snapdragon-node'); * var brace = new Node({type: 'brace'}); * var open = new Node({type: 'brace.open'}); * var close = new Node({type: 'brace.close'}); * * console.log(utils.isOpen(brace)); // false * console.log(utils.isOpen(open)); // true * console.log(utils.isOpen(close)); // false * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @return {Boolean} * @api public */ utils.isOpen = function(node) { assert(utils.isNode(node), 'expected node to be an instance of Node'); return node.type.slice(-5) === '.open'; }; /** * Returns true if the given node is a "*.close" node. * * ```js * var Node = require('snapdragon-node'); * var brace = new Node({type: 'brace'}); * var open = new Node({type: 'brace.open'}); * var close = new Node({type: 'brace.close'}); * * console.log(utils.isClose(brace)); // false * console.log(utils.isClose(open)); // false * console.log(utils.isClose(close)); // true * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @return {Boolean} * @api public */ utils.isClose = function(node) { assert(utils.isNode(node), 'expected node to be an instance of Node'); return node.type.slice(-6) === '.close'; }; /** * Returns true if `node.nodes` **has** an `.open` node * * ```js * var Node = require('snapdragon-node'); * var brace = new Node({ * type: 'brace', * nodes: [] * }); * * var open = new Node({type: 'brace.open'}); * console.log(utils.hasOpen(brace)); // false * * brace.pushNode(open); * console.log(utils.hasOpen(brace)); // true * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @return {Boolean} * @api public */ utils.hasOpen = function(node) { assert(utils.isNode(node), 'expected node to be an instance of Node'); var first = node.first || node.nodes ? node.nodes[0] : null; if (utils.isNode(first)) { return first.type === node.type + '.open'; } return false; }; /** * Returns true if `node.nodes` **has** a `.close` node * * ```js * var Node = require('snapdragon-node'); * var brace = new Node({ * type: 'brace', * nodes: [] * }); * * var close = new Node({type: 'brace.close'}); * console.log(utils.hasClose(brace)); // false * * brace.pushNode(close); * console.log(utils.hasClose(brace)); // true * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @return {Boolean} * @api public */ utils.hasClose = function(node) { assert(utils.isNode(node), 'expected node to be an instance of Node'); var last = node.last || node.nodes ? node.nodes[node.nodes.length - 1] : null; if (utils.isNode(last)) { return last.type === node.type + '.close'; } return false; }; /** * Returns true if `node.nodes` has both `.open` and `.close` nodes * * ```js * var Node = require('snapdragon-node'); * var brace = new Node({ * type: 'brace', * nodes: [] * }); * * var open = new Node({type: 'brace.open'}); * var close = new Node({type: 'brace.close'}); * console.log(utils.hasOpen(brace)); // false * console.log(utils.hasClose(brace)); // false * * brace.pushNode(open); * brace.pushNode(close); * console.log(utils.hasOpen(brace)); // true * console.log(utils.hasClose(brace)); // true * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @return {Boolean} * @api public */ utils.hasOpenAndClose = function(node) { return utils.hasOpen(node) && utils.hasClose(node); }; /** * Push the given `node` onto the `state.inside` array for the * given type. This array is used as a specialized "stack" for * only the given `node.type`. * * ```js * var state = { inside: {}}; * var node = new Node({type: 'brace'}); * utils.addType(state, node); * console.log(state.inside); * //=> { brace: [{type: 'brace'}] } * ``` * @param {Object} `state` The `compiler.state` object or custom state object. * @param {Object} `node` Instance of [snapdragon-node][] * @return {Array} Returns the `state.inside` stack for the given type. * @api public */ utils.addType = function(state, node) { assert(utils.isNode(node), 'expected node to be an instance of Node'); assert(isObject(state), 'expected state to be an object'); var type = node.parent ? node.parent.type : node.type.replace(/\.open$/, ''); if (!state.hasOwnProperty('inside')) { state.inside = {}; } if (!state.inside.hasOwnProperty(type)) { state.inside[type] = []; } var arr = state.inside[type]; arr.push(node); return arr; }; /** * Remove the given `node` from the `state.inside` array for the * given type. This array is used as a specialized "stack" for * only the given `node.type`. * * ```js * var state = { inside: {}}; * var node = new Node({type: 'brace'}); * utils.addType(state, node); * console.log(state.inside); * //=> { brace: [{type: 'brace'}] } * utils.removeType(state, node); * //=> { brace: [] } * ``` * @param {Object} `state` The `compiler.state` object or custom state object. * @param {Object} `node` Instance of [snapdragon-node][] * @return {Array} Returns the `state.inside` stack for the given type. * @api public */ utils.removeType = function(state, node) { assert(utils.isNode(node), 'expected node to be an instance of Node'); assert(isObject(state), 'expected state to be an object'); var type = node.parent ? node.parent.type : node.type.replace(/\.close$/, ''); if (state.inside.hasOwnProperty(type)) { return state.inside[type].pop(); } }; /** * Returns true if `node.val` is an empty string, or `node.nodes` does * not contain any non-empty text nodes. * * ```js * var node = new Node({type: 'text'}); * utils.isEmpty(node); //=> true * node.val = 'foo'; * utils.isEmpty(node); //=> false * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @param {Function} `fn` * @return {Boolean} * @api public */ utils.isEmpty = function(node, fn) { assert(utils.isNode(node), 'expected node to be an instance of Node'); if (!Array.isArray(node.nodes)) { if (node.type !== 'text') { return true; } if (typeof fn === 'function') { return fn(node, node.parent); } return !utils.trim(node.val); } for (var i = 0; i < node.nodes.length; i++) { var child = node.nodes[i]; if (utils.isOpen(child) || utils.isClose(child)) { continue; } if (!utils.isEmpty(child, fn)) { return false; } } return true; }; /** * Returns true if the `state.inside` stack for the given type exists * and has one or more nodes on it. * * ```js * var state = { inside: {}}; * var node = new Node({type: 'brace'}); * console.log(utils.isInsideType(state, 'brace')); //=> false * utils.addType(state, node); * console.log(utils.isInsideType(state, 'brace')); //=> true * utils.removeType(state, node); * console.log(utils.isInsideType(state, 'brace')); //=> false * ``` * @param {Object} `state` * @param {String} `type` * @return {Boolean} * @api public */ utils.isInsideType = function(state, type) { assert(isObject(state), 'expected state to be an object'); assert(isString(type), 'expected type to be a string'); if (!state.hasOwnProperty('inside')) { return false; } if (!state.inside.hasOwnProperty(type)) { return false; } return state.inside[type].length > 0; }; /** * Returns true if `node` is either a child or grand-child of the given `type`, * or `state.inside[type]` is a non-empty array. * * ```js * var state = { inside: {}}; * var node = new Node({type: 'brace'}); * var open = new Node({type: 'brace.open'}); * console.log(utils.isInside(state, open, 'brace')); //=> false * utils.pushNode(node, open); * console.log(utils.isInside(state, open, 'brace')); //=> true * ``` * @param {Object} `state` Either the `compiler.state` object, if it exists, or a user-supplied state object. * @param {Object} `node` Instance of [snapdragon-node][] * @param {String} `type` The `node.type` to check for. * @return {Boolean} * @api public */ utils.isInside = function(state, node, type) { assert(utils.isNode(node), 'expected node to be an instance of Node'); assert(isObject(state), 'expected state to be an object'); if (Array.isArray(type)) { for (var i = 0; i < type.length; i++) { if (utils.isInside(state, node, type[i])) { return true; } } return false; } var parent = node.parent; if (typeof type === 'string') { return (parent && parent.type === type) || utils.isInsideType(state, type); } if (typeOf(type) === 'regexp') { if (parent && parent.type && type.test(parent.type)) { return true; } var keys = Object.keys(state.inside); var len = keys.length; var idx = -1; while (++idx < len) { var key = keys[idx]; var val = state.inside[key]; if (Array.isArray(val) && val.length !== 0 && type.test(key)) { return true; } } } return false; }; /** * Get the last `n` element from the given `array`. Used for getting * a node from `node.nodes.` * * @param {Array} `array` * @param {Number} `n` * @return {undefined} * @api public */ utils.last = function(arr, n) { return arr[arr.length - (n || 1)]; }; /** * Cast the given `val` to an array. * * ```js * console.log(utils.arrayify('')); * //=> [] * console.log(utils.arrayify('foo')); * //=> ['foo'] * console.log(utils.arrayify(['foo'])); * //=> ['foo'] * ``` * @param {any} `val` * @return {Array} * @api public */ utils.arrayify = function(val) { if (typeof val === 'string' && val !== '') { return [val]; } if (!Array.isArray(val)) { return []; } return val; }; /** * Convert the given `val` to a string by joining with `,`. Useful * for creating a cheerio/CSS/DOM-style selector from a list of strings. * * @param {any} `val` * @return {Array} * @api public */ utils.stringify = function(val) { return utils.arrayify(val).join(','); }; /** * Ensure that the given value is a string and call `.trim()` on it, * or return an empty string. * * @param {String} `str` * @return {String} * @api public */ utils.trim = function(str) { return typeof str === 'string' ? str.trim() : ''; }; /** * Return true if val is an object */ function isObject(val) { return typeOf(val) === 'object'; } /** * Return true if val is a string */ function isString(val) { return typeof val === 'string'; } /** * Return true if val is a function */ function isFunction(val) { return typeof val === 'function'; } /** * Return true if val is an array */ function isArray(val) { return Array.isArray(val); } /** * Shim to ensure the `.append` methods work with any version of snapdragon */ function append(compiler, val, node) { if (typeof compiler.append !== 'function') { return compiler.emit(val, node); } return compiler.append(val, node); } /** * Simplified assertion. Throws an error is `val` is falsey. */ function assert(val, message) { if (!val) throw new Error(message); }