diff --git a/component.json b/component.json index 0b4f5f10..c2bf6c17 100644 --- a/component.json +++ b/component.json @@ -14,7 +14,7 @@ ], "dependencies": { "wooorm/nlcst-to-textom": "^0.2.0", - "wooorm/parse-latin": "^0.4.0", + "wooorm/parse-latin": "^0.5.1", "wooorm/textom": "^0.4.0", "segmentio/ware": "^1.2.0" }, diff --git a/package.json b/package.json index bce6b1d4..9db6a3b6 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ ], "dependencies": { "nlcst-to-textom": "^0.2.0", - "parse-latin": "^0.4.0", + "parse-latin": "^0.5.1", "textom": "^0.4.0", "ware": "^1.2.0" }, @@ -24,12 +24,12 @@ }, "author": "Titus Wormer ", "devDependencies": { - "browserify": "^8.0.0", - "eslint": "^0.13.0", + "browserify": "^11.0.0", + "eslint": "^0.24.0", "esmangle": "^1.0.0", "istanbul": "^0.3.0", "jscs": "^1.0.0", - "jscs-jsdoc": "^0.4.0", + "jscs-jsdoc": "^1.0.0", "matcha": "^0.6.0", "mocha": "^2.0.0" }, diff --git a/retext.js b/retext.js index 46fde2fe..d7edcfd1 100644 --- a/retext.js +++ b/retext.js @@ -1,4 +1,4 @@ -!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.Retext=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o} */ - function tokenize(parser, value) { var tokens, - token, - start, - end, + offset, + line, + column, match; if (value === null || value === undefined) { @@ -442,6 +437,16 @@ function tokenize(parser, value) { } if (typeof value !== 'string') { + /** + * Return the given nodes if this is either an + * empty array, or an array with a node as a first + * child. + */ + + if ('length' in value && (!value[0] || value[0].type)) { + return value; + } + throw new Error( 'Illegal invocation: \'' + value + '\'' + ' is not a valid argument for \'ParseLatin\'' @@ -454,37 +459,237 @@ function tokenize(parser, value) { return tokens; } - EXPRESSION_TOKEN.lastIndex = 0; - start = 0; - match = EXPRESSION_TOKEN.exec(value); + offset = 0; + line = 1; + column = 1; + + /** + * Get the current position. + * + * @example + * position = now(); // {line: 1, column: 1} + * + * @return {Object} + */ + function now() { + return { + 'line': line, + 'column': column, + 'offset': offset + }; + } + + /** + * Store position information for a node. + * + * @example + * start = now(); + * updatePosition('foo'); + * location = new Position(start); + * // {start: {line: 1, column: 1}, end: {line: 1, column: 3}} + * + * @param {Object} start + */ + function Position(start) { + this.start = start; + this.end = now(); + } + + /** + * Mark position and patch `node.position`. + * + * @example + * var update = position(); + * updatePosition('foo'); + * update({}); + * // { + * // position: { + * // start: {line: 1, column: 1} + * // end: {line: 1, column: 3} + * // } + * // } + * + * @returns {function(Node): Node} + */ + function position() { + var before = now(); - while (match) { /** - * Move the pointer over to after its last - * character. + * Add the position to a node. + * + * @example + * update({type: 'text', value: 'foo'}); + * + * @param {Node} node - Node to attach position + * on. + * @return {Node} - `node`. */ + function patch(node) { + node.position = new Position(before); - end = match.index + match[0].length; + return node; + } + + return patch; + } + + /** + * Update line and column based on `value`. + * + * @example + * update('foo'); + * + * @param {string} subvalue + */ + function update(subvalue) { + var subvalueLength = subvalue.length, + character = -1, + lastIndex = -1; + + offset += subvalueLength; + while (++character < subvalueLength) { + if (subvalue.charAt(character) === '\n') { + lastIndex = character; + line++; + } + } + + if (lastIndex === -1) { + column = column + subvalueLength; + } else { + column = subvalueLength - lastIndex; + } + } + + /** + * Add mechanism. + * + * @param {NLCSTNode} node - Node to add. + * @param {NLCSTParentNode?} [parent] - Optional parent + * node to insert into. + * @return {NLCSTNode} - `node`. + */ + function add(node, parent) { + if (parent) { + parent.children.push(node); + } else { + tokens.push(node); + } + + return node; + } + + /** + * Remove `subvalue` from `value`. + * Expects `subvalue` to be at the start from + * `value`, and applies no validation. + * + * @example + * eat('foo')({type: 'TextNode', value: 'foo'}); + * + * @param {string} subvalue - Removed from `value`, + * and passed to `update`. + * @return {Function} - Wrapper around `add`, which + * also adds `position` to node. + */ + function eat(subvalue) { + var pos = position(); + + /** + * Add the given arguments, add `position` to + * the returned node, and return the node. + * + * @return {Node} + */ + function apply() { + return pos(add.apply(null, arguments)); + } + + value = value.substring(subvalue.length); + + update(subvalue); + + return apply; + } + + /** + * Remove `subvalue` from `value`. Does not patch + * positional information. + * + * @param {string} subvalue - Value to eat. + * @return {Function} + */ + function noPositionEat(subvalue) { /** - * Slice the found content, from (including) - * start to (not including) end, classify it, - * and add the result. + * Add the given arguments and return the node. + * + * @return {Node} */ + function apply() { + return add.apply(null, arguments); + } - token = value.substring(start, end); + value = value.substring(subvalue.length); - tokens.push(parser['tokenize' + classify(token)](token)); + return apply; + } + + /* + * Eat mechanism to use. + */ + + var eater = parser.position ? eat : noPositionEat; + + /** + * Continue matching. + */ + function next() { + EXPRESSION_TOKEN.lastIndex = 0; match = EXPRESSION_TOKEN.exec(value); + } + + next(); + + while (match) { + parser['tokenize' + classify(match[0])](match[0], eater); - start = end; + next(); } return tokens; } /** + * Add mechanism used when text-tokenisers are called + * directly outside of the `tokenize` function. + * + * @param {NLCSTNode} node - Node to add. + * @param {NLCSTParentNode?} [parent] - Optional parent + * node to insert into. + * @return {NLCSTNode} - `node`. + */ +function noopAdd(node, parent) { + if (parent) { + parent.children.push(node); + } + + return node; +} + +/** + * Eat and add mechanism without adding positional + * information, used when text-tokenisers are called + * directly outside of the `tokenize` function. + * + * @return {Function} + */ +function noopEat() { + return noopAdd; +} + +/* * == PARSE LATIN ============================================================ */ @@ -492,21 +697,22 @@ function tokenize(parser, value) { * Transform Latin-script natural language into * an NLCST-tree. * - * @constructor + * @constructor {ParseLatin} */ - -function ParseLatin() { - /** +function ParseLatin(options) { + /* * TODO: This should later be removed (when this * change bubbles through to dependants). */ if (!(this instanceof ParseLatin)) { - return new ParseLatin(); + return new ParseLatin(options); } + + this.position = Boolean(options && options.position); } -/** +/* * Quick access to the prototype. */ @@ -514,7 +720,7 @@ var parseLatinPrototype; parseLatinPrototype = ParseLatin.prototype; -/** +/* * == TOKENIZE =============================================================== */ @@ -523,12 +729,11 @@ parseLatinPrototype = ParseLatin.prototype; * * @see tokenize */ - parseLatinPrototype.tokenize = function (value) { return tokenize(this, value); }; -/** +/* * == TEXT NODES ============================================================= */ @@ -538,26 +743,28 @@ parseLatinPrototype.tokenize = function (value) { * @param {string?} type * @return {function(value): NLCSTText} */ - function createTextFactory(type) { type += 'Node'; /** * Construct a `Text` from a bound `type` * - * @param {value} value + * @param {value} value - Value of the node. + * @param {Function?} [eat] - Optional eat mechanism + * to use. + * @param {NLCSTParentNode?} [parent] - Optional + * parent to insert into. * @return {NLCSTText} */ - - return function (value) { + return function (value, eat, parent) { if (value === null || value === undefined) { value = ''; } - return { + return (eat || noopEat)(value)({ 'type': type, 'value': String(value) - }; + }, parent); }; } @@ -567,7 +774,6 @@ function createTextFactory(type) { * @param {string?} value * @return {NLCSTSymbolNode} */ - parseLatinPrototype.tokenizeSymbol = createTextFactory('Symbol'); /** @@ -576,7 +782,6 @@ parseLatinPrototype.tokenizeSymbol = createTextFactory('Symbol'); * @param {string?} value * @return {NLCSTWhiteSpaceNode} */ - parseLatinPrototype.tokenizeWhiteSpace = createTextFactory('WhiteSpace'); /** @@ -585,7 +790,6 @@ parseLatinPrototype.tokenizeWhiteSpace = createTextFactory('WhiteSpace'); * @param {string?} value * @return {NLCSTPunctuationNode} */ - parseLatinPrototype.tokenizePunctuation = createTextFactory('Punctuation'); /** @@ -594,7 +798,6 @@ parseLatinPrototype.tokenizePunctuation = createTextFactory('Punctuation'); * @param {string?} value * @return {NLCSTSourceNode} */ - parseLatinPrototype.tokenizeSource = createTextFactory('Source'); /** @@ -603,10 +806,9 @@ parseLatinPrototype.tokenizeSource = createTextFactory('Source'); * @param {string?} value * @return {NLCSTTextNode} */ - parseLatinPrototype.tokenizeText = createTextFactory('Text'); -/** +/* * == PARENT NODES =========================================================== * * All these nodes are `pluggable`: they come with a @@ -622,38 +824,50 @@ parseLatinPrototype.tokenizeText = createTextFactory('Text'); */ /** - * @param {Function} Constructor + * Run transform plug-ins for `key` on `nodes`. + * * @param {string} key - * @param {function(*): undefined} callback - * @return {undefined} + * @param {Array.} nodes + * @return {Array.} - `nodes`. */ - -function pluggable(Constructor, key, callback) { - var wareKey; +function run(key, nodes) { + var wareKey, + plugins, + index; wareKey = key + 'Plugins'; - Constructor.prototype[key] = function () { - var self, - result, - plugins, - index; + plugins = this[wareKey]; - self = this; + if (plugins) { + index = -1; - result = callback.apply(self, arguments); + while (plugins[++index]) { + plugins[index](nodes); + } + } - plugins = self[wareKey]; + return nodes; +} - if (plugins) { - index = -1; +/* + * Expose `run`. + */ - while (plugins[++index]) { - plugins[index](result); - } - } +parseLatinPrototype.run = run; - return result; +/** + * @param {Function} Constructor + * @param {string} key + * @param {function(*): undefined} callback + */ +function pluggable(Constructor, key, callback) { + /** + * Set a pluggable version of `callback` + * on `Constructor`. + */ + Constructor.prototype[key] = function () { + return this.run(key, callback.apply(this, arguments)); }; } @@ -661,12 +875,11 @@ function pluggable(Constructor, key, callback) { * Factory to inject `plugins`. Takes `callback` for * the actual inserting. * - * @param {fucntion(Object, string, Array.)} callback + * @param {function(Object, string, Array.)} callback * @return {function(string, Array.)} */ - function useFactory(callback) { - /** + /* * Validate if `plugins` can be inserted. Invokes * the bound `callback` to do the actual inserting. * @@ -681,7 +894,7 @@ function useFactory(callback) { self = this; - /** + /* * Throw if the method is not pluggable. */ @@ -693,7 +906,7 @@ function useFactory(callback) { ); } - /** + /* * Fail silently when no plugins are given. */ @@ -703,7 +916,7 @@ function useFactory(callback) { wareKey = key + 'Plugins'; - /** + /* * Make sure `plugins` is a list. */ @@ -713,7 +926,7 @@ function useFactory(callback) { plugins = plugins.concat(); } - /** + /* * Make sure `wareKey` exists. */ @@ -721,7 +934,7 @@ function useFactory(callback) { self[wareKey] = []; } - /** + /* * Invoke callback with the ware key and plugins. */ @@ -729,7 +942,7 @@ function useFactory(callback) { }; } -/** +/* * Inject `plugins` to modifiy the result of the method * at `key` on the operated on context. * @@ -742,7 +955,7 @@ parseLatinPrototype.use = useFactory(function (context, key, plugins) { context[key] = context[key].concat(plugins); }); -/** +/* * Inject `plugins` to modifiy the result of the method * at `key` on the operated on context, before any other. * @@ -764,14 +977,19 @@ parseLatinPrototype.useFirst = useFactory(function (context, key, plugins) { * @param {string?} value * @return {NLCSTWordNode} */ +pluggable(ParseLatin, 'tokenizeWord', function (value, eat) { + var add, + parent; -pluggable(ParseLatin, 'tokenizeWord', function (value) { - return { + add = (eat || noopEat)(''); + parent = { 'type': 'WordNode', - 'children': [ - this.tokenizeText(value) - ] + 'children': [] }; + + this.tokenizeText(value, eat, parent); + + return add(parent); }); /** @@ -788,8 +1006,7 @@ pluggable(ParseLatin, 'tokenizeWord', function (value) { * @param {string?} value * @return {NLCSTSentenceNode} */ - -pluggable(ParseLatin, 'tokenizeSentence', parser({ +pluggable(ParseLatin, 'tokenizeSentence', createParser({ 'type': 'SentenceNode', 'tokenizer': 'tokenize' })); @@ -807,8 +1024,7 @@ pluggable(ParseLatin, 'tokenizeSentence', parser({ * @param {string?} value * @return {NLCSTParagraphNode} */ - -pluggable(ParseLatin, 'tokenizeParagraph', parser({ +pluggable(ParseLatin, 'tokenizeParagraph', createParser({ 'type': 'ParagraphNode', 'delimiter': expressions.terminalMarker, 'delimiterType': 'PunctuationNode', @@ -827,8 +1043,7 @@ pluggable(ParseLatin, 'tokenizeParagraph', parser({ * @param {string?} value * @return {NLCSTRootNode} */ - -pluggable(ParseLatin, 'tokenizeRoot', parser({ +pluggable(ParseLatin, 'tokenizeRoot', createParser({ 'type': 'RootNode', 'delimiter': expressions.newLine, 'delimiterType': 'WhiteSpaceNode', @@ -840,12 +1055,11 @@ pluggable(ParseLatin, 'tokenizeRoot', parser({ * * @see ParseLatin#tokenizeRoot */ - parseLatinPrototype.parse = function (value) { return this.tokenizeRoot(value); }; -/** +/* * == PLUGINS ================================================================ */ @@ -853,7 +1067,9 @@ parseLatinPrototype.use('tokenizeSentence', [ require('./plugin/merge-initial-word-symbol'), require('./plugin/merge-final-word-symbol'), require('./plugin/merge-inner-word-symbol'), - require('./plugin/merge-initialisms') + require('./plugin/merge-initialisms'), + require('./plugin/merge-words'), + require('./plugin/patch-position') ]); parseLatinPrototype.use('tokenizeParagraph', [ @@ -866,44 +1082,52 @@ parseLatinPrototype.use('tokenizeParagraph', [ require('./plugin/make-initial-white-space-siblings'), require('./plugin/make-final-white-space-siblings'), require('./plugin/break-implicit-sentences'), - require('./plugin/remove-empty-nodes') + require('./plugin/remove-empty-nodes'), + require('./plugin/patch-position') ]); parseLatinPrototype.use('tokenizeRoot', [ require('./plugin/make-initial-white-space-siblings'), require('./plugin/make-final-white-space-siblings'), - require('./plugin/remove-empty-nodes') + require('./plugin/remove-empty-nodes'), + require('./plugin/patch-position') ]); -/** +/* * == EXPORT ================================================================= */ -/** +/* * Expose `ParseLatin`. */ module.exports = ParseLatin; -/** +/* * Expose `pluginFactory` on `ParseLatin` as `plugin`. */ ParseLatin.plugin = pluginFactory; -/** +/* * Expose `modifierFactory` on `ParseLatin` as `modifier`. */ ParseLatin.modifier = modifierFactory; -},{"./expressions":4,"./modifier":5,"./parser":7,"./plugin":8,"./plugin/break-implicit-sentences":9,"./plugin/make-final-white-space-siblings":10,"./plugin/make-initial-white-space-siblings":11,"./plugin/merge-affix-exceptions":12,"./plugin/merge-affix-symbol":13,"./plugin/merge-final-word-symbol":14,"./plugin/merge-initial-lower-case-letter-sentences":15,"./plugin/merge-initial-word-symbol":16,"./plugin/merge-initialisms":17,"./plugin/merge-inner-word-symbol":18,"./plugin/merge-non-word-sentences":19,"./plugin/merge-prefix-exceptions":20,"./plugin/merge-remaining-full-stops":21,"./plugin/remove-empty-nodes":22}],7:[function(require,module,exports){ +},{"./expressions":4,"./modifier":5,"./parser":7,"./plugin":8,"./plugin/break-implicit-sentences":9,"./plugin/make-final-white-space-siblings":10,"./plugin/make-initial-white-space-siblings":11,"./plugin/merge-affix-exceptions":12,"./plugin/merge-affix-symbol":13,"./plugin/merge-final-word-symbol":14,"./plugin/merge-initial-lower-case-letter-sentences":15,"./plugin/merge-initial-word-symbol":16,"./plugin/merge-initialisms":17,"./plugin/merge-inner-word-symbol":18,"./plugin/merge-non-word-sentences":19,"./plugin/merge-prefix-exceptions":20,"./plugin/merge-remaining-full-stops":21,"./plugin/merge-words":22,"./plugin/patch-position":23,"./plugin/remove-empty-nodes":24}],7:[function(require,module,exports){ 'use strict'; var tokenizer; tokenizer = require('./tokenizer'); +/** + * Construct a parser based on `options`. + * + * @param {Object} options + * @return {function(string): NLCSTNode} + */ function parserFactory(options) { var type, delimiter, @@ -931,7 +1155,7 @@ function parserFactory(options) { module.exports = parserFactory; -},{"./tokenizer":23}],8:[function(require,module,exports){ +},{"./tokenizer":25}],8:[function(require,module,exports){ 'use strict'; /** @@ -940,7 +1164,6 @@ module.exports = parserFactory; * @param {function(Object, number, Object)} callback * @return {function(NLCSTParent)} */ - function pluginFactory(callback) { return function (parent) { var index, @@ -955,7 +1178,7 @@ function pluginFactory(callback) { }; } -/** +/* * Expose `pluginFactory`. */ @@ -964,7 +1187,7 @@ module.exports = pluginFactory; },{}],9:[function(require,module,exports){ 'use strict'; -/** +/* * Dependencies. */ @@ -976,7 +1199,7 @@ nlcstToString = require('nlcst-to-string'); modifier = require('../modifier'); expressions = require('../expressions'); -/** +/* * Constants. * * - Two or more new line characters. @@ -995,11 +1218,14 @@ EXPRESSION_MULTI_NEW_LINE = expressions.newLineMulti; * @param {NLCSTParagraphNode} parent * @return {undefined} */ - function breakImplicitSentences(child, index, parent) { var children, position, length, + tail, + head, + end, + insertion, node; if (child.type !== 'SentenceNode') { @@ -1024,25 +1250,41 @@ function breakImplicitSentences(child, index, parent) { child.children = children.slice(0, position); - parent.children.splice(index + 1, 0, node, { + insertion = { 'type': 'SentenceNode', 'children': children.slice(position + 1) - }); + }; + + tail = children[position - 1]; + head = children[position + 1]; + + parent.children.splice(index + 1, 0, node, insertion); + + if (child.position && tail.position && head.position) { + end = child.position.end; + + child.position.end = tail.position.end; + + insertion.position = { + 'start': head.position.start, + 'end': end + }; + } return index + 1; } } -/** +/* * Expose `breakImplicitSentences` as a plugin. */ module.exports = modifier(breakImplicitSentences); -},{"../expressions":4,"../modifier":5,"nlcst-to-string":25}],10:[function(require,module,exports){ +},{"../expressions":4,"../modifier":5,"nlcst-to-string":27}],10:[function(require,module,exports){ 'use strict'; -/** +/* * Dependencies. */ @@ -1059,9 +1301,9 @@ modifier = require('../modifier'); * @param {NLCSTParent} parent * @return {undefined|number} */ - function makeFinalWhiteSpaceSiblings(child, index, parent) { - var children; + var children, + prev; children = child.children; @@ -1071,8 +1313,13 @@ function makeFinalWhiteSpaceSiblings(child, index, parent) { children[children.length - 1].type === 'WhiteSpaceNode' ) { parent.children.splice(index + 1, 0, child.children.pop()); + prev = children[children.length - 1]; - /** + if (prev && prev.position && child.position) { + child.position.end = prev.position.end; + } + + /* * Next, iterate over the current node again. */ @@ -1080,7 +1327,7 @@ function makeFinalWhiteSpaceSiblings(child, index, parent) { } } -/** +/* * Expose `makeFinalWhiteSpaceSiblings` as a modifier. */ @@ -1089,7 +1336,7 @@ module.exports = modifier(makeFinalWhiteSpaceSiblings); },{"../modifier":5}],11:[function(require,module,exports){ 'use strict'; -/** +/* * Dependencies. */ @@ -1104,11 +1351,10 @@ plugin = require('../plugin'); * @param {NLCSTNode} child * @param {number} index * @param {NLCSTParent} parent - * @return {undefined} */ - function makeInitialWhiteSpaceSiblings(child, index, parent) { - var children; + var children, + next; children = child.children; @@ -1118,10 +1364,15 @@ function makeInitialWhiteSpaceSiblings(child, index, parent) { children[0].type === 'WhiteSpaceNode' ) { parent.children.splice(index, 0, children.shift()); + next = children[0]; + + if (next && next.position && child.position) { + child.position.start = next.position.start; + } } } -/** +/* * Expose `makeInitialWhiteSpaceSiblings` as a plugin. */ @@ -1130,7 +1381,7 @@ module.exports = plugin(makeInitialWhiteSpaceSiblings); },{"../plugin":8}],12:[function(require,module,exports){ 'use strict'; -/** +/* * Dependencies. */ @@ -1149,7 +1400,6 @@ modifier = require('../modifier'); * @param {NLCSTParagraphNode} parent * @return {undefined|number} */ - function mergeAffixExceptions(child, index, parent) { var children, node, @@ -1184,13 +1434,19 @@ function mergeAffixExceptions(child, index, parent) { previousChild = parent.children[index - 1]; - previousChild.children = previousChild.children.concat( - children - ); + previousChild.children = previousChild.children.concat(children); + + /* + * Update position. + */ + + if (previousChild.position && child.position) { + previousChild.position.end = child.position.end; + } parent.children.splice(index, 1); - /** + /* * Next, iterate over the node *now* at the current * position. */ @@ -1200,16 +1456,16 @@ function mergeAffixExceptions(child, index, parent) { } } -/** +/* * Expose `mergeAffixExceptions` as a modifier. */ module.exports = modifier(mergeAffixExceptions); -},{"../modifier":5,"nlcst-to-string":25}],13:[function(require,module,exports){ +},{"../modifier":5,"nlcst-to-string":27}],13:[function(require,module,exports){ 'use strict'; -/** +/* * Dependencies. */ @@ -1221,7 +1477,7 @@ nlcstToString = require('nlcst-to-string'); modifier = require('../modifier'); expressions = require('../expressions'); -/** +/* * Constants. * * - Closing or final punctuation, or terminal markers @@ -1244,10 +1500,11 @@ EXPRESSION_AFFIX_SYMBOL = expressions.affixSymbol; * @param {NLCSTParagraphNode} parent * @return {undefined|number} */ - function mergeAffixSymbol(child, index, parent) { var children, - firstChild; + prev, + first, + second; children = child.children; @@ -1256,18 +1513,32 @@ function mergeAffixSymbol(child, index, parent) { children.length && index !== 0 ) { - firstChild = children[0]; + first = children[0]; + second = children[1]; + prev = parent.children[index - 1]; if ( ( - firstChild.type === 'SymbolNode' || - firstChild.type === 'PunctuationNode' + first.type === 'SymbolNode' || + first.type === 'PunctuationNode' ) && - EXPRESSION_AFFIX_SYMBOL.test(nlcstToString(firstChild)) + EXPRESSION_AFFIX_SYMBOL.test(nlcstToString(first)) ) { - parent.children[index - 1].children.push(children.shift()); + prev.children.push(children.shift()); + + /* + * Update position. + */ - /** + if (first.position && prev.position) { + prev.position.end = first.position.end; + } + + if (second && second.position && child.position) { + child.position.start = second.position.start; + } + + /* * Next, iterate over the previous node again. */ @@ -1276,16 +1547,16 @@ function mergeAffixSymbol(child, index, parent) { } } -/** +/* * Expose `mergeAffixSymbol` as a modifier. */ module.exports = modifier(mergeAffixSymbol); -},{"../expressions":4,"../modifier":5,"nlcst-to-string":25}],14:[function(require,module,exports){ +},{"../expressions":4,"../modifier":5,"nlcst-to-string":27}],14:[function(require,module,exports){ 'use strict'; -/** +/* * Dependencies. */ @@ -1304,7 +1575,6 @@ modifier = require('../modifier'); * @param {NLCSTSentenceNode} parent * @return {undefined|number} */ - function mergeFinalWordSymbol(child, index, parent) { var children, prev, @@ -1333,20 +1603,28 @@ function mergeFinalWordSymbol(child, index, parent) { prev.type === 'WordNode' ) ) { - /** + /* * Remove `child` from parent. */ children.splice(index, 1); - /** + /* * Add the punctuation mark at the end of the * previous node. */ prev.children.push(child); - /** + /* + * Update position. + */ + + if (prev.position && child.position) { + prev.position.end = child.position.end; + } + + /* * Next, iterate over the node *now* at the * current position (which was the next node). */ @@ -1356,16 +1634,16 @@ function mergeFinalWordSymbol(child, index, parent) { } } -/** +/* * Expose `mergeFinalWordSymbol` as a modifier. */ module.exports = modifier(mergeFinalWordSymbol); -},{"../modifier":5,"nlcst-to-string":25}],15:[function(require,module,exports){ +},{"../modifier":5,"nlcst-to-string":27}],15:[function(require,module,exports){ 'use strict'; -/** +/* * Dependencies. */ @@ -1377,7 +1655,7 @@ nlcstToString = require('nlcst-to-string'); modifier = require('../modifier'); expressions = require('../expressions'); -/** +/* * Constants. * * - Initial lowercase letter. @@ -1396,7 +1674,6 @@ EXPRESSION_LOWER_INITIAL = expressions.lowerInitial; * @param {NLCSTParagraphNode} parent * @return {undefined|number} */ - function mergeInitialLowerCaseLetterSentences(child, index, parent) { var siblings, children, @@ -1429,7 +1706,15 @@ function mergeInitialLowerCaseLetterSentences(child, index, parent) { siblings.splice(index, 1); - /** + /* + * Update position. + */ + + if (prev.position && child.position) { + prev.position.end = child.position.end; + } + + /* * Next, iterate over the node *now* at * the current position. */ @@ -1447,16 +1732,16 @@ function mergeInitialLowerCaseLetterSentences(child, index, parent) { } } -/** +/* * Expose `mergeInitialLowerCaseLetterSentences` as a modifier. */ module.exports = modifier(mergeInitialLowerCaseLetterSentences); -},{"../expressions":4,"../modifier":5,"nlcst-to-string":25}],16:[function(require,module,exports){ +},{"../expressions":4,"../modifier":5,"nlcst-to-string":27}],16:[function(require,module,exports){ 'use strict'; -/** +/* * Dependencies. */ @@ -1475,7 +1760,6 @@ modifier = require('../modifier'); * @param {NLCSTSentenceNode} parent * @return {undefined|number} */ - function mergeInitialWordSymbol(child, index, parent) { var children, next; @@ -1494,7 +1778,7 @@ function mergeInitialWordSymbol(child, index, parent) { next = children[index + 1]; - /** + /* * If either a previous word, or no following word, * exists, exit early. */ @@ -1512,20 +1796,28 @@ function mergeInitialWordSymbol(child, index, parent) { return; } - /** + /* * Remove `child` from parent. */ children.splice(index, 1); - /** + /* * Add the punctuation mark at the start of the * next node. */ next.children.unshift(child); - /** + /* + * Update position. + */ + + if (next.position && child.position) { + next.position.start = child.position.start; + } + + /* * Next, iterate over the node at the previous * position, as it's now adjacent to a following * word. @@ -1534,16 +1826,16 @@ function mergeInitialWordSymbol(child, index, parent) { return index - 1; } -/** +/* * Expose `mergeInitialWordSymbol` as a modifier. */ module.exports = modifier(mergeInitialWordSymbol); -},{"../modifier":5,"nlcst-to-string":25}],17:[function(require,module,exports){ +},{"../modifier":5,"nlcst-to-string":27}],17:[function(require,module,exports){ 'use strict'; -/** +/* * Dependencies. */ @@ -1555,7 +1847,7 @@ nlcstToString = require('nlcst-to-string'); modifier = require('../modifier'); expressions = require('../expressions'); -/** +/* * Constants. * * - Numbers. @@ -1573,7 +1865,6 @@ EXPRESSION_NUMERICAL = expressions.numerical; * @param {NLCSTSentenceNode} parent * @return {undefined|number} */ - function mergeInitialisms(child, index, parent) { var siblings, prev, @@ -1610,7 +1901,7 @@ function mergeInitialisms(child, index, parent) { value = nlcstToString(otherChild); if (position % 2 === 0) { - /** + /* * Initialisms consist of one * character values. */ @@ -1632,19 +1923,27 @@ function mergeInitialisms(child, index, parent) { } if (!isAllDigits) { - /** + /* * Remove `child` from parent. */ siblings.splice(index, 1); - /** + /* * Add child to the previous children. */ children.push(child); - /** + /* + * Update position. + */ + + if (prev.position && child.position) { + prev.position.end = child.position.end; + } + + /* * Next, iterate over the node *now* at the current * position. */ @@ -1655,16 +1954,16 @@ function mergeInitialisms(child, index, parent) { } } -/** +/* * Expose `mergeInitialisms` as a modifier. */ module.exports = modifier(mergeInitialisms); -},{"../expressions":4,"../modifier":5,"nlcst-to-string":25}],18:[function(require,module,exports){ +},{"../expressions":4,"../modifier":5,"nlcst-to-string":27}],18:[function(require,module,exports){ 'use strict'; -/** +/* * Dependencies. */ @@ -1676,7 +1975,7 @@ nlcstToString = require('nlcst-to-string'); modifier = require('../modifier'); expressions = require('../expressions'); -/** +/* * Constants. * * - Symbols part of surrounding words. @@ -1694,11 +1993,11 @@ EXPRESSION_INNER_WORD_SYMBOL = expressions.wordSymbolInner; * @param {NLCSTSentenceNode} parent * @return {undefined|number} */ - function mergeInnerWordSymbol(child, index, parent) { var siblings, sibling, prev, + last, position, tokens, queue; @@ -1720,7 +2019,7 @@ function mergeInnerWordSymbol(child, index, parent) { tokens = []; queue = []; - /** + /* * - If a token which is neither word nor * inner word symbol is found, the loop * is broken. @@ -1751,7 +2050,7 @@ function mergeInnerWordSymbol(child, index, parent) { } if (tokens.length) { - /** + /* * If there is a queue, remove its length * from `position`. */ @@ -1760,20 +2059,30 @@ function mergeInnerWordSymbol(child, index, parent) { position -= queue.length; } - /** + /* * Remove every (one or more) inner-word punctuation * marks and children of words. */ siblings.splice(index, position - index); - /** + /* * Add all found tokens to `prev`s children. */ prev.children = prev.children.concat(tokens); - /** + last = tokens[tokens.length - 1]; + + /* + * Update position. + */ + + if (prev.position && last.position) { + prev.position.end = last.position.end; + } + + /* * Next, iterate over the node *now* at the current * position. */ @@ -1784,16 +2093,16 @@ function mergeInnerWordSymbol(child, index, parent) { } } -/** +/* * Expose `mergeInnerWordSymbol` as a modifier. */ module.exports = modifier(mergeInnerWordSymbol); -},{"../expressions":4,"../modifier":5,"nlcst-to-string":25}],19:[function(require,module,exports){ +},{"../expressions":4,"../modifier":5,"nlcst-to-string":27}],19:[function(require,module,exports){ 'use strict'; -/** +/* * Dependencies. */ @@ -1810,11 +2119,11 @@ modifier = require('../modifier'); * @param {NLCSTParagraphNode} parent * @return {undefined|number} */ - function mergeNonWordSentences(child, index, parent) { var children, position, - prev; + prev, + next; children = child.children; position = -1; @@ -1830,13 +2139,21 @@ function mergeNonWordSentences(child, index, parent) { if (prev) { prev.children = prev.children.concat(children); - /** + /* * Remove the child. */ parent.children.splice(index, 1); - /** + /* + * Patch position. + */ + + if (prev.position && child.position) { + prev.position.end = child.position.end; + } + + /* * Next, iterate over the node *now* at * the current position (which was the * next node). @@ -1845,12 +2162,20 @@ function mergeNonWordSentences(child, index, parent) { return index; } - prev = parent.children[index + 1]; + next = parent.children[index + 1]; - if (prev) { - prev.children = children.concat(prev.children); + if (next) { + next.children = children.concat(next.children); - /** + /* + * Patch position. + */ + + if (next.position && child.position) { + next.position.start = child.position.start; + } + + /* * Remove the child. */ @@ -1858,7 +2183,7 @@ function mergeNonWordSentences(child, index, parent) { } } -/** +/* * Expose `mergeNonWordSentences` as a modifier. */ @@ -1867,7 +2192,7 @@ module.exports = modifier(mergeNonWordSentences); },{"../modifier":5}],20:[function(require,module,exports){ 'use strict'; -/** +/* * Dependencies. */ @@ -1877,7 +2202,7 @@ var nlcstToString, nlcstToString = require('nlcst-to-string'); modifier = require('../modifier'); -/** +/* * Constants. * * - Blacklist of full stop characters that should not @@ -1892,7 +2217,7 @@ EXPRESSION_ABBREVIATION_PREFIX = new RegExp( '[0-9]+|' + '[a-z]|' + - /** + /* * Common Latin Abbreviations: * Based on: http://en.wikipedia.org/wiki/List_of_Latin_abbreviations * Where only the abbreviations written without joining full stops, @@ -1918,10 +2243,10 @@ EXPRESSION_ABBREVIATION_PREFIX = new RegExp( * @param {NLCSTParagraphNode} parent * @return {undefined|number} */ - function mergePrefixExceptions(child, index, parent) { var children, - node; + node, + next; children = child.children; @@ -1945,13 +2270,21 @@ function mergePrefixExceptions(child, index, parent) { nlcstToString(node).toLowerCase() ) ) { - child.children = children.concat( - parent.children[index + 1].children - ); + next = parent.children[index + 1]; + + child.children = children.concat(next.children); parent.children.splice(index + 1, 1); - /** + /* + * Update position. + */ + + if (next.position && child.position) { + child.position.end = next.position.end; + } + + /* * Next, iterate over the current node again. */ @@ -1961,16 +2294,16 @@ function mergePrefixExceptions(child, index, parent) { } } -/** +/* * Expose `mergePrefixExceptions` as a modifier. */ module.exports = modifier(mergePrefixExceptions); -},{"../modifier":5,"nlcst-to-string":25}],21:[function(require,module,exports){ +},{"../modifier":5,"nlcst-to-string":27}],21:[function(require,module,exports){ 'use strict'; -/** +/* * Dependencies. */ @@ -1982,7 +2315,7 @@ nlcstToString = require('nlcst-to-string'); plugin = require('../plugin'); expressions = require('../expressions'); -/** +/* * Constants. * * - Blacklist of full stop characters that should not @@ -2000,9 +2333,7 @@ EXPRESSION_TERMINAL_MARKER = expressions.terminalMarker; * word (if available). * * @param {NLCSTNode} child - * @return {undefined} */ - function mergeRemainingFullStops(child) { var children, position, @@ -2024,7 +2355,7 @@ function mergeRemainingFullStops(child) { grandchild.type !== 'SymbolNode' && grandchild.type !== 'PunctuationNode' ) { - /** + /* * This is a sentence without terminal marker, * so we 'fool' the code to make it think we * have found one. @@ -2037,7 +2368,7 @@ function mergeRemainingFullStops(child) { continue; } - /** + /* * Exit when this token is not a terminal marker. */ @@ -2045,7 +2376,7 @@ function mergeRemainingFullStops(child) { continue; } - /** + /* * Ignore the first terminal marker found * (starting at the end), as it should not * be merged. @@ -2057,7 +2388,7 @@ function mergeRemainingFullStops(child) { continue; } - /** + /* * Only merge a single full stop. */ @@ -2071,7 +2402,7 @@ function mergeRemainingFullStops(child) { if (prev && prev.type === 'WordNode') { nextNext = children[position + 2]; - /** + /* * Continue when the full stop is followed by * a space and another full stop, such as: * `{.} .` @@ -2086,47 +2417,180 @@ function mergeRemainingFullStops(child) { continue; } - /** + /* * Remove `child` from parent. */ children.splice(position, 1); - /** + /* * Add the punctuation mark at the end of the * previous node. */ prev.children.push(grandchild); + /* + * Update position. + */ + + if (grandchild.position && prev.position) { + prev.position.end = grandchild.position.end; + } + position--; } else if (next && next.type === 'WordNode') { - /** + /* * Remove `child` from parent. */ children.splice(position, 1); - /** + /* * Add the punctuation mark at the start of * the next node. */ next.children.unshift(grandchild); + + if (grandchild.position && next.position) { + next.position.start = grandchild.position.start; + } } } } -/** +/* * Expose `mergeRemainingFullStops` as a plugin. */ module.exports = plugin(mergeRemainingFullStops); -},{"../expressions":4,"../plugin":8,"nlcst-to-string":25}],22:[function(require,module,exports){ +},{"../expressions":4,"../plugin":8,"nlcst-to-string":27}],22:[function(require,module,exports){ +'use strict'; + +/* + * Dependencies. + */ + +var modifier = require('../modifier'); + +/** + * Merge multiple words. This merges the children of + * adjacent words, something which should not occur + * naturally by parse-latin, but might happen when + * custom tokens were passed in. + * + * @param {NLCSTNode} child + * @param {number} index + * @param {NLCSTSentenceNode} parent + * @return {undefined|number} + */ +function mergeFinalWordSymbol(child, index, parent) { + var siblings = parent.children, + next; + + if (child.type === 'WordNode') { + next = siblings[index + 1]; + + if (next && next.type === 'WordNode') { + /* + * Remove `next` from parent. + */ + + siblings.splice(index + 1, 1); + + /* + * Add the punctuation mark at the end of the + * previous node. + */ + + child.children = child.children.concat(next.children); + + /* + * Update position. + */ + + if (next.position && child.position) { + child.position.end = next.position.end; + } + + /* + * Next, re-iterate the current node. + */ + + return index; + } + } +} + +/* + * Expose `mergeFinalWordSymbol` as a modifier. + */ + +module.exports = modifier(mergeFinalWordSymbol); + +},{"../modifier":5}],23:[function(require,module,exports){ 'use strict'; +/* + * Dependencies. + */ + +var plugin = require('../plugin'); + +/** + * Add a `position` object when it does not yet exist + * on `node`. + * + * @param {NLCSTNode} node - Node to patch. + */ +function patch(node) { + if (!node.position) { + node.position = {}; + } +} + /** + * Patch the position on a parent node based on its first + * and last child. + * + * @param {NLCSTNode} child + */ +function patchPosition(child, index, node) { + var siblings = node.children; + + if (!child.position) { + return; + } + + if ( + index === 0 && + (!node.position || /* istanbul ignore next */ !node.position.start) + ) { + patch(node); + node.position.start = child.position.start; + } + + if ( + index === siblings.length - 1 && + (!node.position || !node.position.end) + ) { + patch(node); + node.position.end = child.position.end; + } +} + +/* + * Expose `patchPosition` as a plugin. + */ + +module.exports = plugin(patchPosition); + +},{"../plugin":8}],24:[function(require,module,exports){ +'use strict'; + +/* * Dependencies. */ @@ -2142,12 +2606,11 @@ modifier = require('../modifier'); * @param {NLCSTParagraphNode} parent * @return {undefined|number} */ - function removeEmptyNodes(child, index, parent) { if ('children' in child && !child.children.length) { parent.children.splice(index, 1); - /** + /* * Next, iterate over the node *now* at * the current position (which was the * next node). @@ -2157,13 +2620,13 @@ function removeEmptyNodes(child, index, parent) { } } -/** +/* * Expose `removeEmptyNodes` as a modifier. */ module.exports = modifier(removeEmptyNodes); -},{"../modifier":5}],23:[function(require,module,exports){ +},{"../modifier":5}],25:[function(require,module,exports){ 'use strict'; var nlcstToString; @@ -2174,29 +2637,33 @@ nlcstToString = require('nlcst-to-string'); * Factory to create a tokenizer based on a given * `expression`. * + * @param {string} childType * @param {RegExp} expression + * @return {function(NLCSTParent): Array.} */ - function tokenizerFactory(childType, expression) { /** * A function which splits * - * @param {RegExp} expression + * @param {NLCSTParent} node + * @return {Array.} */ - - return function (child) { + return function (node) { var children, tokens, type, length, index, lastIndex, - start; + start, + parent, + first, + last; children = []; - tokens = child.children; - type = child.type; + tokens = node.children; + type = node.type; length = tokens.length; @@ -2214,10 +2681,22 @@ function tokenizerFactory(childType, expression) { expression.test(nlcstToString(tokens[index])) ) ) { - children.push({ + first = tokens[start]; + last = tokens[index]; + + parent = { 'type': type, 'children': tokens.slice(start, index + 1) - }); + }; + + if (first.position && last.position) { + parent.position = { + 'start': first.position.start, + 'end': last.position.end + }; + } + + children.push(parent); start = index + 1; } @@ -2229,7 +2708,7 @@ function tokenizerFactory(childType, expression) { module.exports = tokenizerFactory; -},{"nlcst-to-string":25}],24:[function(require,module,exports){ +},{"nlcst-to-string":27}],26:[function(require,module,exports){ 'use strict'; /** @@ -2316,7 +2795,7 @@ function iterate(values, callback, context) { module.exports = iterate; -},{}],25:[function(require,module,exports){ +},{}],27:[function(require,module,exports){ 'use strict'; /** @@ -2360,7 +2839,7 @@ function nlcstToString(nlcst) { module.exports = nlcstToString; -},{}],26:[function(require,module,exports){ +},{}],28:[function(require,module,exports){ 'use strict'; /* @@ -4414,7 +4893,7 @@ function TextOMConstructor() { module.exports = TextOMConstructor; -},{}],27:[function(require,module,exports){ +},{}],29:[function(require,module,exports){ /** * Module Dependencies */ @@ -4428,6 +4907,16 @@ var wrap = require('wrap-fn'); module.exports = Ware; +/** + * Throw an error. + * + * @param {Error} error + */ + +function fail (err) { + throw err; +} + /** * Initialize a new `Ware` manager, with optional `fns`. * @@ -4481,7 +4970,7 @@ Ware.prototype.run = function () { // next step function next (err) { - if (err) return done(err); + if (err) return (done || fail)(err); var fn = fns[i++]; var arr = slice.call(args); @@ -4497,7 +4986,7 @@ Ware.prototype.run = function () { return this; }; -},{"wrap-fn":28}],28:[function(require,module,exports){ +},{"wrap-fn":30}],30:[function(require,module,exports){ /** * Module Dependencies */ @@ -4624,7 +5113,7 @@ function once(fn) { }; } -},{"co":29}],29:[function(require,module,exports){ +},{"co":31}],31:[function(require,module,exports){ /** * slice() reference. diff --git a/retext.min.js b/retext.min.js index 73a3cb44..8232be49 100644 --- a/retext.min.js +++ b/retext.min.js @@ -1 +1 @@ -!function(b,a){'object'==typeof exports&&'undefined'!=typeof module?module.exports=b():'function'==typeof define&&define.amd?define([],b):('undefined'!=typeof window?a=window:'undefined'!=typeof global?a=global:'undefined'!=typeof self&&(a=self),a.Retext=b())}(function(){return function a(b,c,e){function f(d,k){if(!c[d]){if(!b[d]){var i=typeof require=='function'&&require;if(!k&&i)return i(d,!0);if(g)return g(d,!0);var j=new Error("Cannot find module '"+d+"'");throw j.code='MODULE_NOT_FOUND',j}var h=c[d]={exports:{}};b[d][0].call(h.exports,function(c){var a=b[d][1][c];return f(a?a:c)},h,h.exports,a,b,c,e)}return c[d].exports}var g=typeof require=='function'&&require;for(var d=0;d1)return;e.test(f)||(j=!1)}else if(f!=='.')if(ca.length)try{return e.apply(h,a.concat(c))}catch(a){return c(a)}return g(e)?b(e).apply(h,a.concat(c)):d(e,c).apply(h,a)}}function d(b,a){return function(){var c;try{c=b.apply(this,arguments)}catch(b){return a(b)}h(c)?c.then(function(b){a(null,b)},a):c instanceof Error?a(c):a(null,c)}}function g(a){return a&&a.constructor&&'GeneratorFunction'==a.constructor.name}function h(a){return a&&'function'==typeof a.then}function i(b){return function(){var c=b.apply(this,arguments);return b=a,c}}var a=function(){},b=f('co');c.exports=e},{co:29}],29:[function(l,k,m){function b(b){var f=e(b);return function(h){function k(a,b){setImmediate(function(){h.call(e,a,b)})}function i(f,g){var b;if(arguments.length>2&&(g=a.call(arguments,1)),f)try{b=j.throw(f)}catch(a){return k(a)}if(!f)try{b=j.next(g)}catch(a){return k(a)}if(b.done)return k(null,b.value);if(b.value=d(b.value,e),'function'==typeof b.value){var c=!1;try{b.value.call(e,function(){if(c)return;c=!0,i.apply(e,arguments)})}catch(a){setImmediate(function(){if(c)return;c=!0,i(a)})}return}i(new TypeError('You may only yield a function, promise, generator, array, or object, but the following was passed: "'+String(b.value)+'"'))}var e=this,j=b;if(f){var g=a.call(arguments),l=g.length,m=l&&'function'==typeof g[l-1];h=m?g.pop():c,j=b.apply(this,g)}else h=h||c;i()}}function d(a,c){return e(a)?b(a.call(c)):j(a)?b(a):i(a)?f(a):'function'==typeof a?a:h(a)||Array.isArray(a)?g.call(c,a):a}function g(a){var b=this,c=Array.isArray(a);return function(i){function k(a,c){if(j)return;try{if(a=d(a,b),'function'!=typeof a)return f[c]=a,--h||i(null,f);a.call(b,function(a,b){if(j)return;if(a)return j=!0,i(a);f[c]=b,--h||i(null,f)})}catch(a){j=!0,i(a)}}var g=Object.keys(a),h=g.length,f=c?new Array(h):new a.constructor,j;if(!h){setImmediate(function(){i(null,f)});return}if(!c)for(var e=0;e1)return;e.test(h)||(k=!1)}else if(h!=='.')if(ca.length)try{return e.apply(h,a.concat(c))}catch(a){return c(a)}return g(e)?b(e).apply(h,a.concat(c)):d(e,c).apply(h,a)}}function d(b,a){return function(){var c;try{c=b.apply(this,arguments)}catch(b){return a(b)}h(c)?c.then(function(b){a(null,b)},a):c instanceof Error?a(c):a(null,c)}}function g(a){return a&&a.constructor&&'GeneratorFunction'==a.constructor.name}function h(a){return a&&'function'==typeof a.then}function i(b){return function(){var c=b.apply(this,arguments);return b=a,c}}var a=function(){},b=f('co');c.exports=e},{co:31}],31:[function(l,k,m){function b(b){var f=e(b);return function(h){function k(a,b){setImmediate(function(){h.call(e,a,b)})}function i(f,g){var b;if(arguments.length>2&&(g=a.call(arguments,1)),f)try{b=j.throw(f)}catch(a){return k(a)}if(!f)try{b=j.next(g)}catch(a){return k(a)}if(b.done)return k(null,b.value);if(b.value=d(b.value,e),'function'==typeof b.value){var c=!1;try{b.value.call(e,function(){if(c)return;c=!0,i.apply(e,arguments)})}catch(a){setImmediate(function(){if(c)return;c=!0,i(a)})}return}i(new TypeError('You may only yield a function, promise, generator, array, or object, but the following was passed: "'+String(b.value)+'"'))}var e=this,j=b;if(f){var g=a.call(arguments),l=g.length,m=l&&'function'==typeof g[l-1];h=m?g.pop():c,j=b.apply(this,g)}else h=h||c;i()}}function d(a,c){return e(a)?b(a.call(c)):j(a)?b(a):i(a)?f(a):'function'==typeof a?a:h(a)||Array.isArray(a)?g.call(c,a):a}function g(a){var b=this,c=Array.isArray(a);return function(i){function k(a,c){if(j)return;try{if(a=d(a,b),'function'!=typeof a)return f[c]=a,--h||i(null,f);a.call(b,function(a,b){if(j)return;if(a)return j=!0,i(a);f[c]=b,--h||i(null,f)})}catch(a){j=!0,i(a)}}var g=Object.keys(a),h=g.length,f=c?new Array(h):new a.constructor,j;if(!h){setImmediate(function(){i(null,f)});return}if(!c)for(var e=0;e