diff --git a/q/README.md b/q/README.md new file mode 100644 index 0000000..da6ea14 --- /dev/null +++ b/q/README.md @@ -0,0 +1,5 @@ +# [`Q` encoder/decoder](http://mothereff.in/q) + +This tool can be used to encode/decode any text using the `Q` encoding. It uses [_q-encoding](http://mths.be/q) under the hood. + +Made by [Mathias Bynens](http://mathiasbynens.be/). diff --git a/q/eff.css b/q/eff.css new file mode 100644 index 0000000..5c8e48e --- /dev/null +++ b/q/eff.css @@ -0,0 +1,87 @@ +html, textarea { + font: 1em/1.6 sans-serif; +} + +body { + max-width: 40em; + padding: 0 1em; +} + +h1 { + text-align: center; + font-size: 1.3em; + margin: 0 0 .5em; + padding-top: 1em; +} + +h2 { + font-size: 1em; +} + +a { + color: #333; + text-decoration: none; + border-bottom: 1px solid #aaa; + padding: .1em .2em; +} + +a:hover, a:focus { + color: #fff; + border-color: #036; + background: #36c; +} + +textarea { + font-family: Monaco, Consolas, monospace; +} + +#footer { + margin-top: 2em; + text-align: center; +} + +textarea { + border: 3px double green; + background: #90ee90; + width: 100%; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + display: block; + margin: 1em 0 .5em; + padding: .7em; + resize: vertical; + min-height: 9.5em; +} + +code { + font-family: Monaco, Consolas, monospace; + font-size: .9em; + white-space: pre; + white-space: pre-wrap; + word-wrap: break-word; +} + +.invalid, :invalid { + border-color: red; + background: #ffb6c1; +} + +@media (min-width: 42em) { + + html { + font-size: 1.2em; + background: #c4c4c4; + height: 100%; + } + + body { + margin: 0 auto; + padding: 0 2em; + min-height: 100%; + background: #fff; + border: solid #aaa; + border-width: 0 1px; + } + +} diff --git a/q/eff.js b/q/eff.js new file mode 100644 index 0000000..7fcfc12 --- /dev/null +++ b/q/eff.js @@ -0,0 +1,72 @@ +(function(window, document) { + + var textareas = document.getElementsByTagName('textarea'); + var decoded = textareas[0]; + var encoded = textareas[1]; + var permalink = document.getElementById('permalink'); + // http://mathiasbynens.be/notes/localstorage-pattern + var storage = (function() { + var uid = new Date; + var storage; + var result; + try { + (storage = window.localStorage).setItem(uid, uid); + result = storage.getItem(uid) == uid; + storage.removeItem(uid); + return result && storage; + } catch (exception) {} + }()); + + function encode(string) { + // URL-encode some more characters to avoid issues when using permalink URLs in Markdown + return encodeURIComponent(string).replace(/['()_*]/g, function(character) { + return '%' + character.charCodeAt().toString(16); + }); + } + + function update() { + var shouldDecode = this == encoded; + var value; + if (shouldDecode) { + value = utf8.decode(q.decode(encoded.value)); + decoded.value = value; + } else { + value = q.encode(utf8.encode(decoded.value)); + encoded.value = value; + } + value = decoded.value; + permalink.hash = encode(value); + storage && (storage.q = value); + }; + + // http://mathiasbynens.be/notes/oninput + decoded.onkeyup = encoded.onkeyup = update; + decoded.oninput = encoded.oninput = function() { + decoded.onkeyup = encoded.onkeyup = null; + update.call(this); + }; + + if (storage) { + storage.q && (decoded.value = storage.q); + update(); + } + + window.onhashchange = function() { + decoded.value = decodeURIComponent(location.hash.slice(1)); + update(); + }; + + if (location.hash) { + window.onhashchange(); + } + +}(this, document)); + +// Google Analytics +window._gaq = [['_setAccount', 'UA-6065217-60'], ['_trackPageview']]; +(function(d, t) { + var g = d.createElement(t), + s = d.getElementsByTagName(t)[0]; + g.src = '//www.google-analytics.com/ga.js'; + s.parentNode.insertBefore(g, s); +}(document, 'script')); diff --git a/q/index.html b/q/index.html new file mode 100644 index 0000000..05833b3 --- /dev/null +++ b/q/index.html @@ -0,0 +1,19 @@ + + + +`Q` encoder/decoder + + + +

Q encoder/decoder

+ +

Decoded:

+ +

Encoded: ()

+ +

About this tool

+

This tool uses q-encoding and utf8.js to do all the encoding/decoding. +

