Skip to content

Commit

Permalink
Add Q encoder/decoder
Browse files Browse the repository at this point in the history
  • Loading branch information
mathiasbynens committed May 5, 2014
1 parent 3dbc514 commit ad5644a
Show file tree
Hide file tree
Showing 7 changed files with 515 additions and 0 deletions.
5 changes: 5 additions & 0 deletions 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/).
87 changes: 87 additions & 0 deletions 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;
}

}
72 changes: 72 additions & 0 deletions 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'));
19 changes: 19 additions & 0 deletions q/index.html
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang=en>
<meta charset=utf-8>
<title>`Q` encoder/decoder</title>
<meta name=viewport content="width=device-width,initial-scale=1">
<link rel=stylesheet href=eff.css>
<meta name=description content="An online, on-the-fly `Q` encoder/decoder.">
<h1><code>Q</code> encoder/decoder</h1>
<noscript><strong>To use this tool, please <a href=http://enable-javascript.com/>enable JavaScript</a> and reload the page.</strong></noscript>
<h2>Decoded:</h2>
<textarea autofocus>&#xA1;Hola, se&#xF1;or!</textarea>
<h2>Encoded: (<a href=#%C2%A1Hola%2C%20se%C3%B1or! id=permalink>permalink</a>)</h2>
<textarea>=C2=A1Hola,_se=C3=B1or!</textarea>
<h2>About this tool</h2>
<p>This tool uses <a href=http://mths.be/q><i>q-encoding</i></a> and <a href=http://mths.be/utf8js>utf8.js</a> to do all the encoding/decoding.
<p id=footer>Made by <a href=http://mathiasbynens.be/>@mathias</a><a href=https://github.com/mathiasbynens/mothereff.in/tree/master/q>fork this on GitHub!</a></p>
<script src=vendor/q.js></script>
<script src=vendor/utf8.js></script>
<script src=eff.js></script>
88 changes: 88 additions & 0 deletions 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));
10 changes: 10 additions & 0 deletions 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"

0 comments on commit ad5644a

Please sign in to comment.