Skip to content

Instantly share code, notes, and snippets.

@danrouse
Created May 3, 2018 16:22
Show Gist options
  • Save danrouse/52212f0de2fbfe33cfc56583f20ccb74 to your computer and use it in GitHub Desktop.
Save danrouse/52212f0de2fbfe33cfc56583f20ccb74 to your computer and use it in GitHub Desktop.
gmail `compose` query parameter encoder/decoder
const fullAlphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
const restrictedAlphabet = 'BCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz';
const threadPrefix = 'thread-';
const messagePrefix = 'msg-';
const isWhitespace = str => /^[\s\xa0]*$/.test(str);
const isInvalidString = str => str ? (str.indexOf(threadPrefix) !== -1 || str.indexOf(messagePrefix) !== -1) : false;
const encode = function(str) {
if (isWhitespace(str)) return str;
str = str.replace(threadPrefix, '');
return transliterate(btoa(str).replace(/=/g, ''), fullAlphabet, restrictedAlphabet);
};
const decode = function(str) {
if (isInvalidString(str) || !/^[BCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz]*$/.test(str)) return str;
try {
const transliterated = atob(transliterate(str, restrictedAlphabet, fullAlphabet))
return transliterated.indexOf(threadPrefix) === -1 ? threadPrefix + transliterated : transliterated;
} catch (err) {
return str;
}
};
const transliterate = function(subject, inputAlphabet, outputAlphabet) {
if (!outputAlphabet) throw Error('rd');
let i, j;
const inputAlphabetSize = inputAlphabet.length;
const outputAlphabetSize = outputAlphabet.length;
let isEqual = true;
for (i = 0; i < subject.length; i++)
if (subject.charAt(i) != inputAlphabet.charAt(0)) {
isEqual = false;
break;
}
if (isEqual) return outputAlphabet.charAt(0);
const inputAlphabetMap = {};
for (i = 0; i < inputAlphabetSize; i++) inputAlphabetMap[inputAlphabet.charAt(i)] = i;
const inputIndices = [];
for (i = subject.length - 1; i >= 0; i--) {
const char = subject.charAt(i);
if (typeof inputAlphabetMap[char] === 'undefined') throw Error("sd`" + subject + "`" + inputAlphabet + "`" + char);
inputIndices.push(inputAlphabetMap[char]);
}
const outputIndices = [];
for (i = inputIndices.length - 1; i >= 0; i--) {
let offset = 0;
for (j = 0; j < outputIndices.length; j++) {
let index = outputIndices[j] * inputAlphabetSize + offset;
if (index >= outputAlphabetSize) {
const remainder = index % outputAlphabetSize;
offset = (index - remainder) / outputAlphabetSize;
index = remainder;
} else {
offset = 0;
}
outputIndices[j] = index;
}
while (offset) {
const remainder = offset % outputAlphabetSize;
outputIndices.push(remainder);
offset = (offset - remainder) / outputAlphabetSize;
}
offset = inputIndices[i];
for (j = 0; offset; j++) {
if (j >= outputIndices.length) outputIndices.push(0);
let index = outputIndices[j] + offset;
if (index >= outputAlphabetSize) {
const remainder = index % outputAlphabetSize;
offset = (index - remainder) / outputAlphabetSize;
index = remainder;
} else {
offset = 0;
}
outputIndices[j] = index;
}
}
const outputBuffer = [];
for (i = outputIndices.length - 1; i >= 0; i--) {
const index = outputIndices[i];
if (index >= outputAlphabet.length || index < 0) throw Error("td`" + outputIndices + "`" + index);
outputBuffer.push(outputAlphabet.charAt(index));
}
return outputBuffer.join('');
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment