summaryrefslogtreecommitdiffstats
path: root/lib/modifiers/summary/moveArticle.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/modifiers/summary/moveArticle.js')
-rw-r--r--lib/modifiers/summary/moveArticle.js82
1 files changed, 82 insertions, 0 deletions
diff --git a/lib/modifiers/summary/moveArticle.js b/lib/modifiers/summary/moveArticle.js
new file mode 100644
index 0000000..06d82ca
--- /dev/null
+++ b/lib/modifiers/summary/moveArticle.js
@@ -0,0 +1,82 @@
+var is = require('is');
+var removeArticle = require('./removeArticle');
+var insertArticle = require('./insertArticle');
+
+/**
+ Returns a new summary, with the given article removed from its
+ origin level, and placed at the given target level.
+
+ @param {Summary} summary
+ @param {String|SummaryArticle} origin: level to remove
+ @param {String|SummaryArticle} target: the level where the article will be found
+ @return {Summary}
+*/
+function moveArticle(summary, origin, target) {
+ // Coerce to level
+ var originLevel = is.string(origin)? origin : origin.getLevel();
+ var targetLevel = is.string(target)? target : target.getLevel();
+
+ var article = summary.getByLevel(originLevel);
+
+ // Remove
+ var removed = removeArticle(summary, origin);
+
+ // Adjust targetLevel if removing impacted it
+ targetLevel = arrayToLevel(
+ shiftLevel(levelToArray(originLevel),
+ levelToArray(targetLevel)));
+ // Re-insert
+ return insertArticle(removed, target, article);
+}
+
+/**
+ @param {Array<Number>} removedLevel
+ @param {Array<Number>} level The level to udpate
+ @return {Array<Number>}
+ */
+function shiftLevel(removedLevel, level) {
+ if (level.length === 0) {
+ // `removedLevel` is under level, so no effect
+ return level;
+ } else if (removedLevel.length === 0) {
+ // Either `level` is a child of `removedLevel`... or they are equal
+ // This is undefined behavior.
+ return level;
+ }
+
+ var removedRoot = removedLevel[0];
+ var root = level[0];
+ var removedRest = removedLevel.slice(1);
+ var rest = level.slice(1);
+
+ if (removedRoot < root) {
+ // It will shift levels at this point. The rest is unchanged.
+ return Array.prototype.concat(root - 1, rest);
+ } else if (removedRoot === root) {
+ // Look deeper
+ return Array.prototype.concat(root, shiftLevel(removedRest, rest));
+ } else {
+ // No impact
+ return level;
+ }
+}
+
+/**
+ @param {String}
+ @return {Array<Number>}
+ */
+function levelToArray(l) {
+ return l.split('.').map(function (char) {
+ return parseInt(char, 10);
+ });
+}
+
+/**
+ @param {Array<Number>}
+ @return {String}
+ */
+function arrayToLevel(a) {
+ return a.join('.');
+}
+
+module.exports = moveArticle;