UNPKG

hierarchy-model

Version:

A data model for a hierarchy of nodes, used by roles-hierarchy and permissions-hierarchy

1,534 lines (1,342 loc) 84.2 kB
(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define("hierarchy-model", [], factory); else if(typeof exports === 'object') exports["hierarchy-model"] = factory(); else root["hierarchy-model"] = factory(); })(this, function() { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { /******/ configurable: false, /******/ enumerable: true, /******/ get: getter /******/ }); /******/ } /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 2); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, exports) { // shim for using process in browser var process = module.exports = {}; // cached from whatever global is present so that test runners that stub it // don't break things. But we need to wrap it in a try catch in case it is // wrapped in strict mode code which doesn't define any globals. It's inside a // function because try/catches deoptimize in certain engines. var cachedSetTimeout; var cachedClearTimeout; function defaultSetTimout() { throw new Error('setTimeout has not been defined'); } function defaultClearTimeout () { throw new Error('clearTimeout has not been defined'); } (function () { try { if (typeof setTimeout === 'function') { cachedSetTimeout = setTimeout; } else { cachedSetTimeout = defaultSetTimout; } } catch (e) { cachedSetTimeout = defaultSetTimout; } try { if (typeof clearTimeout === 'function') { cachedClearTimeout = clearTimeout; } else { cachedClearTimeout = defaultClearTimeout; } } catch (e) { cachedClearTimeout = defaultClearTimeout; } } ()) function runTimeout(fun) { if (cachedSetTimeout === setTimeout) { //normal enviroments in sane situations return setTimeout(fun, 0); } // if setTimeout wasn't available but was latter defined if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { cachedSetTimeout = setTimeout; return setTimeout(fun, 0); } try { // when when somebody has screwed with setTimeout but no I.E. maddness return cachedSetTimeout(fun, 0); } catch(e){ try { // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally return cachedSetTimeout.call(null, fun, 0); } catch(e){ // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error return cachedSetTimeout.call(this, fun, 0); } } } function runClearTimeout(marker) { if (cachedClearTimeout === clearTimeout) { //normal enviroments in sane situations return clearTimeout(marker); } // if clearTimeout wasn't available but was latter defined if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { cachedClearTimeout = clearTimeout; return clearTimeout(marker); } try { // when when somebody has screwed with setTimeout but no I.E. maddness return cachedClearTimeout(marker); } catch (e){ try { // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally return cachedClearTimeout.call(null, marker); } catch (e){ // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. // Some versions of I.E. have different rules for clearTimeout vs setTimeout return cachedClearTimeout.call(this, marker); } } } var queue = []; var draining = false; var currentQueue; var queueIndex = -1; function cleanUpNextTick() { if (!draining || !currentQueue) { return; } draining = false; if (currentQueue.length) { queue = currentQueue.concat(queue); } else { queueIndex = -1; } if (queue.length) { drainQueue(); } } function drainQueue() { if (draining) { return; } var timeout = runTimeout(cleanUpNextTick); draining = true; var len = queue.length; while(len) { currentQueue = queue; queue = []; while (++queueIndex < len) { if (currentQueue) { currentQueue[queueIndex].run(); } } queueIndex = -1; len = queue.length; } currentQueue = null; draining = false; runClearTimeout(timeout); } process.nextTick = function (fun) { var args = new Array(arguments.length - 1); if (arguments.length > 1) { for (var i = 1; i < arguments.length; i++) { args[i - 1] = arguments[i]; } } queue.push(new Item(fun, args)); if (queue.length === 1 && !draining) { runTimeout(drainQueue); } }; // v8 likes predictible objects function Item(fun, array) { this.fun = fun; this.array = array; } Item.prototype.run = function () { this.fun.apply(null, this.array); }; process.title = 'browser'; process.browser = true; process.env = {}; process.argv = []; process.version = ''; // empty string to avoid regexp issues process.versions = {}; function noop() {} process.on = noop; process.addListener = noop; process.once = noop; process.off = noop; process.removeListener = noop; process.removeAllListeners = noop; process.emit = noop; process.prependListener = noop; process.prependOnceListener = noop; process.listeners = function (name) { return [] } process.binding = function (name) { throw new Error('process.binding is not supported'); }; process.cwd = function () { return '/' }; process.chdir = function (dir) { throw new Error('process.chdir is not supported'); }; process.umask = function() { return 0; }; /***/ }), /* 1 */, /* 2 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(process) { Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _treeModel = __webpack_require__(3); var _treeModel2 = _interopRequireDefault(_treeModel); var _topiary = __webpack_require__(6); var _topiary2 = _interopRequireDefault(_topiary); var _underscore = __webpack_require__(7); var _underscore2 = _interopRequireDefault(_underscore); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } if (process) { process.env.SUPPRESS_NO_CONFIG_WARNING = 'y'; } var Hierarchy = function () { /** * create a new instance of Hierarchy * @param {Object} paramsObj containing a Hierarchy and a loggingConfig (optional) and a TreeModel config (optional): * { * hierarchy: {"name":"teacher", "children": [ {"name":"student"} ]}, * treeModelConfig: { "childrenPropertyName": "children" }, * loggerCallback: an object that has debug, info, warn and error properties whose values are logging functions. * } */ function Hierarchy(paramsObj) { _classCallCheck(this, Hierarchy); // set up config defaults var treeModelConfig = paramsObj.treeModelConfig || { "childrenPropertyName": "children" }; this.logger = paramsObj.loggerCallback || { debug: function debug(msg) {}, info: function info(msg) {}, warn: function warn(msg) {}, error: function error(msg, err) {} }; this.childrenPropertyName = treeModelConfig.childrenPropertyName; // actual constructor stuff here. // get treeModelConfig from config // we need a clone of the treeModelConfig (it doesn't work straight from node-config) treeModelConfig = JSON.parse(JSON.stringify(treeModelConfig)); this.treeModel = new _treeModel2.default(treeModelConfig); this.root = this.treeModel.parse(paramsObj.hierarchy); this.logger.debug(this.getTopiaryAsString()); } /** * re-create the hierarchy with a new object structure. * @param {Object} hierarchy */ _createClass(Hierarchy, [{ key: 'reparse', value: function reparse(hierarchy) { this.root = this.treeModel.parse(hierarchy); } }, { key: '_findNode', value: function _findNode(nodeName) { var startNode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.root; return startNode.first({ strategy: 'breadth' }, function (node) { return node.model.name === nodeName; }); } }, { key: '_getOrganizationsForUser', value: function _getOrganizationsForUser(myUserObj) { var myOrganizations = []; if (myUserObj) { // figure out which organizations we belong to. if (myUserObj.profile && (myUserObj.profile.organization || myUserObj.profile.organizations)) { // note the plural if (myUserObj.profile.organization) { // there can be only one. myOrganizations = [myUserObj.profile.organization]; } else { // this guy is in multiple organizations. myOrganizations = JSON.parse(JSON.stringify(myUserObj.profile.organizations)); // clone organizations } } else { // default to the global group if there is no org info stored on the profile myOrganizations = [_GLOBAL_GROUP]; } } return myOrganizations; } /** * Find the model for a node in the hierarchy, by name * @param {string} nodeName - the name of the node to find (i.e. 'name' property value) * @param {object} [startNode] - the node in the hierarchy to start from * @returns {object} - the model of the node in the tree that matches */ }, { key: 'findNodeInHierarchy', value: function findNodeInHierarchy(nodeName, startNode) { var result = this._findNode(nodeName, startNode); if (result && result.model) { this.logger.debug('findNodeInHierarchy(' + nodeName + ') => returning ' + JSON.stringify(result.model, null, 2)); return result.model; } this.logger.debug('findNodeInHierarchy(' + nodeName + ') => returning undefined'); } /** * Find the node object for a node in the hierarchy, by name * @param {string} nodeName - the name of the node to find (i.e. 'name' property value) * @param {object} [startNode] - the node in the hierarchy to start from */ }, { key: 'findNodeObj', value: function findNodeObj(nodeName, startNode) { return this._findNode(nodeName, startNode); } /** * Return the descendent node of the given nodeName if found. * @param {string} nodeName - the name of the node underneath which we should search * @param {string} descendantNodeName - the name of the descendant node to find * @param {object} [startNode] - the node in the hierarchy to start from * @returns {object} - the node of the descendant, or undefined or false if not found. */ }, { key: 'findDescendantNodeByName', value: function findDescendantNodeByName(nodeName, descendantNodeName, startNode) { // get the node for the node name var senior = this._findNode(nodeName, startNode); if (!senior) { return false; } var junior = this._findNode(descendantNodeName, senior); if (junior) { this.logger.debug('findDescendantNodeByName(' + nodeName + ',' + descendantNodeName + ') => returning ' + JSON.stringify(junior.model, null, 2)); return junior.model; } else { this.logger.debug('findDescendantNodeByName(' + nodeName + ') => returning undefined'); } } /** * Get the names of subordinate nodes as an array * @param {string} nodeName - the name of the senior node i.e. 'name' property value * @param {object} [startNode] - the node in the hierarchy to start from * @returns {Array} - the subordinate node names if any, otherwise undefined. */ }, { key: 'getAllDescendantNodesAsArray', value: function getAllDescendantNodesAsArray(nodeName, startNode) { // find the node for the given node name var seniorNode = this._findNode(nodeName, startNode); if (seniorNode) { // get all the nodes under this one var result = seniorNode.all({ strategy: 'breadth' }, function (node) { return node.model.name !== nodeName; }).map(function (item) { // get the names of each node return item.model.name; }); this.logger.debug('getAllDescendantNodesAsArray(' + nodeName + ') => returning ' + JSON.stringify(result, null, 2)); return result; } } /** * get a string suitable for printing, via the topiary library. * @param {object} hierarchy - a Hierarchy instance * @returns {string} a string representation of the hierarchy */ }, { key: 'getTopiaryAsString', value: function getTopiaryAsString() { var hierarchy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.root; return (0, _topiary2.default)(hierarchy.model, this.childrenPropertyName); } /** * Process each node in the tree via a callback, halting when your callback returns false. * @param {function} callback a function that takes a single parameter, 'node', * which is the value of the node currently being processed. Return false from the callback to halt the traversal. */ }, { key: 'walkNodes', value: function walkNodes(callback) { this.root.walk(callback); } /** * Add a child to a parent. * @param {Object} parentNode the node in the hierarchy to which the child should be added * @param {Object} childNode a node or tree * @returns {Object} the child node. */ }, { key: 'addNodeAsChildOfNode', value: function addNodeAsChildOfNode(parentNode, childNode) { var debug = this.logger.debug; debug('parentNode: ' + JSON.stringify(parentNode.model)); debug('childNode: ' + JSON.stringify(childNode.model)); return parentNode.addChild(childNode); } /** * Get the array of Nodes representing the path from the root to this Node (inclusive). * @param {Object} node * @returns {Object} the array of Nodes representing the path from the root to this Node (inclusive). */ }, { key: 'getTreeModel', /** * get the underlying TreeModel instance * @returns {Object} the underlying TreeModel instance. */ value: function getTreeModel() { return this.treeModel; } /** * Create Node (which is itself just a TreeModel) * @param {Object} paramsObj - an object which has 'name' and 'children' properties */ }, { key: 'getNewNode', value: function getNewNode(paramsObj) { return this.treeModel.parse(paramsObj); } }], [{ key: 'getPathOfNode', value: function getPathOfNode(node) { return node.getPath(); } /** * Get the array of Node names representing the path from the root to this Node (inclusive). * @param {Object} node * @returns {Array<String>} the array of Strings representing the path from the root to this Node (inclusive). */ }, { key: 'getNamesOfNodePath', value: function getNamesOfNodePath(node) { return _underscore2.default.map(node.getPath(), function (thisNode) { return thisNode.model.name; }); } /** * Drop the subtree starting at this node. Returns the node itself, which is now a root node. * @param {Object} node the node in the hierarchy to drop. * @returns {Object} node the node that just got dropped. */ }, { key: 'deleteNodeFromHierarchy', value: function deleteNodeFromHierarchy(node) { return node.drop(); } }]); return Hierarchy; }(); exports.default = Hierarchy; /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(0))) /***/ }), /* 3 */ /***/ (function(module, exports, __webpack_require__) { var mergeSort, findInsertIndex; mergeSort = __webpack_require__(4); findInsertIndex = __webpack_require__(5); module.exports = (function () { 'use strict'; var walkStrategies; walkStrategies = {}; function k(result) { return function () { return result; }; } function TreeModel(config) { config = config || {}; this.config = config; this.config.childrenPropertyName = config.childrenPropertyName || 'children'; this.config.modelComparatorFn = config.modelComparatorFn; } function addChildToNode(node, child) { child.parent = node; node.children.push(child); return child; } function Node(config, model) { this.config = config; this.model = model; this.children = []; } TreeModel.prototype.parse = function (model) { var i, childCount, node; if (!(model instanceof Object)) { throw new TypeError('Model must be of type object.'); } node = new Node(this.config, model); if (model[this.config.childrenPropertyName] instanceof Array) { if (this.config.modelComparatorFn) { model[this.config.childrenPropertyName] = mergeSort( this.config.modelComparatorFn, model[this.config.childrenPropertyName]); } for (i = 0, childCount = model[this.config.childrenPropertyName].length; i < childCount; i++) { addChildToNode(node, this.parse(model[this.config.childrenPropertyName][i])); } } return node; }; function hasComparatorFunction(node) { return typeof node.config.modelComparatorFn === 'function'; } Node.prototype.isRoot = function () { return this.parent === undefined; }; Node.prototype.hasChildren = function () { return this.children.length > 0; }; function addChild(self, child, insertIndex) { var index; if (!(child instanceof Node)) { throw new TypeError('Child must be of type Node.'); } child.parent = self; if (!(self.model[self.config.childrenPropertyName] instanceof Array)) { self.model[self.config.childrenPropertyName] = []; } if (hasComparatorFunction(self)) { // Find the index to insert the child index = findInsertIndex( self.config.modelComparatorFn, self.model[self.config.childrenPropertyName], child.model); // Add to the model children self.model[self.config.childrenPropertyName].splice(index, 0, child.model); // Add to the node children self.children.splice(index, 0, child); } else { if (insertIndex === undefined) { self.model[self.config.childrenPropertyName].push(child.model); self.children.push(child); } else { if (insertIndex < 0 || insertIndex > self.children.length) { throw new Error('Invalid index.'); } self.model[self.config.childrenPropertyName].splice(insertIndex, 0, child.model); self.children.splice(insertIndex, 0, child); } } return child; } Node.prototype.addChild = function (child) { return addChild(this, child); }; Node.prototype.addChildAtIndex = function (child, index) { if (hasComparatorFunction(this)) { throw new Error('Cannot add child at index when using a comparator function.'); } return addChild(this, child, index); }; Node.prototype.setIndex = function (index) { if (hasComparatorFunction(this)) { throw new Error('Cannot set node index when using a comparator function.'); } if (this.isRoot()) { if (index === 0) { return this; } throw new Error('Invalid index.'); } if (index < 0 || index >= this.parent.children.length) { throw new Error('Invalid index.'); } var oldIndex = this.parent.children.indexOf(this); this.parent.children.splice(index, 0, this.parent.children.splice(oldIndex, 1)[0]); this.parent.model[this.parent.config.childrenPropertyName] .splice(index, 0, this.parent.model[this.parent.config.childrenPropertyName].splice(oldIndex, 1)[0]); return this; }; Node.prototype.getPath = function () { var path = []; (function addToPath(node) { path.unshift(node); if (!node.isRoot()) { addToPath(node.parent); } })(this); return path; }; Node.prototype.getIndex = function () { if (this.isRoot()) { return 0; } return this.parent.children.indexOf(this); }; /** * Parse the arguments of traversal functions. These functions can take one optional * first argument which is an options object. If present, this object will be stored * in args.options. The only mandatory argument is the callback function which can * appear in the first or second position (if an options object is given). This * function will be saved to args.fn. The last optional argument is the context on * which the callback function will be called. It will be available in args.ctx. * * @returns Parsed arguments. */ function parseArgs() { var args = {}; if (arguments.length === 1) { if (typeof arguments[0] === 'function') { args.fn = arguments[0]; } else { args.options = arguments[0]; } } else if (arguments.length === 2) { if (typeof arguments[0] === 'function') { args.fn = arguments[0]; args.ctx = arguments[1]; } else { args.options = arguments[0]; args.fn = arguments[1]; } } else { args.options = arguments[0]; args.fn = arguments[1]; args.ctx = arguments[2]; } args.options = args.options || {}; if (!args.options.strategy) { args.options.strategy = 'pre'; } if (!walkStrategies[args.options.strategy]) { throw new Error('Unknown tree walk strategy. Valid strategies are \'pre\' [default], \'post\' and \'breadth\'.'); } return args; } Node.prototype.walk = function () { var args; args = parseArgs.apply(this, arguments); walkStrategies[args.options.strategy].call(this, args.fn, args.ctx); }; walkStrategies.pre = function depthFirstPreOrder(callback, context) { var i, childCount, keepGoing; keepGoing = callback.call(context, this); for (i = 0, childCount = this.children.length; i < childCount; i++) { if (keepGoing === false) { return false; } keepGoing = depthFirstPreOrder.call(this.children[i], callback, context); } return keepGoing; }; walkStrategies.post = function depthFirstPostOrder(callback, context) { var i, childCount, keepGoing; for (i = 0, childCount = this.children.length; i < childCount; i++) { keepGoing = depthFirstPostOrder.call(this.children[i], callback, context); if (keepGoing === false) { return false; } } keepGoing = callback.call(context, this); return keepGoing; }; walkStrategies.breadth = function breadthFirst(callback, context) { var queue = [this]; (function processQueue() { var i, childCount, node; if (queue.length === 0) { return; } node = queue.shift(); for (i = 0, childCount = node.children.length; i < childCount; i++) { queue.push(node.children[i]); } if (callback.call(context, node) !== false) { processQueue(); } })(); }; Node.prototype.all = function () { var args, all = []; args = parseArgs.apply(this, arguments); args.fn = args.fn || k(true); walkStrategies[args.options.strategy].call(this, function (node) { if (args.fn.call(args.ctx, node)) { all.push(node); } }, args.ctx); return all; }; Node.prototype.first = function () { var args, first; args = parseArgs.apply(this, arguments); args.fn = args.fn || k(true); walkStrategies[args.options.strategy].call(this, function (node) { if (args.fn.call(args.ctx, node)) { first = node; return false; } }, args.ctx); return first; }; Node.prototype.drop = function () { var indexOfChild; if (!this.isRoot()) { indexOfChild = this.parent.children.indexOf(this); this.parent.children.splice(indexOfChild, 1); this.parent.model[this.config.childrenPropertyName].splice(indexOfChild, 1); this.parent = undefined; delete this.parent; } return this; }; return TreeModel; })(); /***/ }), /* 4 */ /***/ (function(module, exports) { module.exports = (function () { 'use strict'; /** * Sort an array using the merge sort algorithm. * * @param {function} comparatorFn The comparator function. * @param {array} arr The array to sort. * @returns {array} The sorted array. */ function mergeSort(comparatorFn, arr) { var len = arr.length, firstHalf, secondHalf; if (len >= 2) { firstHalf = arr.slice(0, len / 2); secondHalf = arr.slice(len / 2, len); return merge(comparatorFn, mergeSort(comparatorFn, firstHalf), mergeSort(comparatorFn, secondHalf)); } else { return arr.slice(); } } /** * The merge part of the merge sort algorithm. * * @param {function} comparatorFn The comparator function. * @param {array} arr1 The first sorted array. * @param {array} arr2 The second sorted array. * @returns {array} The merged and sorted array. */ function merge(comparatorFn, arr1, arr2) { var result = [], left1 = arr1.length, left2 = arr2.length; while (left1 > 0 && left2 > 0) { if (comparatorFn(arr1[0], arr2[0]) <= 0) { result.push(arr1.shift()); left1--; } else { result.push(arr2.shift()); left2--; } } if (left1 > 0) { result.push.apply(result, arr1); } else { result.push.apply(result, arr2); } return result; } return mergeSort; })(); /***/ }), /* 5 */ /***/ (function(module, exports) { module.exports = (function () { 'use strict'; /** * Find the index to insert an element in array keeping the sort order. * * @param {function} comparatorFn The comparator function which sorted the array. * @param {array} arr The sorted array. * @param {object} el The element to insert. */ function findInsertIndex(comparatorFn, arr, el) { var i, len; for (i = 0, len = arr.length; i < len; i++) { if (comparatorFn(arr[i], el) > 0) { break; } } return i; } return findInsertIndex; })(); /***/ }), /* 6 */ /***/ (function(module, exports) { module.exports = function (tree, recurseName, opts) { opts = opts || {}; var nameFn = opts.name || function (e) { return e.name; }; var filterFn = opts.filter || function () { return true; }; var sortFn = !opts.sort ? null : function (x, y) { return nameFn(x).localeCompare(nameFn(y)); }; var lines = [nameFn(tree)]; if (!Array.isArray(tree[recurseName])) { throw new Error("No recurse entry for '" + recurseName + "' found on root"); } var recurse = function (xs, level, parentAry) { var ys = (sortFn instanceof Function) ? xs.sort(sortFn) : xs; ys.forEach(function (sub, idx, subAry) { var children = (sub[recurseName] || []).filter(filterFn) , hasChildren = children.length > 0 , forkChar = hasChildren ? "┬" : "─" , isLast = idx === subAry.length - 1 , turnChar = isLast ? "└" : "├" , indent = ''; for (var i = 0; i < level; i += 1) { indent += (parentAry[i] ? " " : "│") + " "; } lines.push(" " + indent + turnChar + "─" + forkChar + nameFn(sub)); if (hasChildren) { // recurse into current tree while keeping track of parent lines recurse(children, level + 1, parentAry.concat(isLast)); } }); }; recurse(tree[recurseName].filter(filterFn), 0, []); return lines.join('\n'); }; /***/ }), /* 7 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// Underscore.js 1.8.3 // http://underscorejs.org // (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors // Underscore may be freely distributed under the MIT license. (function() { // Baseline setup // -------------- // Establish the root object, `window` in the browser, or `exports` on the server. var root = this; // Save the previous value of the `_` variable. var previousUnderscore = root._; // Save bytes in the minified (but not gzipped) version: var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; // Create quick reference variables for speed access to core prototypes. var push = ArrayProto.push, slice = ArrayProto.slice, toString = ObjProto.toString, hasOwnProperty = ObjProto.hasOwnProperty; // All **ECMAScript 5** native function implementations that we hope to use // are declared here. var nativeIsArray = Array.isArray, nativeKeys = Object.keys, nativeBind = FuncProto.bind, nativeCreate = Object.create; // Naked function reference for surrogate-prototype-swapping. var Ctor = function(){}; // Create a safe reference to the Underscore object for use below. var _ = function(obj) { if (obj instanceof _) return obj; if (!(this instanceof _)) return new _(obj); this._wrapped = obj; }; // Export the Underscore object for **Node.js**, with // backwards-compatibility for the old `require()` API. If we're in // the browser, add `_` as a global object. if (true) { if (typeof module !== 'undefined' && module.exports) { exports = module.exports = _; } exports._ = _; } else { root._ = _; } // Current version. _.VERSION = '1.8.3'; // Internal function that returns an efficient (for current engines) version // of the passed-in callback, to be repeatedly applied in other Underscore // functions. var optimizeCb = function(func, context, argCount) { if (context === void 0) return func; switch (argCount == null ? 3 : argCount) { case 1: return function(value) { return func.call(context, value); }; case 2: return function(value, other) { return func.call(context, value, other); }; case 3: return function(value, index, collection) { return func.call(context, value, index, collection); }; case 4: return function(accumulator, value, index, collection) { return func.call(context, accumulator, value, index, collection); }; } return function() { return func.apply(context, arguments); }; }; // A mostly-internal function to generate callbacks that can be applied // to each element in a collection, returning the desired result — either // identity, an arbitrary callback, a property matcher, or a property accessor. var cb = function(value, context, argCount) { if (value == null) return _.identity; if (_.isFunction(value)) return optimizeCb(value, context, argCount); if (_.isObject(value)) return _.matcher(value); return _.property(value); }; _.iteratee = function(value, context) { return cb(value, context, Infinity); }; // An internal function for creating assigner functions. var createAssigner = function(keysFunc, undefinedOnly) { return function(obj) { var length = arguments.length; if (length < 2 || obj == null) return obj; for (var index = 1; index < length; index++) { var source = arguments[index], keys = keysFunc(source), l = keys.length; for (var i = 0; i < l; i++) { var key = keys[i]; if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key]; } } return obj; }; }; // An internal function for creating a new object that inherits from another. var baseCreate = function(prototype) { if (!_.isObject(prototype)) return {}; if (nativeCreate) return nativeCreate(prototype); Ctor.prototype = prototype; var result = new Ctor; Ctor.prototype = null; return result; }; var property = function(key) { return function(obj) { return obj == null ? void 0 : obj[key]; }; }; // Helper for collection methods to determine whether a collection // should be iterated as an array or as an object // Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength // Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094 var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; var getLength = property('length'); var isArrayLike = function(collection) { var length = getLength(collection); return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX; }; // Collection Functions // -------------------- // The cornerstone, an `each` implementation, aka `forEach`. // Handles raw objects in addition to array-likes. Treats all // sparse array-likes as if they were dense. _.each = _.forEach = function(obj, iteratee, context) { iteratee = optimizeCb(iteratee, context); var i, length; if (isArrayLike(obj)) { for (i = 0, length = obj.length; i < length; i++) { iteratee(obj[i], i, obj); } } else { var keys = _.keys(obj); for (i = 0, length = keys.length; i < length; i++) { iteratee(obj[keys[i]], keys[i], obj); } } return obj; }; // Return the results of applying the iteratee to each element. _.map = _.collect = function(obj, iteratee, context) { iteratee = cb(iteratee, context); var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length, results = Array(length); for (var index = 0; index < length; index++) { var currentKey = keys ? keys[index] : index; results[index] = iteratee(obj[currentKey], currentKey, obj); } return results; }; // Create a reducing function iterating left or right. function createReduce(dir) { // Optimized iterator function as using arguments.length // in the main function will deoptimize the, see #1991. function iterator(obj, iteratee, memo, keys, index, length) { for (; index >= 0 && index < length; index += dir) { var currentKey = keys ? keys[index] : index; memo = iteratee(memo, obj[currentKey], currentKey, obj); } return memo; } return function(obj, iteratee, memo, context) { iteratee = optimizeCb(iteratee, context, 4); var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length, index = dir > 0 ? 0 : length - 1; // Determine the initial value if none is provided. if (arguments.length < 3) { memo = obj[keys ? keys[index] : index]; index += dir; } return iterator(obj, iteratee, memo, keys, index, length); }; } // **Reduce** builds up a single result from a list of values, aka `inject`, // or `foldl`. _.reduce = _.foldl = _.inject = createReduce(1); // The right-associative version of reduce, also known as `foldr`. _.reduceRight = _.foldr = createReduce(-1); // Return the first value which passes a truth test. Aliased as `detect`. _.find = _.detect = function(obj, predicate, context) { var key; if (isArrayLike(obj)) { key = _.findIndex(obj, predicate, context); } else { key = _.findKey(obj, predicate, context); } if (key !== void 0 && key !== -1) return obj[key]; }; // Return all the elements that pass a truth test. // Aliased as `select`. _.filter = _.select = function(obj, predicate, context) { var results = []; predicate = cb(predicate, context); _.each(obj, function(value, index, list) { if (predicate(value, index, list)) results.push(value); }); return results; }; // Return all the elements for which a truth test fails. _.reject = function(obj, predicate, context) { return _.filter(obj, _.negate(cb(predicate)), context); }; // Determine whether all of the elements match a truth test. // Aliased as `all`. _.every = _.all = function(obj, predicate, context) { predicate = cb(predicate, context); var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length; for (var index = 0; index < length; index++) { var currentKey = keys ? keys[index] : index; if (!predicate(obj[currentKey], currentKey, obj)) return false; } return true; }; // Determine if at least one element in the object matches a truth test. // Aliased as `any`. _.some = _.any = function(obj, predicate, context) { predicate = cb(predicate, context); var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length; for (var index = 0; index < length; index++) { var currentKey = keys ? keys[index] : index; if (predicate(obj[currentKey], currentKey, obj)) return true; } return false; }; // Determine if the array or object contains a given item (using `===`). // Aliased as `includes` and `include`. _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) { if (!isArrayLike(obj)) obj = _.values(obj); if (typeof fromIndex != 'number' || guard) fromIndex = 0; return _.indexOf(obj, item, fromIndex) >= 0; }; // Invoke a method (with arguments) on every item in a collection. _.invoke = function(obj, method) { var args = slice.call(arguments, 2); var isFunc = _.isFunction(method); return _.map(obj, function(value) { var func = isFunc ? method : value[method]; return func == null ? func : func.apply(value, args); }); }; // Convenience version of a common use case of `map`: fetching a property. _.pluck = function(obj, key) { return _.map(obj, _.property(key)); }; // Convenience version of a common use case of `filter`: selecting only objects // containing specific `key:value` pairs. _.where = function(obj, attrs) { return _.filter(obj, _.matcher(attrs)); }; // Convenience version of a common use case of `find`: getting the first object // containing specific `key:value` pairs. _.findWhere = function(obj, attrs) { return _.find(obj, _.matcher(attrs)); }; // Return the maximum element (or element-based computation). _.max = function(obj, iteratee, context) { var result = -Infinity, lastComputed = -Infinity, value, computed; if (iteratee == null && obj != null) { obj = isArrayLike(obj) ? obj : _.values(obj); for (var i = 0, length = obj.length; i < length; i++) { value = obj[i]; if (value > result) { result = value; } } } else { iteratee = cb(iteratee, context); _.each(obj, function(value, index, list) { computed = iteratee(value, index, list); if (computed > lastComputed || computed === -Infinity && result === -Infinity) { result = value; lastComputed = computed; } }); } return result; }; // Return the minimum element (or element-based computation). _.min = function(obj, iteratee, context) { var result = Infinity, lastComputed = Infinity, value, computed; if (iteratee == null && obj != null) { obj = isArrayLike(obj) ? obj : _.values(obj); for (var i = 0, length = obj.length; i < length; i++) { value = obj[i]; if (value < result) { result = value; } } } else { iteratee = cb(iteratee, context); _.each(obj, function(value, index, list) { computed = iteratee(value, index, list); if (computed < lastComputed || computed === Infinity && result === Infinity) { result = value; lastComputed = computed; } }); } return result; }; // Shuffle a collection, using the modern version of the // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle). _.shuffle = function(obj) { var set = isArrayLike(obj) ? obj : _.values(obj); var length = set.length; var shuffled = Array(length); for (var index = 0, rand; index < length; index++) { rand = _.random(0, index); if (rand !== index) shuffled[index] = shuffled[rand]; shuffled[rand] = set[index]; } return shuffled; }; // Sample **n** random values from a collection. // If **n** is not specified, returns a single random element. // The internal `guard` argument allows it to work with `map`. _.sample = function(obj, n, guard) { if (n == null || guard) { if (!isArrayLike(obj)) obj = _.values(obj); return obj[_.random(obj.length - 1)]; } return _.shuffle(obj).slice(0, Math.max(0, n)); }; // Sort the object's values by a criterion produced by an iteratee. _.sortBy = function(obj, iteratee, context) { iteratee = cb(iteratee, context); return _.pluck(_.map(obj, function(value, index, list) { return { value: value, index: index, criteria: iteratee(value, index, list) }; }).sort(function(left, right) { var a = left.criteria; var b = right.criteria; if (a !== b) { if (a > b || a === void 0) return 1; if (a < b || b === void 0) return -1; } return left.index - right.index; }), 'value'); }; // An internal function used for aggregate "group by" operations. var group = function(behavior) { return function(obj, iteratee, context) { var result = {}; iteratee = cb(iteratee, context); _.each(obj, function(value, index) { var key = iteratee(value, index, obj); behavior(result, value, key); }); return result; }; }; // Groups the object's values by a criterion. Pass either a string attribute // to group by, or a function that returns the criterion. _.groupBy = group(function(result, value, key) { if (_.has(result, key)) result[key].push(value); else result[key] = [value]; }); // Indexes the object's values by a criterion, similar to `groupBy`, but for // when you know that your index values will be unique. _.indexBy = group(function(result, value, key) { result[key] = value; }); // Counts instances of an object that group by a certain criterion. Pass // either a string attribute to count by, or a function that returns the // criterion. _.countBy = group(function(result, value, key) { if (_.has(result, key)) result[key]++; else result[key] = 1; }); // Safely create a real, live array from anything iterable. _.toArray = function(obj) { if (!obj) return []; if (_.isArray(obj)) return slice.call(obj); if (isArrayLike(obj)) return _.map(obj, _.identity); return _.values(obj); }; // Return the number of elements in an object. _.size = function(obj) { if (obj == null) return 0; return isArrayLike(obj) ? obj.length : _.keys(obj).length; }; // Split a collection into two arrays: one whose elements all satisfy the given // predicate, and one whose elements all do not satisfy the predicate. _.partition = function(obj, predicate, context) { predicate = cb(predicate, context); var pass = [], fail = []; _.each(obj, function(value, key, obj) { (predicate(value, key, obj) ? pass : fail).push(value); }); return [pass, fail]; }; // Array Functions // --------------- // Get the first element of an array. Passing **n** will return the first N // values in the array. Aliased as `head` and `take`. The **guard** check // allows it to work with `_.map`. _.first = _.head = _.take = function(array, n, guard) { if (array == null) return void 0; if (n == null || guard) return array[0]; return _.initial(array, array.length - n); }; // Returns everything but the last entry of the array. Especially useful on // the arguments object. Passing **n** will return all the values in // the array, excluding the last N. _.initial = function(array, n, guard) { return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n))); }; // Get the last element of an array. Passing **n** will return the last N // values in the array. _.last = function(array, n, guard) { if (array == null) return void 0; if (n == null || guard) return array[array.length - 1]; return _.rest(array, Math.max(0, array.length - n)); }; // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. // Especially useful on the arguments object. Passing an **n** will return // the rest N values in the array. _.rest = _.tail = _.drop = function(array, n, guard) { return slice.call(array, n == null || guard ? 1 : n); }; // Trim out all falsy values from an array. _.compact = function(array) { return _.filter(array, _.identity); }; // Internal implementation of a recursive `flatten` function. var flatten = function(input, shallow, strict, startIndex) { var output = [], idx = 0; for (var i = startIndex || 0, length = getLength(input); i < length; i++) { var value = input[i]; if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) { //flatten current level of array or arguments object if (!shallow) value = flatten(value, shallow, strict); var j = 0, len = value.length; output.length += len; while (j < len) { output[idx++] = value[j++]; } } else if (!strict) { output[idx++] = value; } } return output; }; // Flatten out an array, either recursively (by default), or just one level. _.flatten = function(array, shallow) { return flatten(array, shallow, false); }; // Return a version of the array that does not contain the specified value(s). _.without = function(array) { return _.difference(array, slice.call(arguments, 1)); }; // Produce a duplicate-free version of the array. If the array has already // been sorted, you have the option of using a faster algorithm. // Aliased as `unique`. _.uniq = _.unique = function(array, isSorted, iteratee, context) { if (!_.isBoolean(isSorted)) { context = iteratee; iteratee = isSorted; isSorted = false; } if (iteratee != null) iteratee = cb(iteratee, context); var result = []; var seen = []; for (var i = 0, length = getLength(array); i < length; i++) { var value = array[i], computed = iteratee ? iteratee(value, i, array) : value; if (isSorted) { if (!i || seen !== computed) result.push(value); seen = computed; } else if (iteratee) { if (!_.contains(seen, computed)) { seen.push(computed); result.push(value); } } else if (!_.contains(r