+ + + diff --git a/q/vendor/q.js b/q/vendor/q.js new file mode 100644 index 0000000..ac75f53 --- /dev/null +++ b/q/vendor/q.js @@ -0,0 +1,88 @@ +/*! http://mths.be/q v0.1.1 by @mathias | MIT license */ +;(function(root) { + + // Detect free variables `exports`. + var freeExports = typeof exports == 'object' && exports; + + // Detect free variable `module`. + var freeModule = typeof module == 'object' && module && + module.exports == freeExports && module; + + // Detect free variable `global`, from Node.js or Browserified code, and use + // it as `root`. + var freeGlobal = typeof global == 'object' && global; + if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) { + root = freeGlobal; + } + + /*--------------------------------------------------------------------------*/ + + // http://tools.ietf.org/html/rfc2047#section-4.2 + var stringFromCharCode = String.fromCharCode; + var decode = function(input) { + return input + // Decode `_` into a space. This is character-encoding-independent; + // see http://tools.ietf.org/html/rfc2047#section-4.2, item 2. + .replace(/_/g, ' ') + // Decode escape sequences of the form `=XX` where `XX` is any + // combination of two hexidecimal digits. For optimal compatibility, + // lowercase hexadecimal digits are supported as well. See + // http://tools.ietf.org/html/rfc2045#section-6.7, note 1. + .replace(/=([a-fA-F0-9]{2})/g, function($0, $1) { + var codePoint = parseInt($1, 16); + return stringFromCharCode(codePoint); + }); + }; + + var regexUnsafeSymbols = /[\0-\x1F=\?_\x7F-\uD7FF\uDC00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF]/g; + var encode = function(string) { + // Note: this assumes the input is already encoded into octets (e.g. using + // UTF-8), and that the resulting octets are within the extended ASCII + // range. + return string + // Encode symbols that are definitely unsafe (i.e. unsafe in any context). + .replace(regexUnsafeSymbols, function(symbol) { + if (symbol > '\xFF') { + throw RangeError( + '`q.encode()` expects extended ASCII input only. Don\u2019t ' + + 'forget to encode the input first using a character encoding ' + + 'like UTF-8.' + ); + } + var codePoint = symbol.charCodeAt(0); + var hexadecimal = codePoint.toString(16).toUpperCase(); + return '=' + ('0' + hexadecimal).slice(-2); + }) + // Encode spaces as `_`, as it’s shorter than `=20`. + .replace(/\x20/g, '_'); + }; + + var q = { + 'encode': encode, + 'decode': decode, + 'version': '0.1.1' + }; + + // Some AMD build optimizers, like r.js, check for specific condition patterns + // like the following: + if ( + typeof define == 'function' && + typeof define.amd == 'object' && + define.amd + ) { + define(function() { + return q; + }); + } else if (freeExports && !freeExports.nodeType) { + if (freeModule) { // in Node.js or RingoJS v0.8.0+ + freeModule.exports = q; + } else { // in Narwhal or RingoJS v0.7.0- + for (var key in q) { + q.hasOwnProperty(key) && (freeExports[key] = q[key]); + } + } + } else { // in Rhino or a web browser + root.q = q; + } + +}(this)); diff --git a/q/vendor/update.sh b/q/vendor/update.sh new file mode 100755 index 0000000..bcac9ee --- /dev/null +++ b/q/vendor/update.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +cd "$(dirname "${BASH_SOURCE}")" + +curl -# "https://raw.githubusercontent.com/mathiasbynens/q-encoding/master/q.js" > q.js +curl -# "https://raw.githubusercontent.com/mathiasbynens/utf8.js/master/utf8.js" > utf8.js + +cat "q.js" "utf8.js" "../eff.js" > "/tmp/q.js" +echo "Copying concatenated JS to pasteboard..." +pbcopy < "/tmp/q.js" diff --git a/q/vendor/utf8.js b/q/vendor/utf8.js new file mode 100644 index 0000000..be74021 --- /dev/null +++ b/q/vendor/utf8.js @@ -0,0 +1,234 @@ +/*! http://mths.be/utf8js v2.0.0 by @mathias */ +;(function(root) { + + // Detect free variables `exports` + var freeExports = typeof exports == 'object' && exports; + + // Detect free variable `module` + var freeModule = typeof module == 'object' && module && + module.exports == freeExports && module; + + // Detect free variable `global`, from Node.js or Browserified code, + // and use it as `root` + var freeGlobal = typeof global == 'object' && global; + if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) { + root = freeGlobal; + } + + /*--------------------------------------------------------------------------*/ + + var stringFromCharCode = String.fromCharCode; + + // Taken from http://mths.be/punycode + function ucs2decode(string) { + var output = []; + var counter = 0; + var length = string.length; + var value; + var extra; + while (counter < length) { + value = string.charCodeAt(counter++); + if (value >= 0xD800 && value <= 0xDBFF && counter < length) { + // high surrogate, and there is a next character + extra = string.charCodeAt(counter++); + if ((extra & 0xFC00) == 0xDC00) { // low surrogate + output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); + } else { + // unmatched surrogate; only append this code unit, in case the next + // code unit is the high surrogate of a surrogate pair + output.push(value); + counter--; + } + } else { + output.push(value); + } + } + return output; + } + + // Taken from http://mths.be/punycode + function ucs2encode(array) { + var length = array.length; + var index = -1; + var value; + var output = ''; + while (++index < length) { + value = array[index]; + if (value > 0xFFFF) { + value -= 0x10000; + output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800); + value = 0xDC00 | value & 0x3FF; + } + output += stringFromCharCode(value); + } + return output; + } + + /*--------------------------------------------------------------------------*/ + + function createByte(codePoint, shift) { + return stringFromCharCode(((codePoint >> shift) & 0x3F) | 0x80); + } + + function encodeCodePoint(codePoint) { + if ((codePoint & 0xFFFFFF80) == 0) { // 1-byte sequence + return stringFromCharCode(codePoint); + } + var symbol = ''; + if ((codePoint & 0xFFFFF800) == 0) { // 2-byte sequence + symbol = stringFromCharCode(((codePoint >> 6) & 0x1F) | 0xC0); + } + else if ((codePoint & 0xFFFF0000) == 0) { // 3-byte sequence + symbol = stringFromCharCode(((codePoint >> 12) & 0x0F) | 0xE0); + symbol += createByte(codePoint, 6); + } + else if ((codePoint & 0xFFE00000) == 0) { // 4-byte sequence + symbol = stringFromCharCode(((codePoint >> 18) & 0x07) | 0xF0); + symbol += createByte(codePoint, 12); + symbol += createByte(codePoint, 6); + } + symbol += stringFromCharCode((codePoint & 0x3F) | 0x80); + return symbol; + } + + function utf8encode(string) { + var codePoints = ucs2decode(string); + var length = codePoints.length; + var index = -1; + var codePoint; + var byteString = ''; + while (++index < length) { + codePoint = codePoints[index]; + byteString += encodeCodePoint(codePoint); + } + return byteString; + } + + /*--------------------------------------------------------------------------*/ + + function readContinuationByte() { + if (byteIndex >= byteCount) { + throw Error('Invalid byte index'); + } + + var continuationByte = byteArray[byteIndex] & 0xFF; + byteIndex++; + + if ((continuationByte & 0xC0) == 0x80) { + return continuationByte & 0x3F; + } + + // If we end up here, it’s not a continuation byte + throw Error('Invalid continuation byte'); + } + + function decodeSymbol() { + var byte1; + var byte2; + var byte3; + var byte4; + var codePoint; + + if (byteIndex > byteCount) { + throw Error('Invalid byte index'); + } + + if (byteIndex == byteCount) { + return false; + } + + // Read first byte + byte1 = byteArray[byteIndex] & 0xFF; + byteIndex++; + + // 1-byte sequence (no continuation bytes) + if ((byte1 & 0x80) == 0) { + return byte1; + } + + // 2-byte sequence + if ((byte1 & 0xE0) == 0xC0) { + var byte2 = readContinuationByte(); + codePoint = ((byte1 & 0x1F) << 6) | byte2; + if (codePoint >= 0x80) { + return codePoint; + } else { + throw Error('Invalid continuation byte'); + } + } + + // 3-byte sequence (may include unpaired surrogates) + if ((byte1 & 0xF0) == 0xE0) { + byte2 = readContinuationByte(); + byte3 = readContinuationByte(); + codePoint = ((byte1 & 0x0F) << 12) | (byte2 << 6) | byte3; + if (codePoint >= 0x0800) { + return codePoint; + } else { + throw Error('Invalid continuation byte'); + } + } + + // 4-byte sequence + if ((byte1 & 0xF8) == 0xF0) { + byte2 = readContinuationByte(); + byte3 = readContinuationByte(); + byte4 = readContinuationByte(); + codePoint = ((byte1 & 0x0F) << 0x12) | (byte2 << 0x0C) | + (byte3 << 0x06) | byte4; + if (codePoint >= 0x010000 && codePoint <= 0x10FFFF) { + return codePoint; + } + } + + throw Error('Invalid UTF-8 detected'); + } + + var byteArray; + var byteCount; + var byteIndex; + function utf8decode(byteString) { + byteArray = ucs2decode(byteString); + byteCount = byteArray.length; + byteIndex = 0; + var codePoints = []; + var tmp; + while ((tmp = decodeSymbol()) !== false) { + codePoints.push(tmp); + } + return ucs2encode(codePoints); + } + + /*--------------------------------------------------------------------------*/ + + var utf8 = { + 'version': '2.0.0', + 'encode': utf8encode, + 'decode': utf8decode + }; + + // Some AMD build optimizers, like r.js, check for specific condition patterns + // like the following: + if ( + typeof define == 'function' && + typeof define.amd == 'object' && + define.amd + ) { + define(function() { + return utf8; + }); + } else if (freeExports && !freeExports.nodeType) { + if (freeModule) { // in Node.js or RingoJS v0.8.0+ + freeModule.exports = utf8; + } else { // in Narwhal or RingoJS v0.7.0- + var object = {}; + var hasOwnProperty = object.hasOwnProperty; + for (var key in utf8) { + hasOwnProperty.call(utf8, key) && (freeExports[key] = utf8[key]); + } + } + } else { // in Rhino or a web browser + root.utf8 = utf8; + } + +}(this));