/*\ title: $:/core/modules/filters/x-listops.js type: application/javascript module-type: filteroperator Extended filter operators to manipulate the current list. \*/ (function () { /*jslint node: true, browser: true */ /*global $tw: false */ "use strict"; /* Fetch titles from the current list */ var prepare_results = function (source) { var results = []; source(function (tiddler, title) { results.push(title); }); return results; }; /* Moves a number of items from the tail of the current list before the item named in the operand */ exports.putbefore = function (source, operator) { var results = prepare_results(source), index = results.indexOf(operator.operand), count = $tw.utils.getInt(operator.suffix,1); return (index === -1) ? results.slice(0, -1) : results.slice(0, index).concat(results.slice(-count)).concat(results.slice(index, -count)); }; /* Moves a number of items from the tail of the current list after the item named in the operand */ exports.putafter = function (source, operator) { var results = prepare_results(source), index = results.indexOf(operator.operand), count = $tw.utils.getInt(operator.suffix,1); return (index === -1) ? results.slice(0, -1) : results.slice(0, index + 1).concat(results.slice(-count)).concat(results.slice(index + 1, -count)); }; /* Replaces the item named in the operand with a number of items from the tail of the current list */ exports.replace = function (source, operator) { var results = prepare_results(source), index = results.indexOf(operator.operand), count = $tw.utils.getInt(operator.suffix,1); return (index === -1) ? results.slice(0, -count) : results.slice(0, index).concat(results.slice(-count)).concat(results.slice(index + 1, -count)); }; /* Moves a number of items from the tail of the current list to the head of the list */ exports.putfirst = function (source, operator) { var results = prepare_results(source), count = $tw.utils.getInt(operator.suffix,1); return results.slice(-count).concat(results.slice(0, -count)); }; /* Moves a number of items from the head of the current list to the tail of the list */ exports.putlast = function (source, operator) { var results = prepare_results(source), count = $tw.utils.getInt(operator.suffix,1); return results.slice(count).concat(results.slice(0, count)); }; /* Moves the item named in the operand a number of places forward or backward in the list */ exports.move = function (source, operator) { var results = prepare_results(source), index = results.indexOf(operator.operand), count = $tw.utils.getInt(operator.suffix,1), marker = results.splice(index, 1), offset = (index + count) > 0 ? index + count : 0; return results.slice(0, offset).concat(marker).concat(results.slice(offset)); }; /* Returns the items from the current list that are after the item named in the operand */ exports.allafter = function (source, operator) { var results = prepare_results(source), index = results.indexOf(operator.operand); return (index === -1) ? [] : (operator.suffix) ? results.slice(index) : results.slice(index + 1); }; /* Returns the items from the current list that are before the item named in the operand */ exports.allbefore = function (source, operator) { var results = prepare_results(source), index = results.indexOf(operator.operand); return (index === -1) ? [] : (operator.suffix) ? results.slice(0, index + 1) : results.slice(0, index); }; /* Appends the items listed in the operand array to the tail of the current list */ exports.append = function (source, operator) { var append = $tw.utils.parseStringArray(operator.operand, "true"), results = prepare_results(source), count = parseInt(operator.suffix) || append.length; return (append.length === 0) ? results : (operator.prefix) ? results.concat(append.slice(-count)) : results.concat(append.slice(0, count)); }; /* Prepends the items listed in the operand array to the head of the current list */ exports.prepend = function (source, operator) { var prepend = $tw.utils.parseStringArray(operator.operand, "true"), results = prepare_results(source), count = $tw.utils.getInt(operator.suffix,prepend.length); return (prepend.length === 0) ? results : (operator.prefix) ? prepend.slice(-count).concat(results) : prepend.slice(0, count).concat(results); }; /* Returns all items from the current list except the items listed in the operand array */ exports.remove = function (source, operator) { var array = $tw.utils.parseStringArray(operator.operand, "true"), results = prepare_results(source), count = parseInt(operator.suffix) || array.length, p, len, index; len = array.length - 1; for (p = 0; p < count; ++p) { if (operator.prefix) { index = results.indexOf(array[len - p]); } else { index = results.indexOf(array[p]); } if (index !== -1) { results.splice(index, 1); } } return results; }; /* Returns all items from the current list sorted in the order of the items in the operand array */ exports.sortby = function (source, operator) { var results = prepare_results(source); if (!results || results.length < 2) { return results; } var lookup = $tw.utils.parseStringArray(operator.operand, "true"); results.sort(function (a, b) { return lookup.indexOf(a) - lookup.indexOf(b); }); return results; }; /* Removes all duplicate items from the current list */ exports.unique = function (source, operator) { var results = prepare_results(source); var set = results.reduce(function (a, b) { if (a.indexOf(b) < 0) { a.push(b); } return a; }, []); return set; }; var cycleValueInArray = function(results,operands,stepSize) { var resultsIndex, step = stepSize || 1, i = 0, opLength = operands.length, nextOperandIndex; for(i; i < opLength; i++) { resultsIndex = results.indexOf(operands[i]); if(resultsIndex !== -1) { break; } } if(resultsIndex !== -1) { i = i + step; nextOperandIndex = (i < opLength ? i : i % opLength); if(operands.length > 1) { results.splice(resultsIndex,1,operands[nextOperandIndex]); } else { results.splice(resultsIndex,1); } } else { results.push(operands[0]); } return results; } /* Toggles an item in the current list. */ exports.toggle = function(source,operator) { return cycleValueInArray(prepare_results(source),operator.operands); } exports.cycle = function(source,operator) { var results = prepare_results(source), operands = (operator.operand.length ? $tw.utils.parseStringArray(operator.operand, "true") : [""]), step = $tw.utils.getInt(operator.operands[1]||"",1); if(step < 0) { operands.reverse(); step = Math.abs(step); } return cycleValueInArray(results,operands,step); } })();