/*\ title: $:/core/modules/filters/strings.js type: application/javascript module-type: filteroperator Filter operators for strings. Unary/binary operators work on each item in turn, and return a new item list. Sum/product/maxall/minall operate on the entire list, returning a single item. \*/ (function(){ /*jslint node: true, browser: true */ /*global $tw: false */ "use strict"; exports.length = makeStringBinaryOperator( function(a) {return ["" + ("" + a).length];} ); exports.uppercase = makeStringBinaryOperator( function(a) {return [("" + a).toUpperCase()];} ); exports.lowercase = makeStringBinaryOperator( function(a) {return [("" + a).toLowerCase()];} ); exports.sentencecase = makeStringBinaryOperator( function(a) {return [$tw.utils.toSentenceCase(a)];} ); exports.titlecase = makeStringBinaryOperator( function(a) {return [$tw.utils.toTitleCase(a)];} ); exports.trim = function(source,operator,options) { var result = [], suffix = operator.suffix || "", operand = (operator.operand || ""), fnCalc; if(suffix === "prefix") { fnCalc = function(a,b) {return [$tw.utils.trimPrefix(a,b)];} } else if(suffix === "suffix") { fnCalc = function(a,b) {return [$tw.utils.trimSuffix(a,b)];} } else { if(operand === "") { fnCalc = function(a) {return [$tw.utils.trim(a)];} } else { fnCalc = function(a,b) {return [$tw.utils.trimSuffix($tw.utils.trimPrefix(a,b),b)];} } } source(function(tiddler,title) { Array.prototype.push.apply(result,fnCalc(title,operand)); }); return result; }; exports.split = makeStringBinaryOperator( function(a,b) {return ("" + a).split(b);} ); exports["enlist-input"] = makeStringBinaryOperator( function(a,o,s) {return $tw.utils.parseStringArray("" + a,(s === "raw"));} ); exports.join = makeStringReducingOperator( function(accumulator,value,operand) { if(accumulator === null) { return value; } else { return accumulator + operand + value; } },null ); var dmp = require("$:/core/modules/utils/diff-match-patch/diff_match_patch.js"); exports.levenshtein = makeStringBinaryOperator( function(a,b) { var dmpObject = new dmp.diff_match_patch(), diffs = dmpObject.diff_main(a,b); return [dmpObject.diff_levenshtein(diffs) + ""]; } ); // these two functions are adapted from https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs function diffLineWordMode(text1,text2,mode) { var dmpObject = new dmp.diff_match_patch(); var a = diffPartsToChars(text1,text2,mode); var lineText1 = a.chars1; var lineText2 = a.chars2; var lineArray = a.lineArray; var diffs = dmpObject.diff_main(lineText1,lineText2,false); dmpObject.diff_charsToLines_(diffs,lineArray); return diffs; } function diffPartsToChars(text1,text2,mode) { var lineArray = []; var lineHash = {}; lineArray[0] = ''; function diff_linesToPartsMunge_(text,mode) { var chars = ''; var lineStart = 0; var lineEnd = -1; var lineArrayLength = lineArray.length, regexpResult; var searchRegexp = /\W+/g; while(lineEnd < text.length - 1) { if(mode === "words") { regexpResult = searchRegexp.exec(text); lineEnd = searchRegexp.lastIndex; if(regexpResult === null) { lineEnd = text.length; } lineEnd = --lineEnd; } else { lineEnd = text.indexOf('\n', lineStart); if(lineEnd == -1) { lineEnd = text.length - 1; } } var line = text.substring(lineStart, lineEnd + 1); if(lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : (lineHash[line] !== undefined)) { chars += String.fromCharCode(lineHash[line]); } else { if (lineArrayLength == maxLines) { line = text.substring(lineStart); lineEnd = text.length; } chars += String.fromCharCode(lineArrayLength); lineHash[line] = lineArrayLength; lineArray[lineArrayLength++] = line; } lineStart = lineEnd + 1; } return chars; } var maxLines = 40000; var chars1 = diff_linesToPartsMunge_(text1,mode); maxLines = 65535; var chars2 = diff_linesToPartsMunge_(text2,mode); return {chars1: chars1, chars2: chars2, lineArray: lineArray}; }; exports.makepatches = function(source,operator,options) { var dmpObject = new dmp.diff_match_patch(), suffix = operator.suffix || "", result = []; source(function(tiddler,title) { var diffs, patches; if(suffix === "lines" || suffix === "words") { diffs = diffLineWordMode(title,operator.operand,suffix); patches = dmpObject.patch_make(title,diffs); } else { patches = dmpObject.patch_make(title,operator.operand); } Array.prototype.push.apply(result,[dmpObject.patch_toText(patches)]); }); return result; }; exports.applypatches = makeStringBinaryOperator( function(a,b) { var dmpObject = new dmp.diff_match_patch(), patches; try { patches = dmpObject.patch_fromText(b); } catch(e) { } if(patches) { return [dmpObject.patch_apply(patches,a)[0]]; } else { return [a]; } } ); function makeStringBinaryOperator(fnCalc) { return function(source,operator,options) { var result = []; source(function(tiddler,title) { Array.prototype.push.apply(result,fnCalc(title,operator.operand || "",operator.suffix || "")); }); return result; }; } function makeStringReducingOperator(fnCalc,initialValue) { return function(source,operator,options) { var result = []; source(function(tiddler,title) { result.push(title); }); if(result.length === 0) { return []; } return [result.reduce(function(accumulator,currentValue) { return fnCalc(accumulator,currentValue,operator.operand || ""); },initialValue) || ""]; }; } exports.splitregexp = function(source,operator,options) { var result = [], suffix = operator.suffix || "", flags = (suffix.indexOf("m") !== -1 ? "m" : "") + (suffix.indexOf("i") !== -1 ? "i" : ""), regExp; try { regExp = new RegExp(operator.operand || "",flags); } catch(ex) { return ["RegExp error: " + ex]; } source(function(tiddler,title) { Array.prototype.push.apply(result,title.split(regExp)); }); return result; }; exports["search-replace"] = function(source,operator,options) { var results = [], suffixes = operator.suffixes || [], flagSuffix = (suffixes[0] ? (suffixes[0][0] || "") : ""), flags = (flagSuffix.indexOf("g") !== -1 ? "g" : "") + (flagSuffix.indexOf("i") !== -1 ? "i" : "") + (flagSuffix.indexOf("m") !== -1 ? "m" : ""), isRegExp = (suffixes[1] && suffixes[1][0] === "regexp") ? true : false, //Escape regexp characters if the operand is not a regular expression searchTerm = isRegExp ? operator.operand : $tw.utils.escapeRegExp(operator.operand), //Escape $ character in replacement string if not in regular expression mode replacement = isRegExp ? operator.operands[1] : (operator.operands[1]||"").replace(/\$/g,"$$$$"), regExp; try { regExp = new RegExp(searchTerm,flags); } catch(ex) { return ["RegExp error: " + ex]; } source(function(tiddler,title) { if(title && (operator.operands.length > 1)) { results.push( title.replace(regExp,replacement) ); regExp.lastIndex = 0; } else { results.push(title); } }); return results; }; exports.pad = function(source,operator,options) { var results = [], targetLength = operator.operand ? parseInt(operator.operand) : 0, fill = operator.operands[1] || "0"; source(function(tiddler,title) { if(title && title.length) { if(title.length >= targetLength) { results.push(title); } else { var padString = "", padStringLength = targetLength - title.length; while (padStringLength > padString.length) { padString += fill; } //make sure we do not exceed the specified length padString = padString.slice(0,padStringLength); if(operator.suffix && (operator.suffix === "suffix")) { title = title + padString; } else { title = padString + title; } results.push(title); } } }); return results; } exports.charcode = function(source,operator,options) { var chars = []; $tw.utils.each(operator.operands,function(operand) { if(operand !== "") { chars.push(String.fromCharCode($tw.utils.parseInt(operand))); } }); return [chars.join("")]; }; })();