can-key-tree
Version:
Add and remove items to a tree
364 lines (356 loc) • 11.4 kB
JavaScript
/*[process-shim]*/
(function(global, env) {
// jshint ignore:line
if (typeof process === "undefined") {
global.process = {
argv: [],
cwd: function() {
return "";
},
browser: true,
env: {
NODE_ENV: env || "development"
},
version: "",
platform:
global.navigator &&
global.navigator.userAgent &&
/Windows/.test(global.navigator.userAgent)
? "win"
: ""
};
}
})(
typeof self == "object" && self.Object == Object
? self
: typeof process === "object" &&
Object.prototype.toString.call(process) === "[object process]"
? global
: window,
"development"
);
/*[global-shim-start]*/
(function(exports, global, doEval) {
// jshint ignore:line
var origDefine = global.define;
var get = function(name) {
var parts = name.split("."),
cur = global,
i;
for (i = 0; i < parts.length; i++) {
if (!cur) {
break;
}
cur = cur[parts[i]];
}
return cur;
};
var set = function(name, val) {
var parts = name.split("."),
cur = global,
i,
part,
next;
for (i = 0; i < parts.length - 1; i++) {
part = parts[i];
next = cur[part];
if (!next) {
next = cur[part] = {};
}
cur = next;
}
part = parts[parts.length - 1];
cur[part] = val;
};
var useDefault = function(mod) {
if (!mod || !mod.__esModule) return false;
var esProps = { __esModule: true, default: true };
for (var p in mod) {
if (!esProps[p]) return false;
}
return true;
};
var hasCjsDependencies = function(deps) {
return (
deps[0] === "require" && deps[1] === "exports" && deps[2] === "module"
);
};
var modules =
(global.define && global.define.modules) ||
(global._define && global._define.modules) ||
{};
var ourDefine = (global.define = function(moduleName, deps, callback) {
var module;
if (typeof deps === "function") {
callback = deps;
deps = [];
}
var args = [],
i;
for (i = 0; i < deps.length; i++) {
args.push(
exports[deps[i]]
? get(exports[deps[i]])
: modules[deps[i]] || get(deps[i])
);
}
// CJS has no dependencies but 3 callback arguments
if (hasCjsDependencies(deps) || (!deps.length && callback.length)) {
module = { exports: {} };
args[0] = function(name) {
return exports[name] ? get(exports[name]) : modules[name];
};
args[1] = module.exports;
args[2] = module;
}
// Babel uses the exports and module object.
else if (!args[0] && deps[0] === "exports") {
module = { exports: {} };
args[0] = module.exports;
if (deps[1] === "module") {
args[1] = module;
}
} else if (!args[0] && deps[0] === "module") {
args[0] = { id: moduleName };
}
global.define = origDefine;
var result = callback ? callback.apply(null, args) : undefined;
global.define = ourDefine;
// Favor CJS module.exports over the return value
result = module && module.exports ? module.exports : result;
modules[moduleName] = result;
// Set global exports
var globalExport = exports[moduleName];
if (globalExport && !get(globalExport)) {
if (useDefault(result)) {
result = result["default"];
}
set(globalExport, result);
}
});
global.define.orig = origDefine;
global.define.modules = modules;
global.define.amd = true;
ourDefine("@loader", [], function() {
// shim for @@global-helpers
var noop = function() {};
return {
get: function() {
return { prepareGlobal: noop, retrieveGlobal: noop };
},
global: global,
__exec: function(__load) {
doEval(__load.source, global);
}
};
});
})(
{},
typeof self == "object" && self.Object == Object
? self
: typeof process === "object" &&
Object.prototype.toString.call(process) === "[object process]"
? global
: window,
function(__$source__, __$global__) {
// jshint ignore:line
eval("(function() { " + __$source__ + " \n }).call(__$global__);");
}
);
/*can-key-tree@1.2.1#can-key-tree*/
define('can-key-tree', [
'require',
'exports',
'module',
'can-reflect'
], function (require, exports, module) {
'use strict';
var reflect = require('can-reflect');
function isBuiltInPrototype(obj) {
if (obj === Object.prototype) {
return true;
}
var protoString = Object.prototype.toString.call(obj);
var isNotObjObj = protoString !== '[object Object]';
var isObjSomething = protoString.indexOf('[object ') !== -1;
return isNotObjObj && isObjSomething;
}
function getDeepSize(root, level) {
if (level === 0) {
return reflect.size(root);
} else if (reflect.size(root) === 0) {
return 0;
} else {
var count = 0;
reflect.each(root, function (value) {
count += getDeepSize(value, level - 1);
});
return count;
}
}
function getDeep(node, items, depth, maxDepth) {
if (!node) {
return;
}
if (maxDepth === depth) {
if (reflect.isMoreListLikeThanMapLike(node)) {
reflect.addValues(items, reflect.toArray(node));
} else {
throw new Error('can-key-tree: Map-type leaf containers are not supported yet.');
}
} else {
reflect.each(node, function (value) {
getDeep(value, items, depth + 1, maxDepth);
});
}
}
function clearDeep(node, keys, maxDepth, deleteHandler) {
if (maxDepth === keys.length) {
if (reflect.isMoreListLikeThanMapLike(node)) {
var valuesToRemove = reflect.toArray(node);
if (deleteHandler) {
valuesToRemove.forEach(function (value) {
deleteHandler.apply(null, keys.concat(value));
});
}
reflect.removeValues(node, valuesToRemove);
} else {
throw new Error('can-key-tree: Map-type leaf containers are not supported yet.');
}
} else {
reflect.each(node, function (value, key) {
clearDeep(value, keys.concat(key), maxDepth, deleteHandler);
reflect.deleteKeyValue(node, key);
});
}
}
var KeyTree = function (treeStructure, callbacks) {
var FirstConstructor = treeStructure[0];
if (reflect.isConstructorLike(FirstConstructor)) {
this.root = new FirstConstructor();
} else {
this.root = FirstConstructor;
}
this.callbacks = callbacks || {};
this.treeStructure = treeStructure;
this.empty = true;
};
reflect.assign(KeyTree.prototype, {
add: function (keys) {
if (keys.length > this.treeStructure.length) {
throw new Error('can-key-tree: Can not add path deeper than tree.');
}
var place = this.root;
var rootWasEmpty = this.empty === true;
for (var i = 0; i < keys.length - 1; i++) {
var key = keys[i];
var childNode = reflect.getKeyValue(place, key);
if (!childNode) {
var Constructor = this.treeStructure[i + 1];
if (isBuiltInPrototype(Constructor.prototype)) {
childNode = new Constructor();
} else {
childNode = new Constructor(key);
}
reflect.setKeyValue(place, key, childNode);
}
place = childNode;
}
if (reflect.isMoreListLikeThanMapLike(place)) {
reflect.addValues(place, [keys[keys.length - 1]]);
} else {
throw new Error('can-key-tree: Map types are not supported yet.');
}
if (rootWasEmpty) {
this.empty = false;
if (this.callbacks.onFirst) {
this.callbacks.onFirst.call(this);
}
}
return this;
},
getNode: function (keys) {
var node = this.root;
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
node = reflect.getKeyValue(node, key);
if (!node) {
return;
}
}
return node;
},
get: function (keys) {
var node = this.getNode(keys);
if (this.treeStructure.length === keys.length) {
return node;
} else {
var Type = this.treeStructure[this.treeStructure.length - 1];
var items = new Type();
getDeep(node, items, keys.length, this.treeStructure.length - 1);
return items;
}
},
delete: function (keys, deleteHandler) {
var parentNode = this.root, path = [this.root], lastKey = keys[keys.length - 1];
for (var i = 0; i < keys.length - 1; i++) {
var key = keys[i];
var childNode = reflect.getKeyValue(parentNode, key);
if (childNode === undefined) {
return false;
} else {
path.push(childNode);
}
parentNode = childNode;
}
if (!keys.length) {
clearDeep(parentNode, [], this.treeStructure.length - 1, deleteHandler);
} else if (keys.length === this.treeStructure.length) {
if (reflect.isMoreListLikeThanMapLike(parentNode)) {
if (deleteHandler) {
deleteHandler.apply(null, keys.concat(lastKey));
}
reflect.removeValues(parentNode, [lastKey]);
} else {
throw new Error('can-key-tree: Map types are not supported yet.');
}
} else {
var nodeToRemove = reflect.getKeyValue(parentNode, lastKey);
if (nodeToRemove !== undefined) {
clearDeep(nodeToRemove, keys, this.treeStructure.length - 1, deleteHandler);
reflect.deleteKeyValue(parentNode, lastKey);
} else {
return false;
}
}
for (i = path.length - 2; i >= 0; i--) {
if (reflect.size(parentNode) === 0) {
parentNode = path[i];
reflect.deleteKeyValue(parentNode, keys[i]);
} else {
break;
}
}
if (reflect.size(this.root) === 0) {
this.empty = true;
if (this.callbacks.onEmpty) {
this.callbacks.onEmpty.call(this);
}
}
return true;
},
size: function () {
return getDeepSize(this.root, this.treeStructure.length - 1);
},
isEmpty: function () {
return this.empty;
}
});
module.exports = KeyTree;
});
/*[global-shim-end]*/
(function(global) { // jshint ignore:line
global._define = global.define;
global.define = global.define.orig;
}
)(typeof self == "object" && self.Object == Object ? self : (typeof process === "object" && Object.prototype.toString.call(process) === "[object process]") ? global : window);