ESM: Ported HTML, Unicode, Quoted Printable and Endian operations

This commit is contained in:
n1474335 2018-05-16 17:10:50 +00:00
parent f26d175cad
commit 5362508a99
9 changed files with 1536 additions and 0 deletions

View File

@ -0,0 +1,79 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* Escape Unicode Characters operation
*/
class EscapeUnicodeCharacters extends Operation {
/**
* EscapeUnicodeCharacters constructor
*/
constructor() {
super();
this.name = "Escape Unicode Characters";
this.module = "Default";
this.description = "Converts characters to their unicode-escaped notations.<br><br>Supports the prefixes:<ul><li><code>\\u</code></li><li><code>%u</code></li><li><code>U+</code></li></ul>e.g. <code>σου</code> becomes <code>\\u03C3\\u03BF\\u03C5</code>";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Prefix",
"type": "option",
"value": ["\\u", "%u", "U+"]
},
{
"name": "Encode all chars",
"type": "boolean",
"value": false
},
{
"name": "Padding",
"type": "number",
"value": 4
},
{
"name": "Uppercase hex",
"type": "boolean",
"value": true
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const regexWhitelist = /[ -~]/i,
[prefix, encodeAll, padding, uppercaseHex] = args;
let output = "",
character = "";
for (let i = 0; i < input.length; i++) {
character = input[i];
if (!encodeAll && regexWhitelist.test(character)) {
// Its a printable ASCII character so dont escape it.
output += character;
continue;
}
let cp = character.codePointAt(0).toString(16);
if (uppercaseHex) cp = cp.toUpperCase();
output += prefix + cp.padStart(padding, "0");
}
return output;
}
}
export default EscapeUnicodeCharacters;

View File

