├── .eslintrc ├── .gitignore ├── .jshintrc ├── LICENSE ├── README.md ├── bin ├── pre-commit └── release ├── dist └── tree-walker-bundle.js ├── index.js ├── package.json ├── test └── test-tree-walk.js └── third_party └── WeakMap ├── LICENSE └── index.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | // To minimize dependencies on Node- or browser-specific features, leave the 3 | // env empty, and instead define globals as needed. 4 | "env": {}, 5 | 6 | // Project-wide globals. If other globals are necessary, prefer putting them 7 | // in a comment at the top of the file rather than adding them here. 8 | "globals": { 9 | "console": true, 10 | "module": true, 11 | "require": true, 12 | }, 13 | "rules": { 14 | // Enforce "one true brace style", allowing start and end braces to be 15 | // on the same line. 16 | "brace-style": [2, "1tbs", {"allowSingleLine": true}], 17 | 18 | // Enforce the name 'self' when assigning `this` to a local variable. 19 | "consistent-this": [0, "self"], 20 | 21 | // Allow braces to be omitted from `if` statements, etc. 22 | curly: [0], 23 | 24 | // Enforce two-space indentation. 25 | "indent": [2, 2, {indentSwitchCase: true}], 26 | 27 | // Enforce the use of strict mode at the file level. 28 | "global-strict": [2, "always"], 29 | 30 | // Allow things like `while(true)`. 31 | "no-constant-condition": 0, 32 | 33 | // Allow variable shadowing. 34 | "no-shadow": 0, 35 | 36 | // Restrict what kind of objects can be used with 'throw'. 37 | "no-throw-literal": 2, 38 | 39 | // Allow identifiers with leading or trailing underscores. 40 | "no-underscore-dangle": 0, 41 | 42 | // Allow unused parameters, but not unused variables. 43 | "no-unused-vars": [2, {"vars": "all", "args": "none"}], 44 | 45 | // Allow functions to be used before they are defined. 46 | "no-use-before-define": [2, "nofunc"], 47 | 48 | // Use single quotes, except when escaping would be necessary. 49 | "quotes": [2, "single", "avoid-escape"], 50 | 51 | // Force IIFEs to be wrapped in parentheses. 52 | "wrap-iife": [2, "inside"], 53 | 54 | "yoda": [2, "never", {"exceptRange": true}] 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "undef": true, 3 | "unused": "vars", 4 | "node": true, 5 | "predef": [] 6 | } 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Patrick Dubroy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | tree-walk 2 | ========= 3 | 4 | tree-walk is a JavaScript library providing useful functions for traversing, 5 | inspecting, and transforming arbitrary tree structures. It's based on the 6 | `walk` module that I wrote for [Underscore-contrib](http://documentcloud.github.io/underscore-contrib/). 7 | 8 | [![NPM](https://nodei.co/npm/tree-walk.png?compact=true)](https://nodei.co/npm/tree-walk/) 9 | 10 | Usage 11 | ----- 12 | 13 | The most basic operation on a tree is to iterate through all its nodes, which 14 | is provided by `preorder` and `postorder`. They can be used in much the same 15 | way as [Underscore's 'each' function][each]. For example, take a simple tree: 16 | 17 | [each]: http://underscorejs.org/#each 18 | 19 | var tree = { 20 | 'name': { 'first': 'Bucky', 'last': 'Fuller' }, 21 | 'occupations': ['designer', 'inventor'] 22 | }; 23 | 24 | We can do a preorder traversal of the tree: 25 | 26 | var walk = require('tree-walk'); 27 | 28 | walk.preorder(tree, function(value, key, parent) { 29 | console.log(key + ': ' + value); 30 | }); 31 | 32 | which produces the following output: 33 | 34 | undefined: [object Object] 35 | name: [object Object] 36 | first: Bucky 37 | last: Fuller 38 | occupations: designer,inventor 39 | 0: designer 40 | 1: inventor 41 | 42 | A preorder traversal visits the nodes in the tree in a top-down fashion: first 43 | the root node is visited, then all of its child nodes are recursively visited. 44 | `postorder` does the opposite, calling the visitor function for a node 45 | only after visiting all of its child nodes. 46 | 47 | Collection Functions 48 | -------------------- 49 | 50 | This module provides versions of most of the 51 | [Underscore collection functions](http://underscorejs.org/#collections), with 52 | some small differences that make them better suited for operating on trees. For 53 | example, you can use `filter` to get a list of all the strings in a tree: 54 | 55 | var walk = require('tree-walk'); 56 | walk.filter(walk.preorder, _.isString); 57 | 58 | Like many other functions in this module, the argument to `filter` is a function 59 | indicating in what order the nodes should be visited. Currently, only 60 | `preorder` and `postorder` are supported. 61 | 62 | Custom Walkers 63 | -------------- 64 | 65 | Sometimes, you have a tree structure that can't be naïvely traversed. A good 66 | example of this is a DOM tree: because each element has a reference to its 67 | parent, a naïve walk would encounter circular references. To handle such cases, 68 | you can create a custom walker by invoking `walk` as a function, and passing 69 | it a function which returns the descendants of a given node. E.g.: 70 | 71 | var walk = require('tree-walk'); 72 | var domWalker = walk(function(el) { 73 | return el.children; 74 | }); 75 | 76 | The resulting object has the same functions as `walk`, but parameterized 77 | to use the custom walking behavior: 78 | 79 | var buttons = domWalker.filter(walk.preorder, function(el) { 80 | return el.tagName === 'BUTTON'; 81 | }); 82 | 83 | However, it's not actually necessary to create custom walkers for DOM nodes -- 84 | walk handles DOM nodes specially by default. 85 | 86 | Parse Trees 87 | ----------- 88 | 89 | A _parse tree_ is tree that represents the syntactic structure of a formal 90 | language. For example, the arithmetic expression `1 + (4 + 2) * 7` might have the 91 | following parse tree: 92 | 93 | var tree = { 94 | 'type': 'Addition', 95 | 'left': { 'type': 'Value', 'value': 1 }, 96 | 'right': { 97 | 'type': 'Multiplication', 98 | 'left': { 99 | 'type': 'Addition', 100 | 'left': { 'type': 'Value', 'value': 4 }, 101 | 'right': { 'type': 'Value', 'value': 2 } 102 | }, 103 | 'right': { 'type': 'Value', 'value': 7 } 104 | } 105 | }; 106 | 107 | We can create a custom walker for this parse tree: 108 | 109 | var walk = require('tree-walk'); 110 | var parseTreeWalker = walk(function(node) { 111 | return _.pick(node, 'left', 'right'); 112 | }); 113 | 114 | Using the `find` function, we could find the first occurrence of the addition 115 | operator. It uses a pre-order traversal of the tree, so the following code 116 | will produce the root node (`tree`): 117 | 118 | parseTreeWalker.find(tree, function(node) { 119 | return node.type === 'Addition'; 120 | }); 121 | 122 | We could use the `reduce` function to evaluate the arithmetic expression 123 | represented by the tree. The following code will produce `43`: 124 | 125 | parseTreeWalker.reduce(tree, function(memo, node) { 126 | if (node.type === 'Value') return node.value; 127 | if (node.type === 'Addition') return memo.left + memo.right; 128 | if (node.type === 'Multiplication') return memo.left * memo.right; 129 | }); 130 | 131 | When the visitor function is called on a node, the `memo` argument contains 132 | the results of calling `reduce` on each of the node's subtrees. To evaluate a 133 | node, we just need to add or multiply the results of the left and right 134 | subtrees of the node. 135 | -------------------------------------------------------------------------------- /bin/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | npm run prepublish 4 | -------------------------------------------------------------------------------- /bin/release: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | npm run prepublish 6 | npm version "$@" -m "v%s" 7 | read -p "Press [Enter] to publish this release..." 8 | git push --tags 9 | npm publish 10 | -------------------------------------------------------------------------------- /dist/tree-walker-bundle.js: -------------------------------------------------------------------------------- 1 | !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.treeWalker=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 3 | // This software is distributed under the terms of the MIT License. 4 | 5 | 'use strict'; 6 | 7 | var extend = require('util-extend'), 8 | WeakMap = require('./third_party/WeakMap'); // eslint-disable-line no-undef,no-native-reassign 9 | 10 | // An internal object that can be returned from a visitor function to 11 | // prevent a top-down walk from walking subtrees of a node. 12 | var stopRecursion = {}; 13 | 14 | // An internal object that can be returned from a visitor function to 15 | // cause the walk to immediately stop. 16 | var stopWalk = {}; 17 | 18 | var hasOwnProp = Object.prototype.hasOwnProperty; 19 | 20 | // Helpers 21 | // ------- 22 | 23 | function isElement(obj) { 24 | return !!(obj && obj.nodeType === 1); 25 | } 26 | 27 | function isObject(obj) { 28 | var type = typeof obj; 29 | return type === 'function' || type === 'object' && !!obj; 30 | } 31 | 32 | function isString(obj) { 33 | return Object.prototype.toString.call(obj) === '[object String]'; 34 | } 35 | 36 | function each(obj, predicate) { 37 | for (var k in obj) { 38 | if (obj.hasOwnProperty(k)) { 39 | if (predicate(obj[k], k, obj)) 40 | return false; 41 | } 42 | } 43 | return true; 44 | } 45 | 46 | // Returns a copy of `obj` containing only the properties given by `keys`. 47 | function pick(obj, keys) { 48 | var result = {}; 49 | for (var i = 0, length = keys.length; i < length; i++) { 50 | var key = keys[i]; 51 | if (key in obj) result[key] = obj[key]; 52 | } 53 | return result; 54 | } 55 | 56 | // Makes a shallow copy of `arr`, and adds `obj` to the end of the copy. 57 | function copyAndPush(arr, obj) { 58 | var result = arr.slice(); 59 | result.push(obj); 60 | return result; 61 | } 62 | 63 | // Implements the default traversal strategy: if `obj` is a DOM node, walk 64 | // its DOM children; otherwise, walk all the objects it references. 65 | function defaultTraversal(obj) { 66 | return isElement(obj) ? obj.children : obj; 67 | } 68 | 69 | // Walk the tree recursively beginning with `root`, calling `beforeFunc` 70 | // before visiting an objects descendents, and `afterFunc` afterwards. 71 | // If `collectResults` is true, the last argument to `afterFunc` will be a 72 | // collection of the results of walking the node's subtrees. 73 | function walkImpl(root, traversalStrategy, beforeFunc, afterFunc, context, collectResults) { 74 | return (function _walk(stack, value, key, parent) { 75 | if (isObject(value) && stack.indexOf(value) >= 0) 76 | throw new TypeError('A cycle was detected at ' + value); 77 | 78 | if (beforeFunc) { 79 | var result = beforeFunc.call(context, value, key, parent); 80 | if (result === stopWalk) return stopWalk; 81 | if (result === stopRecursion) return; // eslint-disable-line consistent-return 82 | } 83 | 84 | var subResults; 85 | var target = traversalStrategy(value); 86 | 87 | if (isObject(target) && Object.keys(target).length > 0) { 88 | // Collect results from subtrees in the same shape as the target. 89 | if (collectResults) subResults = Array.isArray(target) ? [] : {}; 90 | 91 | var ok = each(target, function(obj, key) { 92 | var result = _walk(copyAndPush(stack, value), obj, key, value); 93 | if (result === stopWalk) return false; 94 | if (subResults) subResults[key] = result; 95 | }); 96 | if (!ok) return stopWalk; 97 | } 98 | if (afterFunc) return afterFunc.call(context, value, key, parent, subResults); 99 | })([], root); 100 | } 101 | 102 | // Internal helper providing the implementation for `pluck` and `pluckRec`. 103 | function pluck(obj, propertyName, recursive) { 104 | var results = []; 105 | this.preorder(obj, function(value, key) { 106 | if (!recursive && key === propertyName) 107 | return stopRecursion; 108 | if (hasOwnProp.call(value, propertyName)) 109 | results[results.length] = value[propertyName]; 110 | }); 111 | return results; 112 | } 113 | 114 | function defineEnumerableProperty(obj, propName, getterFn) { 115 | Object.defineProperty(obj, propName, { 116 | enumerable: true, 117 | get: getterFn 118 | }); 119 | } 120 | 121 | // Returns an object containing the walk functions. If `traversalStrategy` 122 | // is specified, it is a function determining how objects should be 123 | // traversed. Given an object, it returns the object to be recursively 124 | // walked. The default strategy is equivalent to `_.identity` for regular 125 | // objects, and for DOM nodes it returns the node's DOM children. 126 | function Walker(traversalStrategy) { 127 | if (!(this instanceof Walker)) 128 | return new Walker(traversalStrategy); 129 | 130 | // There are two different strategy shorthands: if a single string is 131 | // specified, treat the value of that property as the traversal target. 132 | // If an array is specified, the traversal target is the node itself, but 133 | // only the properties contained in the array will be traversed. 134 | if (isString(traversalStrategy)) { 135 | var prop = traversalStrategy; 136 | traversalStrategy = function(node) { 137 | if (isObject(node) && prop in node) return node[prop]; 138 | }; 139 | } else if (Array.isArray(traversalStrategy)) { 140 | var props = traversalStrategy; 141 | traversalStrategy = function(node) { 142 | if (isObject(node)) return pick(node, props); 143 | }; 144 | } 145 | this._traversalStrategy = traversalStrategy || defaultTraversal; 146 | } 147 | 148 | extend(Walker.prototype, { 149 | STOP_RECURSION: stopRecursion, 150 | 151 | // Performs a preorder traversal of `obj` and returns the first value 152 | // which passes a truth test. 153 | find: function(obj, visitor, context) { 154 | var result; 155 | this.preorder(obj, function(value, key, parent) { 156 | if (visitor.call(context, value, key, parent)) { 157 | result = value; 158 | return stopWalk; 159 | } 160 | }, context); 161 | return result; 162 | }, 163 | 164 | // Recursively traverses `obj` and returns all the elements that pass a 165 | // truth test. `strategy` is the traversal function to use, e.g. `preorder` 166 | // or `postorder`. 167 | filter: function(obj, strategy, visitor, context) { 168 | var results = []; 169 | if (obj === null) return results; 170 | strategy(obj, function(value, key, parent) { 171 | if (visitor.call(context, value, key, parent)) results.push(value); 172 | }, null, this._traversalStrategy); 173 | return results; 174 | }, 175 | 176 | // Recursively traverses `obj` and returns all the elements for which a 177 | // truth test fails. 178 | reject: function(obj, strategy, visitor, context) { 179 | return this.filter(obj, strategy, function(value, key, parent) { 180 | return !visitor.call(context, value, key, parent); 181 | }); 182 | }, 183 | 184 | // Produces a new array of values by recursively traversing `obj` and 185 | // mapping each value through the transformation function `visitor`. 186 | // `strategy` is the traversal function to use, e.g. `preorder` or 187 | // `postorder`. 188 | map: function(obj, strategy, visitor, context) { 189 | var results = []; 190 | strategy(obj, function(value, key, parent) { 191 | results[results.length] = visitor.call(context, value, key, parent); 192 | }, null, this._traversalStrategy); 193 | return results; 194 | }, 195 | 196 | // Return the value of properties named `propertyName` reachable from the 197 | // tree rooted at `obj`. Results are not recursively searched; use 198 | // `pluckRec` for that. 199 | pluck: function(obj, propertyName) { 200 | return pluck.call(this, obj, propertyName, false); 201 | }, 202 | 203 | // Version of `pluck` which recursively searches results for nested objects 204 | // with a property named `propertyName`. 205 | pluckRec: function(obj, propertyName) { 206 | return pluck.call(this, obj, propertyName, true); 207 | }, 208 | 209 | // Recursively traverses `obj` in a depth-first fashion, invoking the 210 | // `visitor` function for each object only after traversing its children. 211 | // `traversalStrategy` is intended for internal callers, and is not part 212 | // of the public API. 213 | postorder: function(obj, visitor, context, traversalStrategy) { 214 | traversalStrategy = traversalStrategy || this._traversalStrategy; 215 | walkImpl(obj, traversalStrategy, null, visitor, context); 216 | }, 217 | 218 | // Recursively traverses `obj` in a depth-first fashion, invoking the 219 | // `visitor` function for each object before traversing its children. 220 | // `traversalStrategy` is intended for internal callers, and is not part 221 | // of the public API. 222 | preorder: function(obj, visitor, context, traversalStrategy) { 223 | traversalStrategy = traversalStrategy || this._traversalStrategy; 224 | walkImpl(obj, traversalStrategy, visitor, null, context); 225 | }, 226 | 227 | // Builds up a single value by doing a post-order traversal of `obj` and 228 | // calling the `visitor` function on each object in the tree. For leaf 229 | // objects, the `memo` argument to `visitor` is the value of the `leafMemo` 230 | // argument to `reduce`. For non-leaf objects, `memo` is a collection of 231 | // the results of calling `reduce` on the object's children. 232 | reduce: function(obj, visitor, leafMemo, context) { 233 | var reducer = function(value, key, parent, subResults) { 234 | return visitor(subResults || leafMemo, value, key, parent); 235 | }; 236 | return walkImpl(obj, this._traversalStrategy, null, reducer, context, true); 237 | }, 238 | 239 | // An 'attribute' is a value that is calculated by invoking a visitor 240 | // function on a node. The first argument of the visitor is a collection 241 | // of the attribute values for the node's children. These are calculated 242 | // lazily -- in this way the visitor can decide in what order to visit the 243 | // subtrees. 244 | createAttribute: function(visitor, defaultValue, context) { 245 | var self = this; 246 | var memo = new WeakMap(); 247 | function _visit(stack, value, key, parent) { 248 | if (isObject(value) && stack.indexOf(value) >= 0) 249 | throw new TypeError('A cycle was detected at ' + value); 250 | 251 | if (memo.has(value)) 252 | return memo.get(value); 253 | 254 | var subResults; 255 | var target = self._traversalStrategy(value); 256 | if (isObject(target) && Object.keys(target).length > 0) { 257 | subResults = {}; 258 | each(target, function(child, k) { 259 | defineEnumerableProperty(subResults, k, function() { 260 | return _visit(copyAndPush(stack, value), child, k, value); 261 | }); 262 | }); 263 | } 264 | var result = visitor.call(context, subResults, value, key, parent); 265 | memo.set(value, result); 266 | return result; 267 | } 268 | return function(obj) { return _visit([], obj); }; 269 | } 270 | }); 271 | 272 | var WalkerProto = Walker.prototype; 273 | 274 | // Set up a few convenient aliases. 275 | WalkerProto.each = WalkerProto.preorder; 276 | WalkerProto.collect = WalkerProto.map; 277 | WalkerProto.detect = WalkerProto.find; 278 | WalkerProto.select = WalkerProto.filter; 279 | 280 | // Export the walker constructor, but make it behave like an instance. 281 | Walker._traversalStrategy = defaultTraversal; 282 | module.exports = extend(Walker, WalkerProto); 283 | 284 | },{"./third_party/WeakMap":3,"util-extend":2}],2:[function(require,module,exports){ 285 | // Copyright Joyent, Inc. and other Node contributors. 286 | // 287 | // Permission is hereby granted, free of charge, to any person obtaining a 288 | // copy of this software and associated documentation files (the 289 | // "Software"), to deal in the Software without restriction, including 290 | // without limitation the rights to use, copy, modify, merge, publish, 291 | // distribute, sublicense, and/or sell copies of the Software, and to permit 292 | // persons to whom the Software is furnished to do so, subject to the 293 | // following conditions: 294 | // 295 | // The above copyright notice and this permission notice shall be included 296 | // in all copies or substantial portions of the Software. 297 | // 298 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 299 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 300 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 301 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 302 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 303 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 304 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 305 | 306 | module.exports = extend; 307 | function extend(origin, add) { 308 | // Don't do anything if add isn't an object 309 | if (!add || typeof add !== 'object') return origin; 310 | 311 | var keys = Object.keys(add); 312 | var i = keys.length; 313 | while (i--) { 314 | origin[keys[i]] = add[keys[i]]; 315 | } 316 | return origin; 317 | } 318 | 319 | },{}],3:[function(require,module,exports){ 320 | /* 321 | * Copyright 2012 The Polymer Authors. All rights reserved. 322 | * Use of this source code is governed by a BSD-style 323 | * license that can be found in the LICENSE file. 324 | */ 325 | 326 | if (typeof WeakMap === 'undefined') { 327 | (function() { 328 | var defineProperty = Object.defineProperty; 329 | var counter = Date.now() % 1e9; 330 | 331 | var WeakMap = function() { 332 | this.name = '__st' + (Math.random() * 1e9 >>> 0) + (counter++ + '__'); 333 | }; 334 | 335 | WeakMap.prototype = { 336 | set: function(key, value) { 337 | var entry = key[this.name]; 338 | if (entry && entry[0] === key) 339 | entry[1] = value; 340 | else 341 | defineProperty(key, this.name, {value: [key, value], writable: true}); 342 | return this; 343 | }, 344 | get: function(key) { 345 | var entry; 346 | return (entry = key[this.name]) && entry[0] === key ? 347 | entry[1] : undefined; 348 | }, 349 | delete: function(key) { 350 | var entry = key[this.name]; 351 | if (!entry || entry[0] !== key) return false; 352 | entry[0] = entry[1] = undefined; 353 | return true; 354 | }, 355 | has: function(key) { 356 | var entry = key[this.name]; 357 | if (!entry) return false; 358 | return entry[0] === key; 359 | } 360 | }; 361 | 362 | module.exports = WeakMap; 363 | })(); 364 | } else { 365 | module.exports = WeakMap; 366 | } 367 | 368 | },{}]},{},[1])(1) 369 | }); 370 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCIvVXNlcnMvZHVicm95L2Rldi90cmVlLXdhbGsvaW5kZXguanMiLCIvVXNlcnMvZHVicm95L2Rldi90cmVlLXdhbGsvbm9kZV9tb2R1bGVzL3V0aWwtZXh0ZW5kL2V4dGVuZC5qcyIsIi9Vc2Vycy9kdWJyb3kvZGV2L3RyZWUtd2Fsay90aGlyZF9wYXJ0eS9XZWFrTWFwL2luZGV4LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FDQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pSQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNqQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsIi8vIENvcHlyaWdodCAoYykgMjAxNCBQYXRyaWNrIER1YnJveSA8cGR1YnJveUBnbWFpbC5jb20+XG4vLyBUaGlzIHNvZnR3YXJlIGlzIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgTUlUIExpY2Vuc2UuXG5cbid1c2Ugc3RyaWN0JztcblxudmFyIGV4dGVuZCA9IHJlcXVpcmUoJ3V0aWwtZXh0ZW5kJyksXG4gICAgV2Vha01hcCA9IHJlcXVpcmUoJy4vdGhpcmRfcGFydHkvV2Vha01hcCcpOyAgLy8gZXNsaW50LWRpc2FibGUtbGluZSBuby11bmRlZixuby1uYXRpdmUtcmVhc3NpZ25cblxuLy8gQW4gaW50ZXJuYWwgb2JqZWN0IHRoYXQgY2FuIGJlIHJldHVybmVkIGZyb20gYSB2aXNpdG9yIGZ1bmN0aW9uIHRvXG4vLyBwcmV2ZW50IGEgdG9wLWRvd24gd2FsayBmcm9tIHdhbGtpbmcgc3VidHJlZXMgb2YgYSBub2RlLlxudmFyIHN0b3BSZWN1cnNpb24gPSB7fTtcblxuLy8gQW4gaW50ZXJuYWwgb2JqZWN0IHRoYXQgY2FuIGJlIHJldHVybmVkIGZyb20gYSB2aXNpdG9yIGZ1bmN0aW9uIHRvXG4vLyBjYXVzZSB0aGUgd2FsayB0byBpbW1lZGlhdGVseSBzdG9wLlxudmFyIHN0b3BXYWxrID0ge307XG5cbnZhciBoYXNPd25Qcm9wID0gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eTtcblxuLy8gSGVscGVyc1xuLy8gLS0tLS0tLVxuXG5mdW5jdGlvbiBpc0VsZW1lbnQob2JqKSB7XG4gIHJldHVybiAhIShvYmogJiYgb2JqLm5vZGVUeXBlID09PSAxKTtcbn1cblxuZnVuY3Rpb24gaXNPYmplY3Qob2JqKSB7XG4gIHZhciB0eXBlID0gdHlwZW9mIG9iajtcbiAgcmV0dXJuIHR5cGUgPT09ICdmdW5jdGlvbicgfHwgdHlwZSA9PT0gJ29iamVjdCcgJiYgISFvYmo7XG59XG5cbmZ1bmN0aW9uIGlzU3RyaW5nKG9iaikge1xuICByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKG9iaikgPT09ICdbb2JqZWN0IFN0cmluZ10nO1xufVxuXG5mdW5jdGlvbiBlYWNoKG9iaiwgcHJlZGljYXRlKSB7XG4gIGZvciAodmFyIGsgaW4gb2JqKSB7XG4gICAgaWYgKG9iai5oYXNPd25Qcm9wZXJ0eShrKSkge1xuICAgICAgaWYgKHByZWRpY2F0ZShvYmpba10sIGssIG9iaikpXG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIHRydWU7XG59XG5cbi8vIFJldHVybnMgYSBjb3B5IG9mIGBvYmpgIGNvbnRhaW5pbmcgb25seSB0aGUgcHJvcGVydGllcyBnaXZlbiBieSBga2V5c2AuXG5mdW5jdGlvbiBwaWNrKG9iaiwga2V5cykge1xuICB2YXIgcmVzdWx0ID0ge307XG4gIGZvciAodmFyIGkgPSAwLCBsZW5ndGggPSBrZXlzLmxlbmd0aDsgaSA8IGxlbmd0aDsgaSsrKSB7XG4gICAgdmFyIGtleSA9IGtleXNbaV07XG4gICAgaWYgKGtleSBpbiBvYmopIHJlc3VsdFtrZXldID0gb2JqW2tleV07XG4gIH1cbiAgcmV0dXJuIHJlc3VsdDtcbn1cblxuLy8gTWFrZXMgYSBzaGFsbG93IGNvcHkgb2YgYGFycmAsIGFuZCBhZGRzIGBvYmpgIHRvIHRoZSBlbmQgb2YgdGhlIGNvcHkuXG5mdW5jdGlvbiBjb3B5QW5kUHVzaChhcnIsIG9iaikge1xuICB2YXIgcmVzdWx0ID0gYXJyLnNsaWNlKCk7XG4gIHJlc3VsdC5wdXNoKG9iaik7XG4gIHJldHVybiByZXN1bHQ7XG59XG5cbi8vIEltcGxlbWVudHMgdGhlIGRlZmF1bHQgdHJhdmVyc2FsIHN0cmF0ZWd5OiBpZiBgb2JqYCBpcyBhIERPTSBub2RlLCB3YWxrXG4vLyBpdHMgRE9NIGNoaWxkcmVuOyBvdGhlcndpc2UsIHdhbGsgYWxsIHRoZSBvYmplY3RzIGl0IHJlZmVyZW5jZXMuXG5mdW5jdGlvbiBkZWZhdWx0VHJhdmVyc2FsKG9iaikge1xuICByZXR1cm4gaXNFbGVtZW50KG9iaikgPyBvYmouY2hpbGRyZW4gOiBvYmo7XG59XG5cbi8vIFdhbGsgdGhlIHRyZWUgcmVjdXJzaXZlbHkgYmVnaW5uaW5nIHdpdGggYHJvb3RgLCBjYWxsaW5nIGBiZWZvcmVGdW5jYFxuLy8gYmVmb3JlIHZpc2l0aW5nIGFuIG9iamVjdHMgZGVzY2VuZGVudHMsIGFuZCBgYWZ0ZXJGdW5jYCBhZnRlcndhcmRzLlxuLy8gSWYgYGNvbGxlY3RSZXN1bHRzYCBpcyB0cnVlLCB0aGUgbGFzdCBhcmd1bWVudCB0byBgYWZ0ZXJGdW5jYCB3aWxsIGJlIGFcbi8vIGNvbGxlY3Rpb24gb2YgdGhlIHJlc3VsdHMgb2Ygd2Fsa2luZyB0aGUgbm9kZSdzIHN1YnRyZWVzLlxuZnVuY3Rpb24gd2Fsa0ltcGwocm9vdCwgdHJhdmVyc2FsU3RyYXRlZ3ksIGJlZm9yZUZ1bmMsIGFmdGVyRnVuYywgY29udGV4dCwgY29sbGVjdFJlc3VsdHMpIHtcbiAgcmV0dXJuIChmdW5jdGlvbiBfd2FsayhzdGFjaywgdmFsdWUsIGtleSwgcGFyZW50KSB7XG4gICAgaWYgKGlzT2JqZWN0KHZhbHVlKSAmJiBzdGFjay5pbmRleE9mKHZhbHVlKSA+PSAwKVxuICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignQSBjeWNsZSB3YXMgZGV0ZWN0ZWQgYXQgJyArIHZhbHVlKTtcblxuICAgIGlmIChiZWZvcmVGdW5jKSB7XG4gICAgICB2YXIgcmVzdWx0ID0gYmVmb3JlRnVuYy5jYWxsKGNvbnRleHQsIHZhbHVlLCBrZXksIHBhcmVudCk7XG4gICAgICBpZiAocmVzdWx0ID09PSBzdG9wV2FsaykgcmV0dXJuIHN0b3BXYWxrO1xuICAgICAgaWYgKHJlc3VsdCA9PT0gc3RvcFJlY3Vyc2lvbikgcmV0dXJuOyAgLy8gZXNsaW50LWRpc2FibGUtbGluZSBjb25zaXN0ZW50LXJldHVyblxuICAgIH1cblxuICAgIHZhciBzdWJSZXN1bHRzO1xuICAgIHZhciB0YXJnZXQgPSB0cmF2ZXJzYWxTdHJhdGVneSh2YWx1ZSk7XG5cbiAgICBpZiAoaXNPYmplY3QodGFyZ2V0KSAmJiBPYmplY3Qua2V5cyh0YXJnZXQpLmxlbmd0aCA+IDApIHtcbiAgICAgIC8vIENvbGxlY3QgcmVzdWx0cyBmcm9tIHN1YnRyZWVzIGluIHRoZSBzYW1lIHNoYXBlIGFzIHRoZSB0YXJnZXQuXG4gICAgICBpZiAoY29sbGVjdFJlc3VsdHMpIHN1YlJlc3VsdHMgPSBBcnJheS5pc0FycmF5KHRhcmdldCkgPyBbXSA6IHt9O1xuXG4gICAgICB2YXIgb2sgPSBlYWNoKHRhcmdldCwgZnVuY3Rpb24ob2JqLCBrZXkpIHtcbiAgICAgICAgdmFyIHJlc3VsdCA9IF93YWxrKGNvcHlBbmRQdXNoKHN0YWNrLCB2YWx1ZSksIG9iaiwga2V5LCB2YWx1ZSk7XG4gICAgICAgIGlmIChyZXN1bHQgPT09IHN0b3BXYWxrKSByZXR1cm4gZmFsc2U7XG4gICAgICAgIGlmIChzdWJSZXN1bHRzKSBzdWJSZXN1bHRzW2tleV0gPSByZXN1bHQ7XG4gICAgICB9KTtcbiAgICAgIGlmICghb2spIHJldHVybiBzdG9wV2FsaztcbiAgICB9XG4gICAgaWYgKGFmdGVyRnVuYykgcmV0dXJuIGFmdGVyRnVuYy5jYWxsKGNvbnRleHQsIHZhbHVlLCBrZXksIHBhcmVudCwgc3ViUmVzdWx0cyk7XG4gIH0pKFtdLCByb290KTtcbn1cblxuLy8gSW50ZXJuYWwgaGVscGVyIHByb3ZpZGluZyB0aGUgaW1wbGVtZW50YXRpb24gZm9yIGBwbHVja2AgYW5kIGBwbHVja1JlY2AuXG5mdW5jdGlvbiBwbHVjayhvYmosIHByb3BlcnR5TmFtZSwgcmVjdXJzaXZlKSB7XG4gIHZhciByZXN1bHRzID0gW107XG4gIHRoaXMucHJlb3JkZXIob2JqLCBmdW5jdGlvbih2YWx1ZSwga2V5KSB7XG4gICAgaWYgKCFyZWN1cnNpdmUgJiYga2V5ID09PSBwcm9wZXJ0eU5hbWUpXG4gICAgICByZXR1cm4gc3RvcFJlY3Vyc2lvbjtcbiAgICBpZiAoaGFzT3duUHJvcC5jYWxsKHZhbHVlLCBwcm9wZXJ0eU5hbWUpKVxuICAgICAgcmVzdWx0c1tyZXN1bHRzLmxlbmd0aF0gPSB2YWx1ZVtwcm9wZXJ0eU5hbWVdO1xuICB9KTtcbiAgcmV0dXJuIHJlc3VsdHM7XG59XG5cbmZ1bmN0aW9uIGRlZmluZUVudW1lcmFibGVQcm9wZXJ0eShvYmosIHByb3BOYW1lLCBnZXR0ZXJGbikge1xuICBPYmplY3QuZGVmaW5lUHJvcGVydHkob2JqLCBwcm9wTmFtZSwge1xuICAgIGVudW1lcmFibGU6IHRydWUsXG4gICAgZ2V0OiBnZXR0ZXJGblxuICB9KTtcbn1cblxuLy8gUmV0dXJucyBhbiBvYmplY3QgY29udGFpbmluZyB0aGUgd2FsayBmdW5jdGlvbnMuIElmIGB0cmF2ZXJzYWxTdHJhdGVneWBcbi8vIGlzIHNwZWNpZmllZCwgaXQgaXMgYSBmdW5jdGlvbiBkZXRlcm1pbmluZyBob3cgb2JqZWN0cyBzaG91bGQgYmVcbi8vIHRyYXZlcnNlZC4gR2l2ZW4gYW4gb2JqZWN0LCBpdCByZXR1cm5zIHRoZSBvYmplY3QgdG8gYmUgcmVjdXJzaXZlbHlcbi8vIHdhbGtlZC4gVGhlIGRlZmF1bHQgc3RyYXRlZ3kgaXMgZXF1aXZhbGVudCB0byBgXy5pZGVudGl0eWAgZm9yIHJlZ3VsYXJcbi8vIG9iamVjdHMsIGFuZCBmb3IgRE9NIG5vZGVzIGl0IHJldHVybnMgdGhlIG5vZGUncyBET00gY2hpbGRyZW4uXG5mdW5jdGlvbiBXYWxrZXIodHJhdmVyc2FsU3RyYXRlZ3kpIHtcbiAgaWYgKCEodGhpcyBpbnN0YW5jZW9mIFdhbGtlcikpXG4gICAgcmV0dXJuIG5ldyBXYWxrZXIodHJhdmVyc2FsU3RyYXRlZ3kpO1xuXG4gIC8vIFRoZXJlIGFyZSB0d28gZGlmZmVyZW50IHN0cmF0ZWd5IHNob3J0aGFuZHM6IGlmIGEgc2luZ2xlIHN0cmluZyBpc1xuICAvLyBzcGVjaWZpZWQsIHRyZWF0IHRoZSB2YWx1ZSBvZiB0aGF0IHByb3BlcnR5IGFzIHRoZSB0cmF2ZXJzYWwgdGFyZ2V0LlxuICAvLyBJZiBhbiBhcnJheSBpcyBzcGVjaWZpZWQsIHRoZSB0cmF2ZXJzYWwgdGFyZ2V0IGlzIHRoZSBub2RlIGl0c2VsZiwgYnV0XG4gIC8vIG9ubHkgdGhlIHByb3BlcnRpZXMgY29udGFpbmVkIGluIHRoZSBhcnJheSB3aWxsIGJlIHRyYXZlcnNlZC5cbiAgaWYgKGlzU3RyaW5nKHRyYXZlcnNhbFN0cmF0ZWd5KSkge1xuICAgIHZhciBwcm9wID0gdHJhdmVyc2FsU3RyYXRlZ3k7XG4gICAgdHJhdmVyc2FsU3RyYXRlZ3kgPSBmdW5jdGlvbihub2RlKSB7XG4gICAgICBpZiAoaXNPYmplY3Qobm9kZSkgJiYgcHJvcCBpbiBub2RlKSByZXR1cm4gbm9kZVtwcm9wXTtcbiAgICB9O1xuICB9IGVsc2UgaWYgKEFycmF5LmlzQXJyYXkodHJhdmVyc2FsU3RyYXRlZ3kpKSB7XG4gICAgdmFyIHByb3BzID0gdHJhdmVyc2FsU3RyYXRlZ3k7XG4gICAgdHJhdmVyc2FsU3RyYXRlZ3kgPSBmdW5jdGlvbihub2RlKSB7XG4gICAgICBpZiAoaXNPYmplY3Qobm9kZSkpIHJldHVybiBwaWNrKG5vZGUsIHByb3BzKTtcbiAgICB9O1xuICB9XG4gIHRoaXMuX3RyYXZlcnNhbFN0cmF0ZWd5ID0gdHJhdmVyc2FsU3RyYXRlZ3kgfHwgZGVmYXVsdFRyYXZlcnNhbDtcbn1cblxuZXh0ZW5kKFdhbGtlci5wcm90b3R5cGUsIHtcbiAgU1RPUF9SRUNVUlNJT046IHN0b3BSZWN1cnNpb24sXG5cbiAgLy8gUGVyZm9ybXMgYSBwcmVvcmRlciB0cmF2ZXJzYWwgb2YgYG9iamAgYW5kIHJldHVybnMgdGhlIGZpcnN0IHZhbHVlXG4gIC8vIHdoaWNoIHBhc3NlcyBhIHRydXRoIHRlc3QuXG4gIGZpbmQ6IGZ1bmN0aW9uKG9iaiwgdmlzaXRvciwgY29udGV4dCkge1xuICAgIHZhciByZXN1bHQ7XG4gICAgdGhpcy5wcmVvcmRlcihvYmosIGZ1bmN0aW9uKHZhbHVlLCBrZXksIHBhcmVudCkge1xuICAgICAgaWYgKHZpc2l0b3IuY2FsbChjb250ZXh0LCB2YWx1ZSwga2V5LCBwYXJlbnQpKSB7XG4gICAgICAgIHJlc3VsdCA9IHZhbHVlO1xuICAgICAgICByZXR1cm4gc3RvcFdhbGs7XG4gICAgICB9XG4gICAgfSwgY29udGV4dCk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfSxcblxuICAvLyBSZWN1cnNpdmVseSB0cmF2ZXJzZXMgYG9iamAgYW5kIHJldHVybnMgYWxsIHRoZSBlbGVtZW50cyB0aGF0IHBhc3MgYVxuICAvLyB0cnV0aCB0ZXN0LiBgc3RyYXRlZ3lgIGlzIHRoZSB0cmF2ZXJzYWwgZnVuY3Rpb24gdG8gdXNlLCBlLmcuIGBwcmVvcmRlcmBcbiAgLy8gb3IgYHBvc3RvcmRlcmAuXG4gIGZpbHRlcjogZnVuY3Rpb24ob2JqLCBzdHJhdGVneSwgdmlzaXRvciwgY29udGV4dCkge1xuICAgIHZhciByZXN1bHRzID0gW107XG4gICAgaWYgKG9iaiA9PT0gbnVsbCkgcmV0dXJuIHJlc3VsdHM7XG4gICAgc3RyYXRlZ3kob2JqLCBmdW5jdGlvbih2YWx1ZSwga2V5LCBwYXJlbnQpIHtcbiAgICAgIGlmICh2aXNpdG9yLmNhbGwoY29udGV4dCwgdmFsdWUsIGtleSwgcGFyZW50KSkgcmVzdWx0cy5wdXNoKHZhbHVlKTtcbiAgICB9LCBudWxsLCB0aGlzLl90cmF2ZXJzYWxTdHJhdGVneSk7XG4gICAgcmV0dXJuIHJlc3VsdHM7XG4gIH0sXG5cbiAgLy8gUmVjdXJzaXZlbHkgdHJhdmVyc2VzIGBvYmpgIGFuZCByZXR1cm5zIGFsbCB0aGUgZWxlbWVudHMgZm9yIHdoaWNoIGFcbiAgLy8gdHJ1dGggdGVzdCBmYWlscy5cbiAgcmVqZWN0OiBmdW5jdGlvbihvYmosIHN0cmF0ZWd5LCB2aXNpdG9yLCBjb250ZXh0KSB7XG4gICAgcmV0dXJuIHRoaXMuZmlsdGVyKG9iaiwgc3RyYXRlZ3ksIGZ1bmN0aW9uKHZhbHVlLCBrZXksIHBhcmVudCkge1xuICAgICAgcmV0dXJuICF2aXNpdG9yLmNhbGwoY29udGV4dCwgdmFsdWUsIGtleSwgcGFyZW50KTtcbiAgICB9KTtcbiAgfSxcblxuICAvLyBQcm9kdWNlcyBhIG5ldyBhcnJheSBvZiB2YWx1ZXMgYnkgcmVjdXJzaXZlbHkgdHJhdmVyc2luZyBgb2JqYCBhbmRcbiAgLy8gbWFwcGluZyBlYWNoIHZhbHVlIHRocm91Z2ggdGhlIHRyYW5zZm9ybWF0aW9uIGZ1bmN0aW9uIGB2aXNpdG9yYC5cbiAgLy8gYHN0cmF0ZWd5YCBpcyB0aGUgdHJhdmVyc2FsIGZ1bmN0aW9uIHRvIHVzZSwgZS5nLiBgcHJlb3JkZXJgIG9yXG4gIC8vIGBwb3N0b3JkZXJgLlxuICBtYXA6IGZ1bmN0aW9uKG9iaiwgc3RyYXRlZ3ksIHZpc2l0b3IsIGNvbnRleHQpIHtcbiAgICB2YXIgcmVzdWx0cyA9IFtdO1xuICAgIHN0cmF0ZWd5KG9iaiwgZnVuY3Rpb24odmFsdWUsIGtleSwgcGFyZW50KSB7XG4gICAgICByZXN1bHRzW3Jlc3VsdHMubGVuZ3RoXSA9IHZpc2l0b3IuY2FsbChjb250ZXh0LCB2YWx1ZSwga2V5LCBwYXJlbnQpO1xuICAgIH0sIG51bGwsIHRoaXMuX3RyYXZlcnNhbFN0cmF0ZWd5KTtcbiAgICByZXR1cm4gcmVzdWx0cztcbiAgfSxcblxuICAvLyBSZXR1cm4gdGhlIHZhbHVlIG9mIHByb3BlcnRpZXMgbmFtZWQgYHByb3BlcnR5TmFtZWAgcmVhY2hhYmxlIGZyb20gdGhlXG4gIC8vIHRyZWUgcm9vdGVkIGF0IGBvYmpgLiBSZXN1bHRzIGFyZSBub3QgcmVjdXJzaXZlbHkgc2VhcmNoZWQ7IHVzZVxuICAvLyBgcGx1Y2tSZWNgIGZvciB0aGF0LlxuICBwbHVjazogZnVuY3Rpb24ob2JqLCBwcm9wZXJ0eU5hbWUpIHtcbiAgICByZXR1cm4gcGx1Y2suY2FsbCh0aGlzLCBvYmosIHByb3BlcnR5TmFtZSwgZmFsc2UpO1xuICB9LFxuXG4gIC8vIFZlcnNpb24gb2YgYHBsdWNrYCB3aGljaCByZWN1cnNpdmVseSBzZWFyY2hlcyByZXN1bHRzIGZvciBuZXN0ZWQgb2JqZWN0c1xuICAvLyB3aXRoIGEgcHJvcGVydHkgbmFtZWQgYHByb3BlcnR5TmFtZWAuXG4gIHBsdWNrUmVjOiBmdW5jdGlvbihvYmosIHByb3BlcnR5TmFtZSkge1xuICAgIHJldHVybiBwbHVjay5jYWxsKHRoaXMsIG9iaiwgcHJvcGVydHlOYW1lLCB0cnVlKTtcbiAgfSxcblxuICAvLyBSZWN1cnNpdmVseSB0cmF2ZXJzZXMgYG9iamAgaW4gYSBkZXB0aC1maXJzdCBmYXNoaW9uLCBpbnZva2luZyB0aGVcbiAgLy8gYHZpc2l0b3JgIGZ1bmN0aW9uIGZvciBlYWNoIG9iamVjdCBvbmx5IGFmdGVyIHRyYXZlcnNpbmcgaXRzIGNoaWxkcmVuLlxuICAvLyBgdHJhdmVyc2FsU3RyYXRlZ3lgIGlzIGludGVuZGVkIGZvciBpbnRlcm5hbCBjYWxsZXJzLCBhbmQgaXMgbm90IHBhcnRcbiAgLy8gb2YgdGhlIHB1YmxpYyBBUEkuXG4gIHBvc3RvcmRlcjogZnVuY3Rpb24ob2JqLCB2aXNpdG9yLCBjb250ZXh0LCB0cmF2ZXJzYWxTdHJhdGVneSkge1xuICAgIHRyYXZlcnNhbFN0cmF0ZWd5ID0gdHJhdmVyc2FsU3RyYXRlZ3kgfHwgdGhpcy5fdHJhdmVyc2FsU3RyYXRlZ3k7XG4gICAgd2Fsa0ltcGwob2JqLCB0cmF2ZXJzYWxTdHJhdGVneSwgbnVsbCwgdmlzaXRvciwgY29udGV4dCk7XG4gIH0sXG5cbiAgLy8gUmVjdXJzaXZlbHkgdHJhdmVyc2VzIGBvYmpgIGluIGEgZGVwdGgtZmlyc3QgZmFzaGlvbiwgaW52b2tpbmcgdGhlXG4gIC8vIGB2aXNpdG9yYCBmdW5jdGlvbiBmb3IgZWFjaCBvYmplY3QgYmVmb3JlIHRyYXZlcnNpbmcgaXRzIGNoaWxkcmVuLlxuICAvLyBgdHJhdmVyc2FsU3RyYXRlZ3lgIGlzIGludGVuZGVkIGZvciBpbnRlcm5hbCBjYWxsZXJzLCBhbmQgaXMgbm90IHBhcnRcbiAgLy8gb2YgdGhlIHB1YmxpYyBBUEkuXG4gIHByZW9yZGVyOiBmdW5jdGlvbihvYmosIHZpc2l0b3IsIGNvbnRleHQsIHRyYXZlcnNhbFN0cmF0ZWd5KSB7XG4gICAgdHJhdmVyc2FsU3RyYXRlZ3kgPSB0cmF2ZXJzYWxTdHJhdGVneSB8fCB0aGlzLl90cmF2ZXJzYWxTdHJhdGVneTtcbiAgICB3YWxrSW1wbChvYmosIHRyYXZlcnNhbFN0cmF0ZWd5LCB2aXNpdG9yLCBudWxsLCBjb250ZXh0KTtcbiAgfSxcblxuICAvLyBCdWlsZHMgdXAgYSBzaW5nbGUgdmFsdWUgYnkgZG9pbmcgYSBwb3N0LW9yZGVyIHRyYXZlcnNhbCBvZiBgb2JqYCBhbmRcbiAgLy8gY2FsbGluZyB0aGUgYHZpc2l0b3JgIGZ1bmN0aW9uIG9uIGVhY2ggb2JqZWN0IGluIHRoZSB0cmVlLiBGb3IgbGVhZlxuICAvLyBvYmplY3RzLCB0aGUgYG1lbW9gIGFyZ3VtZW50IHRvIGB2aXNpdG9yYCBpcyB0aGUgdmFsdWUgb2YgdGhlIGBsZWFmTWVtb2BcbiAgLy8gYXJndW1lbnQgdG8gYHJlZHVjZWAuIEZvciBub24tbGVhZiBvYmplY3RzLCBgbWVtb2AgaXMgYSBjb2xsZWN0aW9uIG9mXG4gIC8vIHRoZSByZXN1bHRzIG9mIGNhbGxpbmcgYHJlZHVjZWAgb24gdGhlIG9iamVjdCdzIGNoaWxkcmVuLlxuICByZWR1Y2U6IGZ1bmN0aW9uKG9iaiwgdmlzaXRvciwgbGVhZk1lbW8sIGNvbnRleHQpIHtcbiAgICB2YXIgcmVkdWNlciA9IGZ1bmN0aW9uKHZhbHVlLCBrZXksIHBhcmVudCwgc3ViUmVzdWx0cykge1xuICAgICAgcmV0dXJuIHZpc2l0b3Ioc3ViUmVzdWx0cyB8fCBsZWFmTWVtbywgdmFsdWUsIGtleSwgcGFyZW50KTtcbiAgICB9O1xuICAgIHJldHVybiB3YWxrSW1wbChvYmosIHRoaXMuX3RyYXZlcnNhbFN0cmF0ZWd5LCBudWxsLCByZWR1Y2VyLCBjb250ZXh0LCB0cnVlKTtcbiAgfSxcblxuICAvLyBBbiAnYXR0cmlidXRlJyBpcyBhIHZhbHVlIHRoYXQgaXMgY2FsY3VsYXRlZCBieSBpbnZva2luZyBhIHZpc2l0b3JcbiAgLy8gZnVuY3Rpb24gb24gYSBub2RlLiBUaGUgZmlyc3QgYXJndW1lbnQgb2YgdGhlIHZpc2l0b3IgaXMgYSBjb2xsZWN0aW9uXG4gIC8vIG9mIHRoZSBhdHRyaWJ1dGUgdmFsdWVzIGZvciB0aGUgbm9kZSdzIGNoaWxkcmVuLiBUaGVzZSBhcmUgY2FsY3VsYXRlZFxuICAvLyBsYXppbHkgLS0gaW4gdGhpcyB3YXkgdGhlIHZpc2l0b3IgY2FuIGRlY2lkZSBpbiB3aGF0IG9yZGVyIHRvIHZpc2l0IHRoZVxuICAvLyBzdWJ0cmVlcy5cbiAgY3JlYXRlQXR0cmlidXRlOiBmdW5jdGlvbih2aXNpdG9yLCBkZWZhdWx0VmFsdWUsIGNvbnRleHQpIHtcbiAgICB2YXIgc2VsZiA9IHRoaXM7XG4gICAgdmFyIG1lbW8gPSBuZXcgV2Vha01hcCgpO1xuICAgIGZ1bmN0aW9uIF92aXNpdChzdGFjaywgdmFsdWUsIGtleSwgcGFyZW50KSB7XG4gICAgICBpZiAoaXNPYmplY3QodmFsdWUpICYmIHN0YWNrLmluZGV4T2YodmFsdWUpID49IDApXG4gICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ0EgY3ljbGUgd2FzIGRldGVjdGVkIGF0ICcgKyB2YWx1ZSk7XG5cbiAgICAgIGlmIChtZW1vLmhhcyh2YWx1ZSkpXG4gICAgICAgIHJldHVybiBtZW1vLmdldCh2YWx1ZSk7XG5cbiAgICAgIHZhciBzdWJSZXN1bHRzO1xuICAgICAgdmFyIHRhcmdldCA9IHNlbGYuX3RyYXZlcnNhbFN0cmF0ZWd5KHZhbHVlKTtcbiAgICAgIGlmIChpc09iamVjdCh0YXJnZXQpICYmIE9iamVjdC5rZXlzKHRhcmdldCkubGVuZ3RoID4gMCkge1xuICAgICAgICBzdWJSZXN1bHRzID0ge307XG4gICAgICAgIGVhY2godGFyZ2V0LCBmdW5jdGlvbihjaGlsZCwgaykge1xuICAgICAgICAgIGRlZmluZUVudW1lcmFibGVQcm9wZXJ0eShzdWJSZXN1bHRzLCBrLCBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHJldHVybiBfdmlzaXQoY29weUFuZFB1c2goc3RhY2ssIHZhbHVlKSwgY2hpbGQsIGssIHZhbHVlKTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgICB2YXIgcmVzdWx0ID0gdmlzaXRvci5jYWxsKGNvbnRleHQsIHN1YlJlc3VsdHMsIHZhbHVlLCBrZXksIHBhcmVudCk7XG4gICAgICBtZW1vLnNldCh2YWx1ZSwgcmVzdWx0KTtcbiAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfVxuICAgIHJldHVybiBmdW5jdGlvbihvYmopIHsgcmV0dXJuIF92aXNpdChbXSwgb2JqKTsgfTtcbiAgfVxufSk7XG5cbnZhciBXYWxrZXJQcm90byA9IFdhbGtlci5wcm90b3R5cGU7XG5cbi8vIFNldCB1cCBhIGZldyBjb252ZW5pZW50IGFsaWFzZXMuXG5XYWxrZXJQcm90by5lYWNoID0gV2Fsa2VyUHJvdG8ucHJlb3JkZXI7XG5XYWxrZXJQcm90by5jb2xsZWN0ID0gV2Fsa2VyUHJvdG8ubWFwO1xuV2Fsa2VyUHJvdG8uZGV0ZWN0ID0gV2Fsa2VyUHJvdG8uZmluZDtcbldhbGtlclByb3RvLnNlbGVjdCA9IFdhbGtlclByb3RvLmZpbHRlcjtcblxuLy8gRXhwb3J0IHRoZSB3YWxrZXIgY29uc3RydWN0b3IsIGJ1dCBtYWtlIGl0IGJlaGF2ZSBsaWtlIGFuIGluc3RhbmNlLlxuV2Fsa2VyLl90cmF2ZXJzYWxTdHJhdGVneSA9IGRlZmF1bHRUcmF2ZXJzYWw7XG5tb2R1bGUuZXhwb3J0cyA9IGV4dGVuZChXYWxrZXIsIFdhbGtlclByb3RvKTtcbiIsIi8vIENvcHlyaWdodCBKb3llbnQsIEluYy4gYW5kIG90aGVyIE5vZGUgY29udHJpYnV0b3JzLlxuLy9cbi8vIFBlcm1pc3Npb24gaXMgaGVyZWJ5IGdyYW50ZWQsIGZyZWUgb2YgY2hhcmdlLCB0byBhbnkgcGVyc29uIG9idGFpbmluZyBhXG4vLyBjb3B5IG9mIHRoaXMgc29mdHdhcmUgYW5kIGFzc29jaWF0ZWQgZG9jdW1lbnRhdGlvbiBmaWxlcyAodGhlXG4vLyBcIlNvZnR3YXJlXCIpLCB0byBkZWFsIGluIHRoZSBTb2Z0d2FyZSB3aXRob3V0IHJlc3RyaWN0aW9uLCBpbmNsdWRpbmdcbi8vIHdpdGhvdXQgbGltaXRhdGlvbiB0aGUgcmlnaHRzIHRvIHVzZSwgY29weSwgbW9kaWZ5LCBtZXJnZSwgcHVibGlzaCxcbi8vIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vciBzZWxsIGNvcGllcyBvZiB0aGUgU29mdHdhcmUsIGFuZCB0byBwZXJtaXRcbi8vIHBlcnNvbnMgdG8gd2hvbSB0aGUgU29mdHdhcmUgaXMgZnVybmlzaGVkIHRvIGRvIHNvLCBzdWJqZWN0IHRvIHRoZVxuLy8gZm9sbG93aW5nIGNvbmRpdGlvbnM6XG4vL1xuLy8gVGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UgYW5kIHRoaXMgcGVybWlzc2lvbiBub3RpY2Ugc2hhbGwgYmUgaW5jbHVkZWRcbi8vIGluIGFsbCBjb3BpZXMgb3Igc3Vic3RhbnRpYWwgcG9ydGlvbnMgb2YgdGhlIFNvZnR3YXJlLlxuLy9cbi8vIFRIRSBTT0ZUV0FSRSBJUyBQUk9WSURFRCBcIkFTIElTXCIsIFdJVEhPVVQgV0FSUkFOVFkgT0YgQU5ZIEtJTkQsIEVYUFJFU1Ncbi8vIE9SIElNUExJRUQsIElOQ0xVRElORyBCVVQgTk9UIExJTUlURUQgVE8gVEhFIFdBUlJBTlRJRVMgT0Zcbi8vIE1FUkNIQU5UQUJJTElUWSwgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQU5EIE5PTklORlJJTkdFTUVOVC4gSU5cbi8vIE5PIEVWRU5UIFNIQUxMIFRIRSBBVVRIT1JTIE9SIENPUFlSSUdIVCBIT0xERVJTIEJFIExJQUJMRSBGT1IgQU5ZIENMQUlNLFxuLy8gREFNQUdFUyBPUiBPVEhFUiBMSUFCSUxJVFksIFdIRVRIRVIgSU4gQU4gQUNUSU9OIE9GIENPTlRSQUNULCBUT1JUIE9SXG4vLyBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSwgT1VUIE9GIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUgU09GVFdBUkUgT1IgVEhFXG4vLyBVU0UgT1IgT1RIRVIgREVBTElOR1MgSU4gVEhFIFNPRlRXQVJFLlxuXG5tb2R1bGUuZXhwb3J0cyA9IGV4dGVuZDtcbmZ1bmN0aW9uIGV4dGVuZChvcmlnaW4sIGFkZCkge1xuICAvLyBEb24ndCBkbyBhbnl0aGluZyBpZiBhZGQgaXNuJ3QgYW4gb2JqZWN0XG4gIGlmICghYWRkIHx8IHR5cGVvZiBhZGQgIT09ICdvYmplY3QnKSByZXR1cm4gb3JpZ2luO1xuXG4gIHZhciBrZXlzID0gT2JqZWN0LmtleXMoYWRkKTtcbiAgdmFyIGkgPSBrZXlzLmxlbmd0aDtcbiAgd2hpbGUgKGktLSkge1xuICAgIG9yaWdpbltrZXlzW2ldXSA9IGFkZFtrZXlzW2ldXTtcbiAgfVxuICByZXR1cm4gb3JpZ2luO1xufVxuIiwiLypcbiAqIENvcHlyaWdodCAyMDEyIFRoZSBQb2x5bWVyIEF1dGhvcnMuIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiBVc2Ugb2YgdGhpcyBzb3VyY2UgY29kZSBpcyBnb3Zlcm5lZCBieSBhIEJTRC1zdHlsZVxuICogbGljZW5zZSB0aGF0IGNhbiBiZSBmb3VuZCBpbiB0aGUgTElDRU5TRSBmaWxlLlxuICovXG5cbmlmICh0eXBlb2YgV2Vha01hcCA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgKGZ1bmN0aW9uKCkge1xuICAgIHZhciBkZWZpbmVQcm9wZXJ0eSA9IE9iamVjdC5kZWZpbmVQcm9wZXJ0eTtcbiAgICB2YXIgY291bnRlciA9IERhdGUubm93KCkgJSAxZTk7XG5cbiAgICB2YXIgV2Vha01hcCA9IGZ1bmN0aW9uKCkge1xuICAgICAgdGhpcy5uYW1lID0gJ19fc3QnICsgKE1hdGgucmFuZG9tKCkgKiAxZTkgPj4+IDApICsgKGNvdW50ZXIrKyArICdfXycpO1xuICAgIH07XG5cbiAgICBXZWFrTWFwLnByb3RvdHlwZSA9IHtcbiAgICAgIHNldDogZnVuY3Rpb24oa2V5LCB2YWx1ZSkge1xuICAgICAgICB2YXIgZW50cnkgPSBrZXlbdGhpcy5uYW1lXTtcbiAgICAgICAgaWYgKGVudHJ5ICYmIGVudHJ5WzBdID09PSBrZXkpXG4gICAgICAgICAgZW50cnlbMV0gPSB2YWx1ZTtcbiAgICAgICAgZWxzZVxuICAgICAgICAgIGRlZmluZVByb3BlcnR5KGtleSwgdGhpcy5uYW1lLCB7dmFsdWU6IFtrZXksIHZhbHVlXSwgd3JpdGFibGU6IHRydWV9KTtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgICB9LFxuICAgICAgZ2V0OiBmdW5jdGlvbihrZXkpIHtcbiAgICAgICAgdmFyIGVudHJ5O1xuICAgICAgICByZXR1cm4gKGVudHJ5ID0ga2V5W3RoaXMubmFtZV0pICYmIGVudHJ5WzBdID09PSBrZXkgP1xuICAgICAgICAgICAgZW50cnlbMV0gOiB1bmRlZmluZWQ7XG4gICAgICB9LFxuICAgICAgZGVsZXRlOiBmdW5jdGlvbihrZXkpIHtcbiAgICAgICAgdmFyIGVudHJ5ID0ga2V5W3RoaXMubmFtZV07XG4gICAgICAgIGlmICghZW50cnkgfHwgZW50cnlbMF0gIT09IGtleSkgcmV0dXJuIGZhbHNlO1xuICAgICAgICBlbnRyeVswXSA9IGVudHJ5WzFdID0gdW5kZWZpbmVkO1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgIH0sXG4gICAgICBoYXM6IGZ1bmN0aW9uKGtleSkge1xuICAgICAgICB2YXIgZW50cnkgPSBrZXlbdGhpcy5uYW1lXTtcbiAgICAgICAgaWYgKCFlbnRyeSkgcmV0dXJuIGZhbHNlO1xuICAgICAgICByZXR1cm4gZW50cnlbMF0gPT09IGtleTtcbiAgICAgIH1cbiAgICB9O1xuXG4gICAgbW9kdWxlLmV4cG9ydHMgPSBXZWFrTWFwO1xuICB9KSgpO1xufSBlbHNlIHtcbiAgbW9kdWxlLmV4cG9ydHMgPSBXZWFrTWFwO1xufVxuIl19 371 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 Patrick Dubroy 2 | // This software is distributed under the terms of the MIT License. 3 | 4 | 'use strict'; 5 | 6 | var extend = require('util-extend'), 7 | WeakMap = require('./third_party/WeakMap'); // eslint-disable-line no-undef,no-native-reassign 8 | 9 | // An internal object that can be returned from a visitor function to 10 | // prevent a top-down walk from walking subtrees of a node. 11 | var stopRecursion = {}; 12 | 13 | // An internal object that can be returned from a visitor function to 14 | // cause the walk to immediately stop. 15 | var stopWalk = {}; 16 | 17 | var hasOwnProp = Object.prototype.hasOwnProperty; 18 | 19 | // Helpers 20 | // ------- 21 | 22 | function isElement(obj) { 23 | return !!(obj && obj.nodeType === 1); 24 | } 25 | 26 | function isObject(obj) { 27 | var type = typeof obj; 28 | return type === 'function' || type === 'object' && !!obj; 29 | } 30 | 31 | function isString(obj) { 32 | return Object.prototype.toString.call(obj) === '[object String]'; 33 | } 34 | 35 | function each(obj, predicate) { 36 | for (var k in obj) { 37 | if (obj.hasOwnProperty(k)) { 38 | if (predicate(obj[k], k, obj)) 39 | return false; 40 | } 41 | } 42 | return true; 43 | } 44 | 45 | // Returns a copy of `obj` containing only the properties given by `keys`. 46 | function pick(obj, keys) { 47 | var result = {}; 48 | for (var i = 0, length = keys.length; i < length; i++) { 49 | var key = keys[i]; 50 | if (key in obj) result[key] = obj[key]; 51 | } 52 | return result; 53 | } 54 | 55 | // Makes a shallow copy of `arr`, and adds `obj` to the end of the copy. 56 | function copyAndPush(arr, obj) { 57 | var result = arr.slice(); 58 | result.push(obj); 59 | return result; 60 | } 61 | 62 | // Implements the default traversal strategy: if `obj` is a DOM node, walk 63 | // its DOM children; otherwise, walk all the objects it references. 64 | function defaultTraversal(obj) { 65 | return isElement(obj) ? obj.children : obj; 66 | } 67 | 68 | // Walk the tree recursively beginning with `root`, calling `beforeFunc` 69 | // before visiting an objects descendents, and `afterFunc` afterwards. 70 | // If `collectResults` is true, the last argument to `afterFunc` will be a 71 | // collection of the results of walking the node's subtrees. 72 | function walkImpl(root, traversalStrategy, beforeFunc, afterFunc, context, collectResults) { 73 | return (function _walk(stack, value, key, parent) { 74 | if (isObject(value) && stack.indexOf(value) >= 0) 75 | throw new TypeError('A cycle was detected at ' + value); 76 | 77 | if (beforeFunc) { 78 | var result = beforeFunc.call(context, value, key, parent); 79 | if (result === stopWalk) return stopWalk; 80 | if (result === stopRecursion) return; // eslint-disable-line consistent-return 81 | } 82 | 83 | var subResults; 84 | var target = traversalStrategy(value); 85 | 86 | if (isObject(target) && Object.keys(target).length > 0) { 87 | // Collect results from subtrees in the same shape as the target. 88 | if (collectResults) subResults = Array.isArray(target) ? [] : {}; 89 | 90 | var ok = each(target, function(obj, key) { 91 | var result = _walk(copyAndPush(stack, value), obj, key, value); 92 | if (result === stopWalk) return false; 93 | if (subResults) subResults[key] = result; 94 | }); 95 | if (!ok) return stopWalk; 96 | } 97 | if (afterFunc) return afterFunc.call(context, value, key, parent, subResults); 98 | })([], root); 99 | } 100 | 101 | // Internal helper providing the implementation for `pluck` and `pluckRec`. 102 | function pluck(obj, propertyName, recursive) { 103 | var results = []; 104 | this.preorder(obj, function(value, key) { 105 | if (!recursive && key === propertyName) 106 | return stopRecursion; 107 | if (hasOwnProp.call(value, propertyName)) 108 | results[results.length] = value[propertyName]; 109 | }); 110 | return results; 111 | } 112 | 113 | function defineEnumerableProperty(obj, propName, getterFn) { 114 | Object.defineProperty(obj, propName, { 115 | enumerable: true, 116 | get: getterFn 117 | }); 118 | } 119 | 120 | // Returns an object containing the walk functions. If `traversalStrategy` 121 | // is specified, it is a function determining how objects should be 122 | // traversed. Given an object, it returns the object to be recursively 123 | // walked. The default strategy is equivalent to `_.identity` for regular 124 | // objects, and for DOM nodes it returns the node's DOM children. 125 | function Walker(traversalStrategy) { 126 | if (!(this instanceof Walker)) 127 | return new Walker(traversalStrategy); 128 | 129 | // There are two different strategy shorthands: if a single string is 130 | // specified, treat the value of that property as the traversal target. 131 | // If an array is specified, the traversal target is the node itself, but 132 | // only the properties contained in the array will be traversed. 133 | if (isString(traversalStrategy)) { 134 | var prop = traversalStrategy; 135 | traversalStrategy = function(node) { 136 | if (isObject(node) && prop in node) return node[prop]; 137 | }; 138 | } else if (Array.isArray(traversalStrategy)) { 139 | var props = traversalStrategy; 140 | traversalStrategy = function(node) { 141 | if (isObject(node)) return pick(node, props); 142 | }; 143 | } 144 | this._traversalStrategy = traversalStrategy || defaultTraversal; 145 | } 146 | 147 | extend(Walker.prototype, { 148 | STOP_RECURSION: stopRecursion, 149 | 150 | // Performs a preorder traversal of `obj` and returns the first value 151 | // which passes a truth test. 152 | find: function(obj, visitor, context) { 153 | var result; 154 | this.preorder(obj, function(value, key, parent) { 155 | if (visitor.call(context, value, key, parent)) { 156 | result = value; 157 | return stopWalk; 158 | } 159 | }, context); 160 | return result; 161 | }, 162 | 163 | // Recursively traverses `obj` and returns all the elements that pass a 164 | // truth test. `strategy` is the traversal function to use, e.g. `preorder` 165 | // or `postorder`. 166 | filter: function(obj, strategy, visitor, context) { 167 | var results = []; 168 | if (obj === null) return results; 169 | strategy(obj, function(value, key, parent) { 170 | if (visitor.call(context, value, key, parent)) results.push(value); 171 | }, null, this._traversalStrategy); 172 | return results; 173 | }, 174 | 175 | // Recursively traverses `obj` and returns all the elements for which a 176 | // truth test fails. 177 | reject: function(obj, strategy, visitor, context) { 178 | return this.filter(obj, strategy, function(value, key, parent) { 179 | return !visitor.call(context, value, key, parent); 180 | }); 181 | }, 182 | 183 | // Produces a new array of values by recursively traversing `obj` and 184 | // mapping each value through the transformation function `visitor`. 185 | // `strategy` is the traversal function to use, e.g. `preorder` or 186 | // `postorder`. 187 | map: function(obj, strategy, visitor, context) { 188 | var results = []; 189 | strategy(obj, function(value, key, parent) { 190 | results[results.length] = visitor.call(context, value, key, parent); 191 | }, null, this._traversalStrategy); 192 | return results; 193 | }, 194 | 195 | // Return the value of properties named `propertyName` reachable from the 196 | // tree rooted at `obj`. Results are not recursively searched; use 197 | // `pluckRec` for that. 198 | pluck: function(obj, propertyName) { 199 | return pluck.call(this, obj, propertyName, false); 200 | }, 201 | 202 | // Version of `pluck` which recursively searches results for nested objects 203 | // with a property named `propertyName`. 204 | pluckRec: function(obj, propertyName) { 205 | return pluck.call(this, obj, propertyName, true); 206 | }, 207 | 208 | // Recursively traverses `obj` in a depth-first fashion, invoking the 209 | // `visitor` function for each object only after traversing its children. 210 | // `traversalStrategy` is intended for internal callers, and is not part 211 | // of the public API. 212 | postorder: function(obj, visitor, context, traversalStrategy) { 213 | traversalStrategy = traversalStrategy || this._traversalStrategy; 214 | walkImpl(obj, traversalStrategy, null, visitor, context); 215 | }, 216 | 217 | // Recursively traverses `obj` in a depth-first fashion, invoking the 218 | // `visitor` function for each object before traversing its children. 219 | // `traversalStrategy` is intended for internal callers, and is not part 220 | // of the public API. 221 | preorder: function(obj, visitor, context, traversalStrategy) { 222 | traversalStrategy = traversalStrategy || this._traversalStrategy; 223 | walkImpl(obj, traversalStrategy, visitor, null, context); 224 | }, 225 | 226 | // Builds up a single value by doing a post-order traversal of `obj` and 227 | // calling the `visitor` function on each object in the tree. For leaf 228 | // objects, the `memo` argument to `visitor` is the value of the `leafMemo` 229 | // argument to `reduce`. For non-leaf objects, `memo` is a collection of 230 | // the results of calling `reduce` on the object's children. 231 | reduce: function(obj, visitor, leafMemo, context) { 232 | var reducer = function(value, key, parent, subResults) { 233 | return visitor(subResults || leafMemo, value, key, parent); 234 | }; 235 | return walkImpl(obj, this._traversalStrategy, null, reducer, context, true); 236 | }, 237 | 238 | // An 'attribute' is a value that is calculated by invoking a visitor 239 | // function on a node. The first argument of the visitor is a collection 240 | // of the attribute values for the node's children. These are calculated 241 | // lazily -- in this way the visitor can decide in what order to visit the 242 | // subtrees. 243 | createAttribute: function(visitor, defaultValue, context) { 244 | var self = this; 245 | var memo = new WeakMap(); 246 | function _visit(stack, value, key, parent) { 247 | if (isObject(value) && stack.indexOf(value) >= 0) 248 | throw new TypeError('A cycle was detected at ' + value); 249 | 250 | if (memo.has(value)) 251 | return memo.get(value); 252 | 253 | var subResults; 254 | var target = self._traversalStrategy(value); 255 | if (isObject(target) && Object.keys(target).length > 0) { 256 | subResults = {}; 257 | each(target, function(child, k) { 258 | defineEnumerableProperty(subResults, k, function() { 259 | return _visit(copyAndPush(stack, value), child, k, value); 260 | }); 261 | }); 262 | } 263 | var result = visitor.call(context, subResults, value, key, parent); 264 | memo.set(value, result); 265 | return result; 266 | } 267 | return function(obj) { return _visit([], obj); }; 268 | } 269 | }); 270 | 271 | var WalkerProto = Walker.prototype; 272 | 273 | // Set up a few convenient aliases. 274 | WalkerProto.each = WalkerProto.preorder; 275 | WalkerProto.collect = WalkerProto.map; 276 | WalkerProto.detect = WalkerProto.find; 277 | WalkerProto.select = WalkerProto.filter; 278 | 279 | // Export the walker constructor, but make it behave like an instance. 280 | Walker._traversalStrategy = defaultTraversal; 281 | module.exports = extend(Walker, WalkerProto); 282 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tree-walk", 3 | "version": "0.3.0", 4 | "author": "Patrick Dubroy (http://dubroy.com)", 5 | "license": "MIT", 6 | "dependencies": { 7 | "util-extend": "^1.0.1" 8 | }, 9 | "devDependencies": { 10 | "browserify": "^5.9.1", 11 | "dirwatch": "^1.1.0", 12 | "eslint": "^0.17.1", 13 | "tap-spec": "^1.0.1", 14 | "tape": "^2.13.4", 15 | "underscore": "^1.8.2", 16 | "watchify": "^1.0.1" 17 | }, 18 | "scripts": { 19 | "build": "browserify -d -s tree-walker -o dist/tree-walker-bundle.js index.js", 20 | "watch": "watchify -v -d -s tree-walker -o dist/tree-walker-bundle.js index.js", 21 | "test": "tape test/*.js | tap-spec", 22 | "test-continuous": "dirwatch *.js test/*.js -c 'which tput && tput clear; date; tape test/*.js' | tap-spec", 23 | "prepublish": "npm run build && npm run test && npm run lint", 24 | "lint": "eslint *.js lib test" 25 | }, 26 | "description": "Helpers for traversing, inspecting, and transforming arbitrary trees.", 27 | "main": "index.js", 28 | "directories": { 29 | "test": "test" 30 | }, 31 | "repository": { 32 | "type": "git", 33 | "url": "https://github.com/pdubroy/tree-walk.git" 34 | }, 35 | "keywords": [ 36 | "traverse", 37 | "walk", 38 | "tree", 39 | "recursive" 40 | ], 41 | "bugs": { 42 | "url": "https://github.com/pdubroy/tree-walk/issues" 43 | }, 44 | "homepage": "https://github.com/pdubroy/tree-walk" 45 | } 46 | -------------------------------------------------------------------------------- /test/test-tree-walk.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* global document */ 4 | 5 | var test = require('tape'), 6 | _ = require('underscore'); 7 | 8 | var walk = require('../'); 9 | 10 | function getSimpleTestTree() { 11 | return { 12 | val: 0, 13 | l: { val: 1, l: { val: 2 }, r: { val: 3 } }, 14 | r: { val: 4, l: { val: 5 }, r: { val: 6 } } 15 | }; 16 | } 17 | 18 | function getMixedTestTree() { 19 | return { 20 | current: 21 | { city: 'Munich', aliases: ['Muenchen'], population: 1378000 }, 22 | previous: [ 23 | { city: 'San Francisco', aliases: ['SF', 'San Fran'], population: 812826 }, 24 | { city: 'Toronto', aliases: ['TO', 'T-dot'], population: 2615000 } 25 | ] 26 | }; 27 | } 28 | 29 | function getVal(node) { 30 | return node.val; 31 | } 32 | 33 | test('basic', function(t) { 34 | // Updates the value of `node` to be the sum of the values of its subtrees. 35 | // Ignores leaf nodes. 36 | var visitor = function(node) { 37 | if (node.l && node.r) 38 | node.val = node.l.val + node.r.val; 39 | }; 40 | 41 | var tree = getSimpleTestTree(); 42 | walk.postorder(tree, visitor); 43 | t.equal(tree.val, 16, 'should visit subtrees first'); 44 | 45 | tree = getSimpleTestTree(); 46 | walk.preorder(tree, visitor); 47 | t.equal(tree.val, 5, 'should visit subtrees after the node itself'); 48 | 49 | var f = function(x, y) {}; 50 | var visited = walk.map(f, walk.preorder, _.identity); 51 | t.deepEquals(visited, [f], 'function w/ no properties is treated as a leaf'); 52 | 53 | f.foo = 3; 54 | visited = walk.map(f, walk.preorder, _.identity); 55 | t.deepEqual(visited, [f, 3], 'own property of function is be visited'); 56 | 57 | (function(x) { 58 | visited = walk.map(arguments, walk.preorder, _.identity); 59 | })('x', 99); 60 | t.equal(visited.length, 3); 61 | t.deepEqual(visited.slice(1), ['x', 99], 'arguments is treated like an array'); 62 | 63 | t.end(); 64 | }); 65 | 66 | test('circularRefs', function(t) { 67 | var tree = getSimpleTestTree(); 68 | tree.l.l.r = tree; 69 | t.throws(function() { walk.preorder(tree, _.identity); }, /cycle/, 'preorder t.throws an exception'); 70 | t.throws(function() { walk.postorder(tree, _.identity); }, /cycle/, 'postorder t.throws an exception'); 71 | 72 | tree = getSimpleTestTree(); 73 | tree.r.l = tree.r; 74 | t.throws(function() { walk.preorder(tree, _.identity); }, /cycle/, 'exception for a self-referencing node'); 75 | 76 | tree = getSimpleTestTree(); 77 | tree.l.l = tree.l.r = {}; 78 | t.ok(walk.reduce(tree, function() { return true; }), "same object can be in diff't branches"); 79 | 80 | tree.l.l = tree.l.r = 'hai'; 81 | t.ok(walk.reduce(tree, function() { return true; }), "same string can be in diff't branches"); 82 | 83 | // Create a custom walker that reuses the same object as the walk target. 84 | // This should not be considered a cycle. 85 | var wrapper = []; 86 | var walker = walk(function(node, key) { 87 | if (node && node.hasOwnProperty('val')) { 88 | wrapper[0] = node.l; 89 | wrapper[1] = node.r; 90 | return wrapper; 91 | } 92 | return node; 93 | }); 94 | t.ok(walker.reduce(tree, function() { return true; }), 'target object can be the same'); 95 | 96 | t.end(); 97 | }); 98 | 99 | test('simpleMap', function(t) { 100 | var visitor = function(node, key) { 101 | if (_.has(node, 'val')) return node.val; 102 | if (key !== 'val') throw new Error('Leaf node with incorrect key'); 103 | return this && this.leafChar || '-'; 104 | }; 105 | var visited = walk.map(getSimpleTestTree(), walk.preorder, visitor).join(''); 106 | t.equal(visited, '0-1-2-3-4-5-6-', 'pre-order map'); 107 | 108 | visited = walk.map(getSimpleTestTree(), walk.postorder, visitor).join(''); 109 | t.equal(visited, '---2-31--5-640', 'post-order map'); 110 | 111 | var context = { leafChar: '*' }; 112 | visited = walk.map(getSimpleTestTree(), walk.preorder, visitor, context).join(''); 113 | t.equal(visited, '0*1*2*3*4*5*6*', 'pre-order with context'); 114 | 115 | visited = walk.map(getSimpleTestTree(), walk.postorder, visitor, context).join(''); 116 | t.equal(visited, '***2*31**5*640', 'post-order with context'); 117 | 118 | if (typeof document !== 'undefined') { 119 | var root = document.querySelector('#map-test'); 120 | var ids = walk.map(root, walk.preorder, function(el) { return el.id; }); 121 | t.deepEqual(ids, ['map-test', 'id1', 'id2'], 'preorder map with DOM elements'); 122 | 123 | ids = walk.map(root, walk.postorder, function(el) { return el.id; }); 124 | t.deepEqual(ids, ['id1', 'id2', 'map-test'], 'postorder map with DOM elements'); 125 | } 126 | 127 | t.end(); 128 | }); 129 | 130 | test('mixedMap', function(t) { 131 | var visitor = function(node) { 132 | return _.isString(node) ? node.toLowerCase() : null; 133 | }; 134 | 135 | var tree = getMixedTestTree(); 136 | var preorderResult = walk.map(tree, walk.preorder, visitor); 137 | t.equal(preorderResult.length, 19, 'all nodes are visited'); 138 | t.deepEqual(_.reject(preorderResult, _.isNull), 139 | ['munich', 'muenchen', 'san francisco', 'sf', 'san fran', 'toronto', 'to', 't-dot'], 140 | 'pre-order map on a mixed tree'); 141 | 142 | var postorderResult = walk.map(tree, walk.postorder, visitor); 143 | t.deepEqual(preorderResult.sort(), postorderResult.sort(), 'post-order map on a mixed tree'); 144 | 145 | tree = [['foo'], tree]; 146 | var result = walk.map(tree, walk.postorder, visitor); 147 | t.deepEqual(_.difference(result, postorderResult), ['foo'], 'map on list of trees'); 148 | 149 | t.end(); 150 | }); 151 | 152 | test('pluck', function(t) { 153 | var tree = getSimpleTestTree(); 154 | tree.val = { val: 'z' }; 155 | 156 | var plucked = walk.pluckRec(tree, 'val'); 157 | t.equal(plucked.shift(), tree.val); 158 | t.equal(plucked.join(''), 'z123456', 'pluckRec is recursive'); 159 | 160 | plucked = walk.pluck(tree, 'val'); 161 | t.equal(plucked.shift(), tree.val); 162 | t.equal(plucked.join(''), '123456', 'regular pluck is not recursive'); 163 | 164 | tree.l.r.foo = 42; 165 | t.equal(walk.pluck(tree, 'foo').shift(), 42, 'pluck a value from deep in the tree'); 166 | 167 | tree = getMixedTestTree(); 168 | t.deepEqual(walk.pluck(tree, 'city'), ['Munich', 'San Francisco', 'Toronto'], 'pluck from a mixed tree'); 169 | tree = [tree, { city: 'Loserville', population: 'you' }]; 170 | t.deepEqual(walk.pluck(tree, 'population'), [1378000, 812826, 2615000, 'you'], 'pluck from a list of trees'); 171 | 172 | t.end(); 173 | }); 174 | 175 | test('reduce', function(t) { 176 | var add = function(a, b) { return a + b; }; 177 | var leafMemo = []; 178 | var sum = function(memo, node) { 179 | if (_.isObject(node)) 180 | return _.reduce(memo, add, 0); 181 | 182 | t.strictEqual(memo, leafMemo); 183 | return node; 184 | }; 185 | var tree = getSimpleTestTree(); 186 | t.equal(walk.reduce(tree, sum, leafMemo), 21); 187 | 188 | // A more useful example: transforming a tree. 189 | 190 | // Returns a new node where the left and right subtrees are swapped. 191 | var mirror = function(memo, node) { 192 | if (!_.has(node, 'r')) return node; 193 | return _.extend(_.clone(node), { l: memo.r, r: memo.l }); 194 | }; 195 | // Returns the '-' for internal nodes, and the value itself for leaves. 196 | var toString = function(node) { return _.has(node, 'val') ? '-' : node; }; 197 | 198 | tree = walk.reduce(getSimpleTestTree(), mirror); 199 | t.equal(walk.reduce(tree, sum, leafMemo), 21); 200 | t.equal(walk.map(tree, walk.preorder, toString).join(''), '-0-4-6-5-1-3-2', 'pre-order map'); 201 | 202 | t.end(); 203 | }); 204 | 205 | test('find', function(t) { 206 | var tree = getSimpleTestTree(); 207 | 208 | // Returns a visitor function that will succeed when a node with the given 209 | // value is found, and then raise an exception the next time it's called. 210 | var findValue = function(value) { 211 | var found = false; 212 | return function(node) { 213 | if (found) throw new Error('already found!'); 214 | found = (node.val === value); 215 | return found; 216 | }; 217 | }; 218 | 219 | t.equal(walk.find(tree, findValue(0)).val, 0); 220 | t.equal(walk.find(tree, findValue(6)).val, 6); 221 | t.deepEqual(walk.find(tree, findValue(99)), undefined); 222 | 223 | t.end(); 224 | }); 225 | 226 | test('filter', function(t) { 227 | var tree = getSimpleTestTree(); 228 | tree.r.val = '.oOo.'; // Remove one of the numbers. 229 | var isEvenNumber = function(x) { 230 | return _.isNumber(x) && x % 2 === 0; 231 | }; 232 | 233 | t.equal(walk.filter(tree, walk.preorder, _.isObject).length, 7, 'filter objects'); 234 | t.equal(walk.filter(tree, walk.preorder, _.isNumber).length, 6, 'filter numbers'); 235 | t.equal(walk.filter(tree, walk.postorder, _.isNumber).length, 6, 'postorder filter numbers'); 236 | t.equal(walk.filter(tree, walk.preorder, isEvenNumber).length, 3, 'filter even numbers'); 237 | 238 | // With the identity function, only the value '0' should be omitted. 239 | t.equal(walk.filter(tree, walk.preorder, _.identity).length, 13, 'filter on identity function'); 240 | 241 | t.end(); 242 | }); 243 | 244 | test('reject', function(t) { 245 | var tree = getSimpleTestTree(); 246 | tree.r.val = '.oOo.'; // Remove one of the numbers. 247 | 248 | t.equal(walk.reject(tree, walk.preorder, _.isObject).length, 7, 'reject objects'); 249 | t.equal(walk.reject(tree, walk.preorder, _.isNumber).length, 8, 'reject numbers'); 250 | t.equal(walk.reject(tree, walk.postorder, _.isNumber).length, 8, 'postorder reject numbers'); 251 | 252 | // With the identity function, only the value '0' should be kept. 253 | t.equal(walk.reject(tree, walk.preorder, _.identity).length, 1, 'reject with identity function'); 254 | 255 | t.end(); 256 | }); 257 | 258 | test('custom traversal', function(t) { 259 | var tree = getSimpleTestTree(); 260 | 261 | // Set up a walker that will not traverse the 'val' properties. 262 | var walker = walk(function(node) { 263 | return _.omit(node, 'val'); 264 | }); 265 | t.equal(walker.pluck(tree, 'val').length, 7, 'pluck with custom traversal'); 266 | t.equal(walker.pluckRec(tree, 'val').length, 7, 'pluckRec with custom traversal'); 267 | 268 | t.equal(walker.map(tree, walk.postorder, _.identity).length, 7, 'traversal strategy is dynamically scoped'); 269 | 270 | // Check that the default walker is unaffected. 271 | t.equal(walk.map(tree, walk.postorder, _.identity).length, 14, 'default map still works'); 272 | t.equal(walk.pluckRec(tree, 'val').join(''), '0123456', 'default pluckRec still works'); 273 | 274 | t.end(); 275 | }); 276 | 277 | test('custom traversal shorthand', function(t) { 278 | var tree = getSimpleTestTree(); 279 | 280 | t.deepEqual(walk(['l']).map(tree, walk.preorder, getVal), [0, 1, 2]); 281 | t.deepEqual(walk(['l', 'r']).map(tree, walk.preorder, getVal), [0, 1, 2, 3, 4, 5, 6]); 282 | 283 | t.deepEqual(walk(['l', 'z']).map(tree, walk.preorder, getVal), [0, 1, 2]); 284 | t.deepEqual(walk([]).map(tree, walk.preorder, getVal), [0], 'with empty array, just visits root'); 285 | t.deepEqual(walk('z').map(tree, walk.preorder, getVal), [0], 'with unknown keys, just visits root'); 286 | 287 | // When just a string is passed as the traversal strategy, the behaviour is 288 | // subtly different: the traversal target (in this case, the array of child 289 | // nodes) is not treated as a node. 290 | tree = { val: 0, children: [{ val: 1, children: [] }, { val: 2 }]}; 291 | t.deepEqual(walk('children').map(tree, walk.preorder, getVal), [0, 1, 2]); 292 | t.deepEqual(walk(['children']).map(tree, walk.preorder, getVal), [0, undefined]); 293 | 294 | t.deepEqual(walk('z').map(tree, walk.preorder, getVal), [0], 'with unknown key, just visits root'); 295 | 296 | t.end(); 297 | }); 298 | 299 | test('reduce with custom traversal', function(t) { 300 | var tree = { op: '+', operands: [ 301 | { value: 1 }, 302 | { op: '+', operands: [ 303 | { value: 2 }, 304 | { value: 3 }] 305 | } 306 | ]}; 307 | 308 | var walker = walk(function(node) { return node.operands; }); 309 | var answer = walker.reduce(tree, function(subResults, node) { 310 | t.ok(_.isObject(node), 'should only visit objects, not primitives'); 311 | if ('value' in node) 312 | return node.value; 313 | t.ok(_.isArray(subResults), 'child results should be an array'); 314 | if (node.op === '+') 315 | return subResults[0] + subResults[1]; 316 | }); 317 | t.equal(answer, 6); 318 | 319 | t.end(); 320 | }); 321 | 322 | test('attributes', function(t) { 323 | var tree = getSimpleTestTree(); 324 | 325 | // Set up a walker that will not traverse the 'val' properties. 326 | var walker = walk(function(node) { 327 | return _.omit(node, 'val'); 328 | }); 329 | 330 | var count = 0; 331 | var min = walker.createAttribute(function(subResults, node) { 332 | count++; 333 | if (subResults) 334 | return Math.min(node.val, Math.min(subResults.l, subResults.r)); 335 | return node.val; 336 | }); 337 | t.equal(min(tree), 0); 338 | t.equal(count, 7); 339 | t.equal(min(tree), 0); 340 | t.equal(min(tree.l), 1); 341 | t.equal(min(tree.r), 4); 342 | t.equal(count, 7, 'visitor should be memoized for all nodes'); 343 | 344 | var max = walker.createAttribute(function(subResults, node) { 345 | if (subResults) 346 | return Math.max(node.val, Math.max(subResults.l, subResults.r)); 347 | return node.val; 348 | }); 349 | t.equal(max(tree), 6); 350 | 351 | t.end(); 352 | }); 353 | 354 | test('stopping recursion', function(t) { 355 | var tree = getSimpleTestTree(); 356 | var vals = []; 357 | walk.preorder(tree, function(node, key) { 358 | if (key === 'l') { 359 | return walk.STOP_RECURSION; 360 | } 361 | if (_.isObject(node)) { 362 | vals.push(node.val); 363 | } 364 | }); 365 | t.deepEqual(vals, [0, 4, 6]); 366 | 367 | t.end(); 368 | }); 369 | -------------------------------------------------------------------------------- /third_party/WeakMap/LICENSE: -------------------------------------------------------------------------------- 1 | See http://polymer.github.io/LICENSE.txt. 2 | -------------------------------------------------------------------------------- /third_party/WeakMap/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 The Polymer Authors. All rights reserved. 3 | * Use of this source code is governed by a BSD-style 4 | * license that can be found in the LICENSE file. 5 | */ 6 | 7 | if (typeof WeakMap === 'undefined') { 8 | (function() { 9 | var defineProperty = Object.defineProperty; 10 | var counter = Date.now() % 1e9; 11 | 12 | var WeakMap = function() { 13 | this.name = '__st' + (Math.random() * 1e9 >>> 0) + (counter++ + '__'); 14 | }; 15 | 16 | WeakMap.prototype = { 17 | set: function(key, value) { 18 | var entry = key[this.name]; 19 | if (entry && entry[0] === key) 20 | entry[1] = value; 21 | else 22 | defineProperty(key, this.name, {value: [key, value], writable: true}); 23 | return this; 24 | }, 25 | get: function(key) { 26 | var entry; 27 | return (entry = key[this.name]) && entry[0] === key ? 28 | entry[1] : undefined; 29 | }, 30 | delete: function(key) { 31 | var entry = key[this.name]; 32 | if (!entry || entry[0] !== key) return false; 33 | entry[0] = entry[1] = undefined; 34 | return true; 35 | }, 36 | has: function(key) { 37 | var entry = key[this.name]; 38 | if (!entry) return false; 39 | return entry[0] === key; 40 | } 41 | }; 42 | 43 | module.exports = WeakMap; 44 | })(); 45 | } else { 46 | module.exports = WeakMap; 47 | } 48 | --------------------------------------------------------------------------------