-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issue 4 of cosmoz-treenode-navigator #1
Changes from 16 commits
92c303e
9dca38a
b19a36f
b9fb2ff
f7d2fdb
dea0b9a
760b426
1c88582
cd64e21
5a2064e
f8b75df
5af285e
d8e9ffd
e878ec1
00bb3de
44ca085
13f837a
4174ae1
47dfbf0
16dcf27
90122a3
fa33008
e465ec6
9e5078d
3c5318f
23ab530
5308aae
20a3aad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
bower_components/ | ||
node_modules/ | ||
debug.log | ||
.DS_Store |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,21 @@ | ||
<link rel="import" href="cosmoz-tree.html"> | ||
|
||
<!-- | ||
Navigator through object with treelike datastructure and default settings. | ||
|
||
@demo demo/index.html | ||
--> | ||
<script> | ||
'use strict'; | ||
|
||
Polymer({ is: 'cosmoz-default-tree' }); | ||
|
||
window.Cosmoz = window.Cosmoz || {}; | ||
|
||
function _objectValues(obj) { | ||
if (!obj) { | ||
return; | ||
} | ||
return Object.keys(obj).map(function (key) { | ||
return obj[key]; | ||
}); | ||
|
@@ -23,134 +34,192 @@ | |
|
||
Cosmoz.DefaultTree.prototype = Object.create(Cosmoz.Tree.prototype); | ||
|
||
Cosmoz.DefaultTree.prototype.getNodeByProperty = function (propertyName, propertyValue) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As in comment, needs to stay. |
||
/** | ||
* Returns the first node found. | ||
* @return {object} or null if there was an empty response. | ||
* @param {string} propertyName (The name of the property the match should be based on. e.g. "name") | ||
* @param {string} propertyValue (The value of the property the match should be based on. e.g. "Peter") | ||
* @param {boolean} exact (If the search should be executed exact or flaw. true wouldn't match "Pet") | ||
* @param {object} nodeObj (The object the search should be based on.) or on default this._treeData | ||
*/ | ||
Cosmoz.DefaultTree.prototype.getNodeByProperty = function (propertyName, propertyValue, exact, nodeObj) { | ||
if (propertyName === undefined || propertyValue === undefined) { | ||
return; | ||
} | ||
// Defaults | ||
exact = exact !== undefined ? exact : true; | ||
|
||
if (propertyName === 'pathLocator') { | ||
return this._getNodeByPathLocator(propertyValue); | ||
return this.getNodeByPathLocator(propertyValue, nodeObj); | ||
} | ||
|
||
return this._searchAllRoots(propertyName, propertyValue); | ||
var results = this.searchNodes(propertyName, propertyValue, exact, nodeObj); | ||
return results && results.length > 0 ? results[0] : null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we use But do we need to make a difference between |
||
}; | ||
|
||
Cosmoz.DefaultTree.prototype.getPath = function (node) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. don't think we can drop getPath |
||
if (node === undefined) { | ||
return; | ||
} | ||
return this._getPathByPathLocator(node.pathLocator); | ||
/** | ||
* Searches a (multi root) node and matches nodes based on a property and a value. | ||
* @return {array} | ||
* @param {string} propertyName (The name of the property the match should be based on. e.g. "name") | ||
* @param {string} propertyValue (The value of the property the match should be based on. e.g. "Peter") | ||
* @param {boolean} exact (If the search should be executed exact or flaw. true wouldn't match "Pet") | ||
* @param {object} nodeObj (The object the search should be based on.) or on default this._treeData | ||
* @param {string} childProperty (The string which describes the child property of a node.) Default: "children" | ||
*/ | ||
Cosmoz.DefaultTree.prototype.searchNodes = function (propertyName, propertyValue, exact, nodeObj, childProperty) { | ||
var results = []; | ||
|
||
// Defaults | ||
exact = exact !== undefined ? exact : true; | ||
var nodes = nodeObj ? _objectValues(nodeObj) : this._roots; | ||
|
||
nodes.forEach(function (node) { | ||
results = results.concat(this.search(node, propertyName, propertyValue, exact, childProperty)); | ||
}, this); | ||
|
||
return results; | ||
}; | ||
|
||
Cosmoz.DefaultTree.prototype.getPathByProperty = function (propertyName, propertyValue) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't drop getPathByProperty There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As in comment, needs to stay. |
||
if (propertyName === undefined || propertyValue === undefined) { | ||
return; | ||
/** | ||
* Returns the node of a given path. | ||
* @return {object} | ||
* @param {string} pathLocator (The string which describes the path. e.g. "1.2.9") | ||
* @param {object} nodeObj (The object the search should be based on.) Default: this._treeData | ||
* @param {string} separatorSign (The string which separates the path. e.g ".") Default: "." | ||
* @param {string} childProperty (The string which describes the child property of a node.) Default: "children" | ||
*/ | ||
Cosmoz.DefaultTree.prototype.getNodeByPathLocator = function (pathLocator, nodeObj, separatorSign, childProperty) { | ||
if (!pathLocator) { | ||
return this._treeData; | ||
} | ||
|
||
if (propertyName === 'pathLocator') { | ||
return this._getPathByPathLocator(propertyValue); | ||
} | ||
// Defaults | ||
nodeObj = nodeObj ? nodeObj : this._treeData; | ||
separatorSign = separatorSign ? separatorSign : '.'; | ||
childProperty = childProperty ? childProperty : 'children'; | ||
|
||
var node = this._searchAllRoots(propertyName, propertyValue); | ||
if (node !== undefined) { | ||
return this._getPathByPathLocator(node.pathLocator); | ||
} | ||
var pathNodes = this.getPathNodes(pathLocator, nodeObj, separatorSign, childProperty), | ||
lastNodeOnPath = pathNodes.pop(); | ||
return lastNodeOnPath !== undefined ? lastNodeOnPath : null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
But do we need to differentiate between |
||
}; | ||
|
||
Cosmoz.DefaultTree.prototype.getChildren = function (node) { | ||
if (node === undefined) { | ||
return; | ||
/** | ||
* Returns the nodes on a given path. | ||
* @return {array} or null if there was an empty response. | ||
* @param {string} pathLocator (The string which describes the path. e.g. "1.2.9") | ||
* @param {object} nodeObj (The object the search should be based on.) Default: this._treeData | ||
* @param {string} separatorSign (The string which separates the path. e.g ".") Default: "." | ||
* @param {string} childProperty (The string which describes the child property of a node.) Default: "children" | ||
*/ | ||
Cosmoz.DefaultTree.prototype.getPathNodes = function (pathLocator, nodeObj, separatorSign, childProperty) { | ||
if (!pathLocator) { | ||
return this._roots; | ||
} | ||
return _objectValues(node.children); | ||
}; | ||
|
||
Cosmoz.DefaultTree.prototype.getProperty = function (node, propertyName) { | ||
if (node === undefined || propertyName === undefined) { | ||
return; | ||
} | ||
// Defaults | ||
separatorSign = separatorSign ? separatorSign : '.'; | ||
childProperty = childProperty ? childProperty : 'children'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think since |
||
|
||
var path = pathLocator.split(separatorSign), | ||
pathSegment = nodeObj ? nodeObj : this._treeData, | ||
pathIsInvalid, | ||
nodes; | ||
|
||
// Get the nodes on the path. | ||
nodes = path.map(function (nodeKey) { | ||
var node = pathSegment[nodeKey], | ||
children = node[childProperty]; | ||
if (node && children !== undefined && Object.keys(children).length > 0) { | ||
pathSegment = children; | ||
} | ||
return node; | ||
}, this); | ||
|
||
return node[propertyName]; | ||
}; | ||
// Check if all nodes on the path are valid. | ||
pathIsInvalid = nodes.some(function (n) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When have you found the path to be invalid? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven't found it to be invalid in the current implementation. But you are right, it actually might should be implemented in Imagine those nodes: 1 - 9 - 5 - 7
I'll move the check to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well I think the most plausible scenario is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But I wouldn't have an easy way to figure out which path part was missing plus it could be a complete different path I intended to get (as well with undefined in the path). Imagine a branch:
have to check that use case.. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The logic doesn't start traversing a path until without finding a hit, so you wouldn't ever be able to just "skip" node But still, any while (nodes.length > 0 && nodes[0] === undefined) {
nodes.shift();
} Unless we have a reason to keep them, then we might want to make the path validator function more intelligent instead. Also, to avoid logic like " pathIsValid = nodes.every(function (node) {
return node !== undefined;
} (This also matches your code comment closer.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the rest, thanks, much nicer! (pathIsValid) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I see your point. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Btw I need to change the node gathering of
Reason: With above implementation, no "undefined" values would be added. And an empty array would be returned if the start of the path would be already invalid. Maybe thats better after all and solves the problem in both functions! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm I see.. how about: nodes = path.map(function (nodeKey) {
if (!pathSegment || !pathSegment[nodeKey]) {
return;
}
var node = pathSegment[nodeKey];
pathSegment = node[childProperty];
return node;
}, this); Then, actually, after filtering out the initial pathIsValid = nodes.indexOf(undefined) === -1; right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I guess I have the worst timing when pushing.. |
||
if (n === undefined) { | ||
return true; | ||
} | ||
}); | ||
|
||
Cosmoz.DefaultTree.prototype._searchAllRoots = function (propertyName, propertyValue) { | ||
var i, | ||
node; | ||
return pathIsInvalid ? null : nodes; | ||
}, | ||
|
||
for (i = 0; i < this._roots.length; i+=1) { | ||
node = this.search(this._roots[i], propertyName, propertyValue); | ||
if (node !== undefined) { | ||
return node; | ||
} | ||
} | ||
/** | ||
* Returns a string which describes the path of a node (found by its path locator). | ||
* @return {string} e.g. home/computer/desktop | ||
* @param {string} pathLocator (The string which describes the path. e.g. "1.2.9") | ||
* @param {string} pathProperty (The property of a node on which the path should be build on. e.g "location" if node = {"location": "home"}) | ||
* @param {string} pathSeparator (The string the path should get separated with.) Default: "/" | ||
* @param {string} separatorSign (The string which separates the path. e.g ".") Default: "." | ||
* @param {string} childProperty (The string which describes the child property of a node.) Default: "children" | ||
*/ | ||
Cosmoz.DefaultTree.prototype.getPathString = function (pathLocator, pathProperty, pathSeparator, separatorSign, childProperty) { | ||
var pathNodes = this.getPathNodes(pathLocator, this._treeData); | ||
|
||
// Defaults | ||
pathSeparator = pathSeparator ? pathSeparator : '/'; | ||
separatorSign = separatorSign ? separatorSign : '.'; | ||
childProperty = childProperty ? childProperty : 'children'; | ||
|
||
return pathNodes.map(function (node) { | ||
return node[pathProperty]; | ||
}).join(pathSeparator); | ||
}; | ||
|
||
Cosmoz.DefaultTree.prototype._getNodeByPathLocator = function (pathLocator) { | ||
var node, | ||
path, | ||
i; | ||
/** | ||
* Returns a string which describes the path of a node (found by a node's property and value). | ||
* @return {string} e.g. home/computer/desktop | ||
* @param {string} propertyName (The name of the property the match should be based on. e.g. "name") | ||
* @param {string} propertyValue (The value of the property the match should be based on. e.g. "Peter") | ||
* @param {string} pathProperty (The property of a node on which the path should be build on. e.g "location" if node = {"location": "home"}) | ||
* @param {string} pathSeparator (The string the path should get separated with.) Default: "/" | ||
* @param {string} separatorSign (The string which separates the path. e.g ".") Default: "." | ||
* @param {string} childProperty (The string which describes the child property of a node.) Default: "children" | ||
*/ | ||
Cosmoz.DefaultTree.prototype.getPathStringByProperty = function (propertyName, propertyValue, pathProperty, pathSeparator, separatorSign, childProperty) { | ||
if (propertyName === undefined || propertyValue === undefined) { | ||
return; | ||
} | ||
|
||
for (i = 0; i < this._roots.length; i+=1) { | ||
node = this._roots[i]; | ||
if (node.pathLocator === pathLocator) { | ||
return node; | ||
} | ||
// Defaults | ||
pathProperty = pathProperty ? pathProperty : 'name'; | ||
separatorSign = separatorSign ? separatorSign : '.'; | ||
childProperty = childProperty ? childProperty : 'children'; | ||
|
||
if (pathLocator.indexOf(node.pathLocator + '.') === 0) { | ||
path = pathLocator.slice(node.pathLocator.length + 1).split('.'); | ||
for (i = 0; i < path.length; i+=1) { | ||
if (node.children) { | ||
node = node.children[path[i]]; | ||
} else { | ||
node = null; | ||
} | ||
if (node === undefined || node === null) { | ||
break; | ||
} | ||
} | ||
|
||
if (node !== undefined && node !== null) { | ||
return node; | ||
} | ||
} | ||
if (propertyName === 'pathLocator') { | ||
return this.getPathString(propertyValue, pathProperty); | ||
} | ||
|
||
var node = this.getNodeByProperty(propertyName, propertyValue); | ||
|
||
if (node) { | ||
return this.getPathString(node.pathLocator || node.path, pathProperty); | ||
} | ||
}; | ||
|
||
Cosmoz.DefaultTree.prototype._getPathByPathLocator = function (pathLocator) { | ||
var node, | ||
path, | ||
subPath, | ||
i; | ||
/** | ||
* Returns an Object or an Array representing the children of a node. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, multiple return types. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Docs needs updating |
||
*/ | ||
Cosmoz.DefaultTree.prototype.getChildren = function (node, childProperty) { | ||
if (!node) { | ||
return; | ||
} | ||
|
||
for (i = 0; i < this._roots.length; i+=1) { | ||
node = this._roots[i]; | ||
path = [node]; | ||
// Defaults | ||
childProperty = childProperty ? childProperty : 'children'; | ||
|
||
if (node.pathLocator === pathLocator) { | ||
return path; | ||
} | ||
return _objectValues(node[childProperty]); | ||
}; | ||
|
||
if (pathLocator.indexOf(node.pathLocator + '.') === 0) { | ||
subPath = pathLocator.slice(node.pathLocator.length + 1).split('.'); | ||
for (i = 0; i < subPath.length; i+=1) { | ||
if (node.children) { | ||
node = node.children[subPath[i]]; | ||
} else { | ||
node = null; | ||
} | ||
if (node === undefined || node === null) { | ||
path = null; | ||
break; | ||
} else { | ||
path.push(node); | ||
} | ||
} | ||
|
||
if (path !== null) { | ||
return path; | ||
} | ||
} | ||
/** | ||
* Returns the property of a Node based on a given property name. | ||
*/ | ||
Cosmoz.DefaultTree.prototype.getProperty = function (node, propertyName) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @nomego actually similar discussion as with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right, it's pretty weird. |
||
if (!node || !propertyName) { | ||
return; | ||
} | ||
return node[propertyName]; | ||
}; | ||
|
||
|
||
</script> | ||
</script> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure we actually need to make this a Polymer element.
Did you see a reason?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only reason was for the
iron-component-page
to not crash. (demo & docs)Maybe there is another way but I didn't explore it yet..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh ok well then leave it but maybe add a comment about it, too.