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
JavaScript
(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