@ -0,0 +1,335 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
/**
* From HTML Entity operation
*/
class FromHTMLEntity extends Operation {
/**
* FromHTMLEntity constructor
*/
constructor() {
super();
this.name = "From HTML Entity";
this.module = "Default";
this.description = "Converts HTML entities back to characters<br><br>e.g. <code>&amp;<span>amp;</span></code> becomes <code>&amp;</code>";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const regex = /&(#?x?[a-zA-Z0-9]{1,8});/g;
let output = "",
m,
i = 0;
while ((m = regex.exec(input))) {
// Add up to match
for (; i < m.index;)
output += input[i++];
// Add match
const bite = entityToByte[m[1]];
if (bite) {
output += Utils.chr(bite);
} else if (!bite && m[1][0] === "#" && m[1].length > 1 && /^#\d{1,6}$/.test(m[1])) {
// Numeric entity (e.g. &#10;)
const num = m[1].slice(1, m[1].length);
output += Utils.chr(parseInt(num, 10));
} else if (!bite && m[1][0] === "#" && m[1].length > 3 && /^#x[\dA-F]{2,8}$/i.test(m[1])) {
// Hex entity (e.g. &#x3A;)
const hex = m[1].slice(2, m[1].length);
output += Utils.chr(parseInt(hex, 16));
} else {
// Not a valid entity, print as normal
for (; i < regex.lastIndex;)
output += input[i++];
}
i = regex.lastIndex;
}
// Add all after final match
for (; i < input.length;)
output += input[i++];
return output;
}
}
/**
* Lookup table to translate HTML entity codes to their byte values.
*/
const entityToByte = {
"quot": 34,
"amp": 38,
"apos": 39,
"lt": 60,
"gt": 62,
"nbsp": 160,
"iexcl": 161,
"cent": 162,
"pound": 163,
"curren": 164,
"yen": 165,
"brvbar": 166,
"sect": 167,
"uml": 168,
"copy": 169,
"ordf": 170,
"laquo": 171,
"not": 172,
"shy": 173,
"reg": 174,
"macr": 175,
"deg": 176,
"plusmn": 177,
"sup2": 178,
"sup3": 179,
"acute": 180,
"micro": 181,
"para": 182,
"middot": 183,
"cedil": 184,
"sup1": 185,
"ordm": 186,
"raquo": 187,
"frac14": 188,
"frac12": 189,
"frac34": 190,
"iquest": 191,
"Agrave": 192,
"Aacute": 193,
"Acirc": 194,
"Atilde": 195,
"Auml": 196,
"Aring": 197,
"AElig": 198,
"Ccedil": 199,
"Egrave": 200,
"Eacute": 201,
"Ecirc": 202,
"Euml": 203,
"Igrave": 204,
"Iacute": 205,
"Icirc": 206,
"Iuml": 207,
"ETH": 208,
"Ntilde": 209,
"Ograve": 210,
"Oacute": 211,
"Ocirc": 212,
"Otilde": 213,
"Ouml": 214,
"times": 215,
"Oslash": 216,
"Ugrave": 217,
"Uacute": 218,
"Ucirc": 219,
"Uuml": 220,
"Yacute": 221,
"THORN": 222,
"szlig": 223,
"agrave": 224,
"aacute": 225,
"acirc": 226,
"atilde": 227,
"auml": 228,
"aring": 229,
"aelig": 230,
"ccedil": 231,
"egrave": 232,
"eacute": 233,
"ecirc": 234,
"euml": 235,
"igrave": 236,
"iacute": 237,
"icirc": 238,
"iuml": 239,
"eth": 240,
"ntilde": 241,
"ograve": 242,
"oacute": 243,
"ocirc": 244,
"otilde": 245,
"ouml": 246,
"divide": 247,
"oslash": 248,
"ugrave": 249,
"uacute": 250,
"ucirc": 251,
"uuml": 252,
"yacute": 253,
"thorn": 254,
"yuml": 255,
"OElig": 338,
"oelig": 339,
"Scaron": 352,
"scaron": 353,
"Yuml": 376,
"fnof": 402,
"circ": 710,
"tilde": 732,
"Alpha": 913,
"Beta": 914,
"Gamma": 915,
"Delta": 916,
"Epsilon": 917,
"Zeta": 918,
"Eta": 919,
"Theta": 920,
"Iota": 921,
"Kappa": 922,
"Lambda": 923,
"Mu": 924,
"Nu": 925,
"Xi": 926,
"Omicron": 927,
"Pi": 928,
"Rho": 929,
"Sigma": 931,
"Tau": 932,
"Upsilon": 933,
"Phi": 934,
"Chi": 935,
"Psi": 936,
"Omega": 937,
"alpha": 945,
"beta": 946,
"gamma": 947,
"delta": 948,
"epsilon": 949,
"zeta": 950,
"eta": 951,
"theta": 952,
"iota": 953,
"kappa": 954,
"lambda": 955,
"mu": 956,
"nu": 957,
"xi": 958,
"omicron": 959,
"pi": 960,
"rho": 961,
"sigmaf": 962,
"sigma": 963,
"tau": 964,
"upsilon": 965,
"phi": 966,
"chi": 967,
"psi": 968,
"omega": 969,
"thetasym": 977,
"upsih": 978,
"piv": 982,
"ensp": 8194,
"emsp": 8195,
"thinsp": 8201,
"zwnj": 8204,
"zwj": 8205,
"lrm": 8206,
"rlm": 8207,
"ndash": 8211,
"mdash": 8212,
"lsquo": 8216,
"rsquo": 8217,
"sbquo": 8218,
"ldquo": 8220,
"rdquo": 8221,
"bdquo": 8222,
"dagger": 8224,
"Dagger": 8225,
"bull": 8226,
"hellip": 8230,
"permil": 8240,
"prime": 8242,
"Prime": 8243,
"lsaquo": 8249,
"rsaquo": 8250,
"oline": 8254,
"frasl": 8260,
"euro": 8364,
"image": 8465,
"weierp": 8472,
"real": 8476,
"trade": 8482,
"alefsym": 8501,
"larr": 8592,
"uarr": 8593,
"rarr": 8594,
"darr": 8595,
"harr": 8596,
"crarr": 8629,
"lArr": 8656,
"uArr": 8657,
"rArr": 8658,
"dArr": 8659,
"hArr": 8660,
"forall": 8704,
"part": 8706,
"exist": 8707,
"empty": 8709,
"nabla": 8711,
"isin": 8712,
"notin": 8713,
"ni": 8715,
"prod": 8719,
"sum": 8721,
"minus": 8722,
"lowast": 8727,
"radic": 8730,
"prop": 8733,
"infin": 8734,
"ang": 8736,
"and": 8743,
"or": 8744,
"cap": 8745,
"cup": 8746,
"int": 8747,
"there4": 8756,
"sim": 8764,
"cong": 8773,
"asymp": 8776,
"ne": 8800,
"equiv": 8801,
"le": 8804,
"ge": 8805,
"sub": 8834,
"sup": 8835,
"nsub": 8836,
"sube": 8838,
"supe": 8839,
"oplus": 8853,
"otimes": 8855,
"perp": 8869,
"sdot": 8901,
"vellip": 8942,
"lceil": 8968,
"rceil": 8969,
"lfloor": 8970,
"rfloor": 8971,
"lang": 9001,
"rang": 9002,
"loz": 9674,
"spades": 9824,
"clubs": 9827,
"hearts": 9829,
"diams": 9830,
};
export default FromHTMLEntity;

View File

@ -0,0 +1,61 @@
/**
* Some parts taken from mimelib (http://github.com/andris9/mimelib)
* @author Andris Reinman
* @license MIT
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* From Quoted Printable operation
*/
class FromQuotedPrintable extends Operation {
/**
* FromQuotedPrintable constructor
*/
constructor() {
super();
this.name = "From Quoted Printable";
this.module = "Default";
this.description = "Converts QP-encoded text back to standard text.";
this.inputType = "string";
this.outputType = "byteArray";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const str = input.replace(/=(?:\r?\n|$)/g, "");
const encodedBytesCount = (str.match(/=[\da-fA-F]{2}/g) || []).length,
bufferLength = str.length - encodedBytesCount * 2,
buffer = new Array(bufferLength);
let chr, hex,
bufferPos = 0;
for (let i = 0, len = str.length; i < len; i++) {
chr = str.charAt(i);
if (chr === "=" && (hex = str.substr(i + 1, 2)) && /[\da-fA-F]{2}/.test(hex)) {
buffer[bufferPos++] = parseInt(hex, 16);
i += 2;
continue;
}
buffer[bufferPos++] = chr.charCodeAt(0);
}
return buffer;
}
}
export default FromQuotedPrintable;

View File

@ -0,0 +1,195 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* Parse colour code operation
*/
class ParseColourCode extends Operation {
/**
* ParseColourCode constructor
*/
constructor() {
super();
this.name = "Parse colour code";
this.module = "Default";
this.description = "Converts a colour code in a standard format to other standard formats and displays the colour itself.<br><br><strong>Example inputs</strong><ul><li><code>#d9edf7</code></li><li><code>rgba(217,237,247,1)</code></li><li><code>hsla(200,65%,91%,1)</code></li><li><code>cmyk(0.12, 0.04, 0.00, 0.03)</code></li></ul>";
this.inputType = "string";
this.outputType = "html";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
run(input, args) {
let m = null,
r = 0, g = 0, b = 0, a = 1;
// Read in the input
if ((m = input.match(/#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/i))) {
// Hex - #d9edf7
r = parseInt(m[1], 16);
g = parseInt(m[2], 16);
b = parseInt(m[3], 16);
} else if ((m = input.match(/rgba?\((\d{1,3}(?:\.\d+)?),\s?(\d{1,3}(?:\.\d+)?),\s?(\d{1,3}(?:\.\d+)?)(?:,\s?(\d(?:\.\d+)?))?\)/i))) {
// RGB or RGBA - rgb(217,237,247) or rgba(217,237,247,1)
r = parseFloat(m[1]);
g = parseFloat(m[2]);
b = parseFloat(m[3]);
a = m[4] ? parseFloat(m[4]) : 1;
} else if ((m = input.match(/hsla?\((\d{1,3}(?:\.\d+)?),\s?(\d{1,3}(?:\.\d+)?)%,\s?(\d{1,3}(?:\.\d+)?)%(?:,\s?(\d(?:\.\d+)?))?\)/i))) {
// HSL or HSLA - hsl(200, 65%, 91%) or hsla(200, 65%, 91%, 1)
const h_ = parseFloat(m[1]) / 360,
s_ = parseFloat(m[2]) / 100,
l_ = parseFloat(m[3]) / 100,
rgb_ = ParseColourCode._hslToRgb(h_, s_, l_);
r = rgb_[0];
g = rgb_[1];
b = rgb_[2];
a = m[4] ? parseFloat(m[4]) : 1;
} else if ((m = input.match(/cmyk\((\d(?:\.\d+)?),\s?(\d(?:\.\d+)?),\s?(\d(?:\.\d+)?),\s?(\d(?:\.\d+)?)\)/i))) {
// CMYK - cmyk(0.12, 0.04, 0.00, 0.03)
const c_ = parseFloat(m[1]),
m_ = parseFloat(m[2]),
y_ = parseFloat(m[3]),
k_ = parseFloat(m[4]);
r = Math.round(255 * (1 - c_) * (1 - k_));
g = Math.round(255 * (1 - m_) * (1 - k_));
b = Math.round(255 * (1 - y_) * (1 - k_));
}
const hsl_ = ParseColourCode._rgbToHsl(r, g, b),
h = Math.round(hsl_[0] * 360),
s = Math.round(hsl_[1] * 100),
l = Math.round(hsl_[2] * 100);
let k = 1 - Math.max(r/255, g/255, b/255),
c = (1 - r/255 - k) / (1 - k),
y = (1 - b/255 - k) / (1 - k);
m = (1 - g/255 - k) / (1 - k);
c = isNaN(c) ? "0" : c.toFixed(2);
m = isNaN(m) ? "0" : m.toFixed(2);
y = isNaN(y) ? "0" : y.toFixed(2);
k = k.toFixed(2);
const hex = "#" +
Math.round(r).toString(16).padStart(2, "0") +
Math.round(g).toString(16).padStart(2, "0") +
Math.round(b).toString(16).padStart(2, "0"),
rgb = "rgb(" + r + ", " + g + ", " + b + ")",
rgba = "rgba(" + r + ", " + g + ", " + b + ", " + a + ")",
hsl = "hsl(" + h + ", " + s + "%, " + l + "%)",
hsla = "hsla(" + h + ", " + s + "%, " + l + "%, " + a + ")",
cmyk = "cmyk(" + c + ", " + m + ", " + y + ", " + k + ")";
// Generate output
return `<div id="colorpicker" style="display: inline-block"></div>
Hex: ${hex}
RGB: ${rgb}
RGBA: ${rgba}
HSL: ${hsl}
HSLA: ${hsla}
CMYK: ${cmyk}
<script>
$('#colorpicker').colorpicker({
format: 'rgba',
color: '${rgba}',
container: true,
inline: true,
}).on('changeColor', function(e) {
var color = e.color.toRGB();
document.getElementById('input-text').value = 'rgba(' +
color.r + ', ' + color.g + ', ' + color.b + ', ' + color.a + ')';
window.app.autoBake();
});
</script>`;
}
/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_colorSpace.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* @author Mohsen (http://stackoverflow.com/a/9493060)
*
* @param {number} h - The hue
* @param {number} s - The saturation
* @param {number} l - The lightness
* @return {Array} The RGB representation
*/
static _hslToRgb(h, s, l) {
let r, g, b;
if (s === 0){
r = g = b = l; // achromatic
} else {
const hue2rgb = function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1/6) return p + (q - p) * 6 * t;
if (t < 1/2) return q;
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
};
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
/**
* Converts an RGB color value to HSL. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_colorSpace.
* Assumes r, g, and b are contained in the set [0, 255] and
* returns h, s, and l in the set [0, 1].
*
* @author Mohsen (http://stackoverflow.com/a/9493060)
*
* @param {number} r - The red color value
* @param {number} g - The green color value
* @param {number} b - The blue color value
* @return {Array} The HSL representation
*/
static _rgbToHsl(r, g, b) {
r /= 255; g /= 255; b /= 255;
const max = Math.max(r, g, b),
min = Math.min(r, g, b),
l = (max + min) / 2;
let h, s;
if (max === min) {
h = s = 0; // achromatic
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return [h, s, l];
}
}
export default ParseColourCode;

View File

@ -0,0 +1,65 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
/**
* Strip HTML tags operation
*/
class StripHTMLTags extends Operation {
/**
* StripHTMLTags constructor
*/
constructor() {
super();
this.name = "Strip HTML tags";
this.module = "Default";
this.description = "Removes all HTML tags from the input.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Remove indentation",
"type": "boolean",
"value": true
},
{
"name": "Remove excess line breaks",
"type": "boolean",
"value": true
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [removeIndentation, removeLineBreaks] = args;
input = Utils.stripHtmlTags(input);
if (removeIndentation) {
input = input.replace(/\n[ \f\t]+/g, "\n");
}
if (removeLineBreaks) {
input = input
.replace(/^\s*\n/, "") // first line
.replace(/(\n\s*){2,}/g, "\n"); // all others
}
return input;
}
}
export default StripHTMLTags;

View File

@ -0,0 +1,137 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import {toHex, fromHex} from "../lib/Hex";
import OperationError from "../errors/OperationError";
/**
* Swap endianness operation
*/
class SwapEndianness extends Operation {
/**
* SwapEndianness constructor
*/
constructor() {
super();
this.name = "Swap endianness";
this.module = "Default";
this.description = "Switches the data from big-endian to little-endian or vice-versa. Data can be read in as hexadecimal or raw bytes. It will be returned in the same format as it is entered.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Data format",
"type": "option",
"value": ["Hex", "Raw"]
},
{
"name": "Word length (bytes)",
"type": "number",
"value": 4
},
{
"name": "Pad incomplete words",
"type": "boolean",
"value": true
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [dataFormat, wordLength, padIncompleteWords] = args,
result = [],
words = [];
let i = 0,
j = 0,
data = [];
if (wordLength <= 0) {
throw new OperationError("Word length must be greater than 0");
}
// Convert input to raw data based on specified data format
switch (dataFormat) {
case "Hex":
data = fromHex(input);
break;
case "Raw":
data = Utils.strToByteArray(input);
break;
default:
data = input;
}
// Split up into words
for (i = 0; i < data.length; i += wordLength) {
const word = data.slice(i, i + wordLength);
// Pad word if too short
if (padIncompleteWords && word.length < wordLength){
for (j = word.length; j < wordLength; j++) {
word.push(0);
}
}
words.push(word);
}
// Swap endianness and flatten
for (i = 0; i < words.length; i++) {
j = words[i].length;
while (j--) {
result.push(words[i][j]);
}
}
// Convert data back to specified data format
switch (dataFormat) {
case "Hex":
return toHex(result);
case "Raw":
return Utils.byteArrayToUtf8(result);
default:
return result;
}
}
/**
* Highlight Swap endianness
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
return pos;
}
/**
* Highlight Swap endianness in reverse
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
return pos;
}
}
export default SwapEndianness;

View File

@ -0,0 +1,345 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
/**
* To HTML Entity operation
*/
class ToHTMLEntity extends Operation {
/**
* ToHTMLEntity constructor
*/
constructor() {
super();
this.name = "To HTML Entity";
this.module = "Default";
this.description = "Converts characters to HTML entities<br><br>e.g. <code>&amp;</code> becomes <code>&amp;<span>amp;</span></code>";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Convert all characters",
"type": "boolean",
"value": false
},
{
"name": "Convert to",
"type": "option",
"value": ["Named entities where possible", "Numeric entities", "Hex entities"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const convertAll = args[0],
numeric = args[1] === "Numeric entities",
hexa = args[1] === "Hex entities";
const charcodes = Utils.strToCharcode(input);
let output = "";
for (let i = 0; i < charcodes.length; i++) {
if (convertAll && numeric) {
output += "&#" + charcodes[i] + ";";
} else if (convertAll && hexa) {
output += "&#x" + Utils.hex(charcodes[i]) + ";";
} else if (convertAll) {
output += byteToEntity[charcodes[i]] || "&#" + charcodes[i] + ";";
} else if (numeric) {
if (charcodes[i] > 255 || byteToEntity.hasOwnProperty(charcodes[i])) {
output += "&#" + charcodes[i] + ";";
} else {
output += Utils.chr(charcodes[i]);
}
} else if (hexa) {
if (charcodes[i] > 255 || byteToEntity.hasOwnProperty(charcodes[i])) {
output += "&#x" + Utils.hex(charcodes[i]) + ";";
} else {
output += Utils.chr(charcodes[i]);
}
} else {
output += byteToEntity[charcodes[i]] || (
charcodes[i] > 255 ?
"&#" + charcodes[i] + ";" :
Utils.chr(charcodes[i])
);
}
}
return output;
}
}
/**
* Lookup table to translate byte values to their HTML entity codes.
*/
const byteToEntity = {
34: "&quot;",
38: "&amp;",
39: "&apos;",
60: "&lt;",
62: "&gt;",
160: "&nbsp;",
161: "&iexcl;",
162: "&cent;",
163: "&pound;",
164: "&curren;",
165: "&yen;",
166: "&brvbar;",
167: "&sect;",
168: "&uml;",
169: "&copy;",
170: "&ordf;",
171: "&laquo;",
172: "&not;",
173: "&shy;",
174: "&reg;",
175: "&macr;",
176: "&deg;",
177: "&plusmn;",
178: "&sup2;",
179: "&sup3;",
180: "&acute;",
181: "&micro;",
182: "&para;",
183: "&middot;",
184: "&cedil;",
185: "&sup1;",
186: "&ordm;",
187: "&raquo;",
188: "&frac14;",
189: "&frac12;",
190: "&frac34;",
191: "&iquest;",
192: "&Agrave;",
193: "&Aacute;",
194: "&Acirc;",
195: "&Atilde;",
196: "&Auml;",
197: "&Aring;",
198: "&AElig;",
199: "&Ccedil;",
200: "&Egrave;",
201: "&Eacute;",
202: "&Ecirc;",
203: "&Euml;",
204: "&Igrave;",
205: "&Iacute;",
206: "&Icirc;",
207: "&Iuml;",
208: "&ETH;",
209: "&Ntilde;",
210: "&Ograve;",
211: "&Oacute;",
212: "&Ocirc;",
213: "&Otilde;",
214: "&Ouml;",
215: "&times;",
216: "&Oslash;",
217: "&Ugrave;",
218: "&Uacute;",
219: "&Ucirc;",
220: "&Uuml;",
221: "&Yacute;",
222: "&THORN;",
223: "&szlig;",
224: "&agrave;",
225: "&aacute;",
226: "&acirc;",
227: "&atilde;",
228: "&auml;",
229: "&aring;",
230: "&aelig;",
231: "&ccedil;",
232: "&egrave;",
233: "&eacute;",
234: "&ecirc;",
235: "&euml;",
236: "&igrave;",
237: "&iacute;",
238: "&icirc;",
239: "&iuml;",
240: "&eth;",
241: "&ntilde;",
242: "&ograve;",
243: "&oacute;",
244: "&ocirc;",
245: "&otilde;",
246: "&ouml;",
247: "&divide;",
248: "&oslash;",
249: "&ugrave;",
250: "&uacute;",
251: "&ucirc;",
252: "&uuml;",
253: "&yacute;",
254: "&thorn;",
255: "&yuml;",
338: "&OElig;",
339: "&oelig;",
352: "&Scaron;",
353: "&scaron;",
376: "&Yuml;",
402: "&fnof;",
710: "&circ;",
732: "&tilde;",
913: "&Alpha;",
914: "&Beta;",
915: "&Gamma;",
916: "&Delta;",
917: "&Epsilon;",
918: "&Zeta;",
919: "&Eta;",
920: "&Theta;",
921: "&Iota;",
922: "&Kappa;",
923: "&Lambda;",
924: "&Mu;",
925: "&Nu;",
926: "&Xi;",
927: "&Omicron;",
928: "&Pi;",
929: "&Rho;",
931: "&Sigma;",
932: "&Tau;",
933: "&Upsilon;",
934: "&Phi;",
935: "&Chi;",
936: "&Psi;",
937: "&Omega;",
945: "&alpha;",
946: "&beta;",
947: "&gamma;",
948: "&delta;",
949: "&epsilon;",
950: "&zeta;",
951: "&eta;",
952: "&theta;",
953: "&iota;",
954: "&kappa;",
955: "&lambda;",
956: "&mu;",
957: "&nu;",
958: "&xi;",
959: "&omicron;",
960: "&pi;",
961: "&rho;",
962: "&sigmaf;",
963: "&sigma;",
964: "&tau;",
965: "&upsilon;",
966: "&phi;",
967: "&chi;",
968: "&psi;",
969: "&omega;",
977: "&thetasym;",
978: "&upsih;",
982: "&piv;",
8194: "&ensp;",
8195: "&emsp;",
8201: "&thinsp;",
8204: "&zwnj;",
8205: "&zwj;",
8206: "&lrm;",
8207: "&rlm;",
8211: "&ndash;",
8212: "&mdash;",
8216: "&lsquo;",
8217: "&rsquo;",
8218: "&sbquo;",
8220: "&ldquo;",
8221: "&rdquo;",
8222: "&bdquo;",
8224: "&dagger;",
8225: "&Dagger;",
8226: "&bull;",
8230: "&hellip;",
8240: "&permil;",
8242: "&prime;",
8243: "&Prime;",
8249: "&lsaquo;",
8250: "&rsaquo;",
8254: "&oline;",
8260: "&frasl;",
8364: "&euro;",
8465: "&image;",
8472: "&weierp;",
8476: "&real;",
8482: "&trade;",
8501: "&alefsym;",
8592: "&larr;",
8593: "&uarr;",
8594: "&rarr;",
8595: "&darr;",
8596: "&harr;",
8629: "&crarr;",
8656: "&lArr;",
8657: "&uArr;",
8658: "&rArr;",
8659: "&dArr;",
8660: "&hArr;",
8704: "&forall;",
8706: "&part;",
8707: "&exist;",
8709: "&empty;",
8711: "&nabla;",
8712: "&isin;",
8713: "&notin;",
8715: "&ni;",
8719: "&prod;",
8721: "&sum;",
8722: "&minus;",
8727: "&lowast;",
8730: "&radic;",
8733: "&prop;",
8734: "&infin;",
8736: "&ang;",
8743: "&and;",
8744: "&or;",
8745: "&cap;",
8746: "&cup;",
8747: "&int;",
8756: "&there4;",
8764: "&sim;",
8773: "&cong;",
8776: "&asymp;",
8800: "&ne;",
8801: "&equiv;",
8804: "&le;",
8805: "&ge;",
8834: "&sub;",
8835: "&sup;",
8836: "&nsub;",
8838: "&sube;",
8839: "&supe;",
8853: "&oplus;",
8855: "&otimes;",
8869: "&perp;",
8901: "&sdot;",
8942: "&vellip;",
8968: "&lceil;",
8969: "&rceil;",
8970: "&lfloor;",
8971: "&rfloor;",
9001: "&lang;",
9002: "&rang;",
9674: "&loz;",
9824: "&spades;",
9827: "&clubs;",
9829: "&hearts;",
9830: "&diams;",
};
export default ToHTMLEntity;

View File

@ -0,0 +1,244 @@
/**
* Some parts taken from mimelib (http://github.com/andris9/mimelib)
* @author Andris Reinman
* @license MIT
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* To Quoted Printable operation
*/
class ToQuotedPrintable extends Operation {
/**
* ToQuotedPrintable constructor
*/
constructor() {
super();
this.name = "To Quoted Printable";
this.module = "Default";
this.description = "Quoted-Printable, or QP encoding, is an encoding using printable ASCII characters (alphanumeric and the equals sign '=') to transmit 8-bit data over a 7-bit data path or, generally, over a medium which is not 8-bit clean. It is defined as a MIME content transfer encoding for use in e-mail.<br><br>QP works by using the equals sign '=' as an escape character. It also limits line length to 76, as some software has limits on line length.";
this.inputType = "byteArray";
this.outputType = "string";
this.args = [];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
let mimeEncodedStr = this.mimeEncode(input);
// fix line breaks
mimeEncodedStr = mimeEncodedStr.replace(/\r?\n|\r/g, function() {
return "\r\n";
}).replace(/[\t ]+$/gm, function(spaces) {
return spaces.replace(/ /g, "=20").replace(/\t/g, "=09");
});
return this._addSoftLinebreaks(mimeEncodedStr, "qp");
}
/** @license
========================================================================
mimelib: http://github.com/andris9/mimelib
Copyright (c) 2011-2012 Andris Reinman
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/**
* Encodes mime data.
*
* @param {byteArray} buffer
* @returns {string}
*/
mimeEncode(buffer) {
const ranges = [
[0x09],
[0x0A],
[0x0D],
[0x20],
[0x21],
[0x23, 0x3C],
[0x3E],
[0x40, 0x5E],
[0x60, 0x7E]
];
let result = "";
for (let i = 0, len = buffer.length; i < len; i++) {
if (this._checkRanges(buffer[i], ranges)) {
result += String.fromCharCode(buffer[i]);
continue;
}
result += "=" + (buffer[i] < 0x10 ? "0" : "") + buffer[i].toString(16).toUpperCase();
}
return result;
}
/**
* Checks if a given number falls within a given set of ranges.
*
* @private
* @param {number} nr
* @param {byteArray[]} ranges
* @returns {bolean}
*/
_checkRanges(nr, ranges) {
for (let i = ranges.length - 1; i >= 0; i--) {
if (!ranges[i].length)
continue;
if (ranges[i].length === 1 && nr === ranges[i][0])
return true;
if (ranges[i].length === 2 && nr >= ranges[i][0] && nr <= ranges[i][1])
return true;
}
return false;
}
/**
* Adds soft line breaks to a string.
* Lines can't be longer that 76 + <CR><LF> = 78 bytes
* http://tools.ietf.org/html/rfc2045#section-6.7
*
* @private
* @param {string} str
* @param {string} encoding
* @returns {string}
*/
_addSoftLinebreaks(str, encoding) {
const lineLengthMax = 76;
encoding = (encoding || "base64").toString().toLowerCase().trim();
if (encoding === "qp") {
return this._addQPSoftLinebreaks(str, lineLengthMax);
} else {
return this._addBase64SoftLinebreaks(str, lineLengthMax);
}
}
/**
* Adds soft line breaks to a base64 string.
*
* @private
* @param {string} base64EncodedStr
* @param {number} lineLengthMax
* @returns {string}
*/
_addBase64SoftLinebreaks(base64EncodedStr, lineLengthMax) {
base64EncodedStr = (base64EncodedStr || "").toString().trim();
return base64EncodedStr.replace(new RegExp(".{" + lineLengthMax + "}", "g"), "$&\r\n").trim();
}
/**
* Adds soft line breaks to a quoted printable string.
*
* @private
* @param {string} mimeEncodedStr
* @param {number} lineLengthMax
* @returns {string}
*/
_addQPSoftLinebreaks(mimeEncodedStr, lineLengthMax) {
const len = mimeEncodedStr.length,
lineMargin = Math.floor(lineLengthMax / 3);
let pos = 0,
match, code, line,
result = "";
// insert soft linebreaks where needed
while (pos < len) {
line = mimeEncodedStr.substr(pos, lineLengthMax);
if ((match = line.match(/\r\n/))) {
line = line.substr(0, match.index + match[0].length);
result += line;
pos += line.length;
continue;
}
if (line.substr(-1) === "\n") {
// nothing to change here
result += line;
pos += line.length;
continue;
} else if ((match = line.substr(-lineMargin).match(/\n.*?$/))) {
// truncate to nearest line break
line = line.substr(0, line.length - (match[0].length - 1));
result += line;
pos += line.length;
continue;
} else if (line.length > lineLengthMax - lineMargin && (match = line.substr(-lineMargin).match(/[ \t.,!?][^ \t.,!?]*$/))) {
// truncate to nearest space
line = line.substr(0, line.length - (match[0].length - 1));
} else if (line.substr(-1) === "\r") {
line = line.substr(0, line.length - 1);
} else {
if (line.match(/=[\da-f]{0,2}$/i)) {
// push incomplete encoding sequences to the next line
if ((match = line.match(/=[\da-f]{0,1}$/i))) {
line = line.substr(0, line.length - match[0].length);
}
// ensure that utf-8 sequences are not split
while (line.length > 3 && line.length < len - pos && !line.match(/^(?:=[\da-f]{2}){1,4}$/i) && (match = line.match(/=[\da-f]{2}$/ig))) {
code = parseInt(match[0].substr(1, 2), 16);
if (code < 128) {
break;
}
line = line.substr(0, line.length - 3);
if (code >= 0xC0) {
break;
}
}
}
}
if (pos + line.length < len && line.substr(-1) !== "\n") {
if (line.length === 76 && line.match(/=[\da-f]{2}$/i)) {
line = line.substr(0, line.length - 3);
} else if (line.length === 76) {
line = line.substr(0, line.length - 1);
}
pos += line.length;
line += "=\r\n";
} else {
pos += line.length;
}
result += line;
}
return result;
}
}
export default ToQuotedPrintable;

View File

@ -0,0 +1,75 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
/**
* Unescape Unicode Characters operation
*/
class UnescapeUnicodeCharacters extends Operation {
/**
* UnescapeUnicodeCharacters constructor
*/
constructor() {
super();
this.name = "Unescape Unicode Characters";
this.module = "Default";
this.description = "Converts unicode-escaped character notation back into raw characters.<br><br>Supports the prefixes:<ul><li><code>\\u</code></li><li><code>%u</code></li><li><code>U+</code></li></ul>e.g. <code>\\u03c3\\u03bf\\u03c5</code> becomes <code>σου</code>";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Prefix",
"type": "option",
"value": ["\\u", "%u", "U+"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const prefix = prefixToRegex[args[0]],
regex = new RegExp(prefix+"([a-f\\d]{4})", "ig");
let output = "",
m,
i = 0;
while ((m = regex.exec(input))) {
// Add up to match
output += input.slice(i, m.index);
i = m.index;
// Add match
output += Utils.chr(parseInt(m[1], 16));
i = regex.lastIndex;
}
// Add all after final match
output += input.slice(i, input.length);
return output;
}
}
/**
* Lookup table to add prefixes to unicode delimiters so that they can be used in a regex.
*/
const prefixToRegex = {
"\\u": "\\\\u",
"%u": "%u",
"U+": "U\\+"
};
export default UnescapeUnicodeCharacters;