diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..0a91f75
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+# Automatically normalize line endings for all text-based files
+* text=auto
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..444ec4d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,17 @@
+# Coverage report
+coverage
+
+# Installed npm modules
+node_modules
+
+# Folder view configuration files
+.DS_Store
+Desktop.ini
+
+# Thumbnail cache files
+._*
+Thumbs.db
+
+# Files that might appear on external disks
+.Spotlight-V100
+.Trashes
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..0fc50ac
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,18 @@
+language: node_js
+node_js:
+ - "0.10"
+before_script:
+ - "npm install -g grunt-cli"
+ # Narwhal uses a hardcoded path to openjdk v6, so use that version
+ - "sudo apt-get update -qq"
+ - "sudo apt-get install -qq openjdk-6-jre"
+ - "PACKAGE=rhino1_7R3; wget http://ftp.mozilla.org/pub/mozilla.org/js/$PACKAGE.zip && sudo unzip $PACKAGE -d /opt/ && rm $PACKAGE.zip"
+ - "PACKAGE=rhino1_7R3; echo -e '#!/bin/sh\\njava -jar /opt/'$PACKAGE'/js.jar $@' | sudo tee /usr/local/bin/rhino && sudo chmod +x /usr/local/bin/rhino"
+ - "PACKAGE=ringojs-0.9; wget http://ringojs.org/downloads/$PACKAGE.zip && sudo unzip $PACKAGE -d /opt/ && rm $PACKAGE.zip"
+ - "PACKAGE=ringojs-0.9; sudo ln -s /opt/$PACKAGE/bin/ringo /usr/local/bin/ringo && sudo chmod +x /usr/local/bin/ringo"
+ - "PACKAGE=v0.3.2; wget https://github.com/280north/narwhal/archive/$PACKAGE.zip && sudo unzip $PACKAGE -d /opt/ && rm $PACKAGE.zip"
+ - "PACKAGE=narwhal-0.3.2; sudo ln -s /opt/$PACKAGE/bin/narwhal /usr/local/bin/narwhal && sudo chmod +x /usr/local/bin/narwhal"
+ # If the enviroment stores rt.jar in a different directory, find it and symlink the directory
+ - "PREFIX=/usr/lib/jvm; if [ ! -d $PREFIX/java-6-openjdk ]; then for d in $PREFIX/java-6-openjdk-*; do if [ -e $d/jre/lib/rt.jar ]; then sudo ln -s $d $PREFIX/java-6-openjdk; break; fi; done; fi"
+script:
+ "grunt ci"
diff --git a/Gruntfile.js b/Gruntfile.js
new file mode 100644
index 0000000..72ed4d9
--- /dev/null
+++ b/Gruntfile.js
@@ -0,0 +1,61 @@
+module.exports = function(grunt) {
+
+ grunt.initConfig({
+ 'shell': {
+ 'options': {
+ 'stdout': true,
+ 'stderr': true,
+ 'failOnError': true
+ },
+ 'cover': {
+ 'command': 'istanbul cover --report "html" --verbose --dir "coverage" "tests/tests.js"'
+ },
+ 'test-narwhal': {
+ 'command': 'echo "Testing in Narwhal..."; export NARWHAL_OPTIMIZATION=-1; narwhal "tests/tests.js"'
+ },
+ 'test-phantomjs': {
+ 'command': 'echo "Testing in PhantomJS..."; phantomjs "tests/tests.js"'
+ },
+ // Rhino 1.7R4 has a bug that makes it impossible to test in.
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=775566
+ // To test, use Rhino 1.7R3, or wait (heh) for the 1.7R5 release.
+ 'test-rhino': {
+ 'command': 'echo "Testing in Rhino..."; rhino -opt -1 "tests.js"',
+ 'options': {
+ 'execOptions': {
+ 'cwd': 'tests'
+ }
+ }
+ },
+ 'test-ringo': {
+ 'command': 'echo "Testing in Ringo..."; ringo -o -1 "tests/tests.js"'
+ },
+ 'test-node': {
+ 'command': 'echo "Testing in Node..."; node "tests/tests.js"'
+ },
+ 'test-browser': {
+ 'command': 'echo "Testing in a browser..."; open "tests/index.html"'
+ }
+ }
+ });
+
+ grunt.loadNpmTasks('grunt-shell');
+
+ grunt.registerTask('cover', 'shell:cover');
+ grunt.registerTask('ci', [
+ 'shell:test-narwhal',
+ 'shell:test-phantomjs',
+ 'shell:test-rhino',
+ 'shell:test-ringo',
+ 'shell:test-node',
+ ]);
+ grunt.registerTask('test', [
+ 'ci',
+ 'shell:test-browser'
+ ]);
+
+ grunt.registerTask('default', [
+ 'shell:test-node'
+ ]);
+
+};
diff --git a/LICENSE-MIT.txt b/LICENSE-MIT.txt
new file mode 100644
index 0000000..97067e5
--- /dev/null
+++ b/LICENSE-MIT.txt
@@ -0,0 +1,20 @@
+Copyright Mathias Bynens
+
+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 above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+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.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..116c325
--- /dev/null
+++ b/README.md
@@ -0,0 +1,188 @@
+# bacon-cipher [![Build status](https://travis-ci.org/mathiasbynens/bacon-cipher.svg?branch=master)](https://travis-ci.org/mathiasbynens/bacon-cipher) [![Dependency status](https://gemnasium.com/mathiasbynens/bacon-cipher.svg)](https://gemnasium.com/mathiasbynens/bacon-cipher)
+
+_bacon-cipher_ is a JavaScript implementation of [Bacon’s cipher, a.k.a. the Baconian cipher](http://en.wikipedia.org/wiki/Bacon's_cipher). It can be used to encode plaintext to Bacon-ciphertext, or the other way around (i.e. decoding).
+
+By default it uses the most common Bacon cipher alphabet, i.e. `ABCDEFGHIKLMNOPQRSTUWXYZ` (24 letters). This boils down to the following translations:
+
+```
+a AAAAA g AABBA n ABBAA t BAABA
+b AAAAB h AABBB o ABBAB u-v BAABB
+c AAABA i-j ABAAA p ABBBA w BABAA
+d AAABB k ABAAB q ABBBB x BABAB
+e AABAA l ABABA r BAAAA y BABBA
+f AABAB m ABABB s BAAAB z BABBB
+```
+
+## Installation
+
+Via [npm](http://npmjs.org/):
+
+```bash
+npm install bacon-cipher
+```
+
+Via [Bower](http://bower.io/):
+
+```bash
+bower install bacon-cipher
+```
+
+Via [Component](https://github.com/component/component):
+
+```bash
+component install mathiasbynens/bacon-cipher
+```
+
+In a browser:
+
+```html
+
+```
+
+In [Narwhal](http://narwhaljs.org/), [Node.js](http://nodejs.org/), and [RingoJS](http://ringojs.org/):
+
+```js
+var bacon = require('bacon-cipher');
+```
+
+In [Rhino](http://www.mozilla.org/rhino/):
+
+```js
+load('bacon.js');
+```
+
+Using an AMD loader like [RequireJS](http://requirejs.org/):
+
+```js
+require(
+ {
+ 'paths': {
+ 'bacon': 'path/to/bacon'
+ }
+ },
+ ['bacon'],
+ function(bacon) {
+ console.log(bacon);
+ }
+);
+```
+
+## API
+
+### `bacon.version`
+
+A string representing the semantic version number.
+
+### `bacon.encode(text, options)`
+
+This function takes a string of text (the `text` parameter) and encrypts it using Bacon’s cipher.
+
+```js
+bacon.encode('steganography');
+// → 'BAAABBAABAAABAAAABBAAAAAAABBAAABBABAABBABAAAAAAAAAABBBAAABBBBABBA'
+```
+
+By default it uses the most common Bacon cipher alphabet, i.e. `ABCDEFGHIKLMNOPQRSTUWXYZ` (24 letters). In this case instances of `J` are replaced with `I`, and instances of `V` are replaced with `U` before further encoding the input string.
+
+```js
+bacon.encode('James Vendetta');
+// → 'ABAAAAAAAAABABBAABAABAAAB BAABBAABAAABBAAAAABBAABAABAABABAABAAAAAA'
+```
+
+It’s possible to pass an (optional) `options` object with an `alphabet` property to override the cipher alphabet. Note that in that case, no preprocessing (like replacing `j` and `v`) is done. For example, to use the full 26-letter alphabet:
+
+```js
+bacon.encode('James Vendetta', {
+ 'alphabet': 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+});
+// → 'ABAABAAAAAABBAAAABAABAABA BABABAABAAABBABAAABBAABAABAABBBAABBAAAAA'
+```
+
+### `bacon.decode(text, options)`
+
+This function takes a string of text (the `text` parameter) and decrypts it using Bacon’s cipher.
+
+By default it uses the most common Bacon cipher alphabet, i.e. `ABCDEFGHIKLMNOPQRSTUWXYZ` (24 letters).
+
+```js
+bacon.decode('BAAABBAABAAABAAAABBAAAAAAABBAAABBABAABBABAAAAAAAAAABBBAAABBBBABBA');
+// → 'STEGANOGRAPHY'
+
+bacon.decode('ABAAAAAAAAABABBAABAABAAAB BAABBAABAAABBAAAAABBAABAABAABABAABAAAAAA');
+// → 'IAMES UENDETTA'
+```
+
+It’s possible to pass an (optional) `options` object with an `alphabet` property to override the cipher alphabet. For example, to use the full 26-letter alphabet:
+
+```js
+bacon.decode('ABAABAAAAAABBAAAABAABAABA BABABAABAAABBABAAABBAABAABAABBBAABBAAAAA', {
+ 'alphabet': 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+});
+// → 'JAMES VENDETTA'
+```
+
+### Using the `bacon` binary
+
+To use the `bacon` binary in your shell, simply install _bacon-cipher_ globally using npm:
+
+```bash
+npm install -g bacon-cipher
+```
+
+After that you will be able to use Bacon’s cipher from the command line:
+
+```bash
+$ bacon --encode 'foo bar baz'
+AABABABBABABBAB AAAABAAAAABAAAA AAAABAAAAABABBB
+
+$ bacon --decode 'AABABABBABABBAB AAAABAAAAABAAAA AAAABAAAAABABBB'
+FOO BAR BAZ
+
+$ bacon --encode --alphabet=ABCDEFGHIJKLMNOPQRSTUVWXYZ 'Julius Caesar'
+ABAABBABAAABABBABAAABABAABAABA AAABAAAAAAAABAABAABAAAAAABAAAB
+
+$ bacon --decode --alphabet=ABCDEFGHIJKLMNOPQRSTUVWXYZ 'ABAABBABAAABABBABAAABABAABAABA AAABAAAAAAAABAABAABAAAAAABAAAB'
+JULIUS CAESAR
+```
+
+Read a local text file, encrypt it using Bacon’s cipher with a non-default cipher alphabet, and save the result to a new file:
+
+```bash
+$ bacon --encode < foo.txt > foo-bacon.txt
+```
+
+Or do the same with an online text file:
+
+```bash
+$ curl -sL 'http://mths.be/brh' | bacon --encode > bacon.txt
+```
+
+Or, the opposite — read a local file containing Bacon ciphertext, decode it back to plain text, and save the result to a new file:
+
+```bash
+$ bacon --decode < bacon.txt > original.txt
+```
+
+See `bacon --help` for the full list of options.
+
+## Support
+
+_bacon_ is designed to work in at least Node.js v0.10.0, Narwhal 0.3.2, RingoJS 0.8-0.9, PhantomJS 1.9.0, Rhino 1.7RC4, as well as old and modern versions of Chrome, Firefox, Safari, Opera, and Internet Explorer.
+
+## Unit tests & code coverage
+
+After cloning this repository, run `npm install` to install the dependencies needed for development and testing. You may want to install Istanbul _globally_ using `npm install istanbul -g`.
+
+Once that’s done, you can run the unit tests in Node using `npm test` or `node tests/tests.js`. To run the tests in Rhino, Ringo, Narwhal, and web browsers as well, use `grunt test`.
+
+To generate the code coverage report, use `grunt cover`.
+
+## Author
+
+| [![twitter/mathias](https://gravatar.com/avatar/24e08a9ea84deb17ae121074d0f17125?s=70)](https://twitter.com/mathias "Follow @mathias on Twitter") |
+|---|
+| [Mathias Bynens](http://mathiasbynens.be/) |
+
+## License
+
+_bacon_ is available under the [MIT](http://mths.be/mit) license.
diff --git a/bacon.js b/bacon.js
new file mode 100644
index 0000000..1720bc8
--- /dev/null
+++ b/bacon.js
@@ -0,0 +1,123 @@
+/*! http://mths.be/bacon v0.1.0 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;
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ var DEFAULT_ALPHABET = 'ABCDEFGHIKLMNOPQRSTUWXYZ';
+
+ var decode = function(ciphertext, options) {
+ ciphertext = ciphertext.toUpperCase();
+ // Use the most common 24-letter Bacon alphabet by default.
+ var alphabet = options && options.alphabet != null ?
+ options.alphabet.toUpperCase() :
+ DEFAULT_ALPHABET;
+ var index = -1;
+ var length = ciphertext.length;
+ var space = '';
+ var result = '';
+ var buffer = [];
+ var symbol;
+ var alphabetIndex;
+ while (++index < length) {
+ symbol = ciphertext.charAt(index);
+ if (symbol == 'A' || symbol == 'B') {
+ buffer.push(symbol);
+ } else {
+ // Prepare a space to be added to the output.
+ space = ' ';
+ }
+ if (buffer.length == 5) {
+ alphabetIndex = (
+ (buffer[0] == 'A' ? 0 : 0x10) + // 0b10000
+ (buffer[1] == 'A' ? 0 : 0x08) + // 0b01000
+ (buffer[2] == 'A' ? 0 : 0x04) + // 0b00100
+ (buffer[3] == 'A' ? 0 : 0x02) + // 0b00010
+ (buffer[4] == 'A' ? 0 : 0x01) // 0b00001
+ );
+ buffer = [];
+ result += (result.length ? space : '') + alphabet.charAt(alphabetIndex);
+ space = '';
+ }
+ }
+ return result;
+ };
+
+ var encode = function(string, options) {
+ string = string.toUpperCase();
+ var alphabet;
+ if (options && options.alphabet != null) {
+ alphabet = options.alphabet.toUpperCase();
+ } else {
+ // Use the most common 24-letter Bacon alphabet by default.
+ alphabet = DEFAULT_ALPHABET;
+ string = string
+ .replace(/J/g, 'I')
+ .replace(/V/g, 'U');
+ }
+ var index = -1;
+ var length = string.length;
+ var alphabetIndex;
+ var space = '';
+ var result = '';
+ while (++index < length) {
+ alphabetIndex = alphabet.indexOf(string.charAt(index));
+ if (alphabetIndex > -1) {
+ result += space + (
+ (alphabetIndex & 0x10 ? 'B' : 'A') + // 0b10000
+ (alphabetIndex & 0x08 ? 'B' : 'A') + // 0b01000
+ (alphabetIndex & 0x04 ? 'B' : 'A') + // 0b00100
+ (alphabetIndex & 0x02 ? 'B' : 'A') + // 0b00010
+ (alphabetIndex & 0x01 ? 'B' : 'A') // 0b00001
+ );
+ space = '';
+ } else if (index) {
+ // Prepare a space to be added to the output, unless it’s leading space.
+ space = ' ';
+ }
+ }
+ return result;
+ };
+
+ var bacon = {
+ 'encode': encode,
+ 'decode': decode,
+ 'version': '0.1.0'
+ };
+
+ // 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 bacon;
+ });
+ } else if (freeExports && !freeExports.nodeType) {
+ if (freeModule) { // in Node.js or RingoJS v0.8.0+
+ freeModule.exports = bacon;
+ } else { // in Narwhal or RingoJS v0.7.0-
+ for (var key in bacon) {
+ bacon.hasOwnProperty(key) && (freeExports[key] = bacon[key]);
+ }
+ }
+ } else { // in Rhino or a web browser
+ root.bacon = bacon;
+ }
+
+}(this));
diff --git a/bin/bacon b/bin/bacon
new file mode 100755
index 0000000..fe7625b
--- /dev/null
+++ b/bin/bacon
@@ -0,0 +1,121 @@
+#!/usr/bin/env node
+(function() {
+
+ var fs = require('fs');
+ var bacon = require('../bacon.js');
+ var strings = process.argv.splice(2);
+ var stdin = process.stdin;
+ var data;
+ var timeout;
+ var action;
+ var options = {};
+ var log = console.log;
+
+ var main = function() {
+ var option = strings[0];
+ var count = 0;
+
+ if (/^(?:-h|--help|undefined)$/.test(option)) {
+ log(
+ 'bacon-cipher v%s - http://mths.be/bacon',
+ bacon.version
+ );
+ log([
+ '\nUsage:\n',
+ '\tbacon [-e | --encode] [--alphabet=string] string',
+ '\tbacon [-d | --decode] [--alphabet=string] string',
+ '\tbacon [-v | --version]',
+ '\tbacon [-h | --help]',
+ '\nExamples:\n',
+ '\tbacon --encode \'BACON\'',
+ '\techo \'AAAABAAAAAAAABAABBABABBAA\' | bacon --decode',
+ '\tbacon --encode --alphabet=\'ABCDEFGHIJKLMNOPQRSTUVWXYZ\' \'vendetta\''
+ ].join('\n'));
+ return process.exit(1);
+ }
+
+ if (/^(?:-v|--version)$/.test(option)) {
+ log('v%s', bacon.version);
+ return process.exit(1);
+ }
+
+ strings.forEach(function(string) {
+ // Process options
+ if (string == '-e' || string == '--encode') {
+ action = 'encode';
+ return;
+ }
+ if (string == '-d' || string == '--decode') {
+ action = 'decode';
+ return;
+ }
+ var matches;
+ if (matches = string.match(/^--alphabet=(.+)/)) {
+ options.alphabet = matches[1];
+ return;
+ }
+ if (string == '--strict') {
+ action = 'decode';
+ options.strict = true;
+ return;
+ }
+ // Process string(s)
+ var result;
+ if (!action) {
+ log('Error: bacon requires at least one option and a string argument.');
+ log('Try `bacon --help` for more information.');
+ return process.exit(1);
+ }
+ try {
+ result = bacon[action](string, options);
+ log(result);
+ count++;
+ } catch (error) {
+ log(error.message + '\n');
+ log('Error: failed to %s.', action);
+ log('If you think this is a bug in bacon-cipher, please report it:');
+ log('https://github.com/mathiasbynens/bacon-cipher/issues/new');
+ log('\nStack trace using bacon-cipher@%s:\n', bacon.version);
+ log(error.stack);
+ return process.exit(1);
+ }
+ });
+ if (!count) {
+ log('Error: bacon requires a string argument.');
+ log('Try `bacon --help` for more information.');
+ return process.exit(1);
+ }
+ // Return with exit status 0 outside of the `forEach` loop, in case
+ // multiple strings were passed in.
+ return process.exit(0);
+ };
+
+ if (stdin.isTTY) {
+ // handle shell arguments
+ main();
+ } else {
+ // Either the script is called from within a non-TTY context, or `stdin`
+ // content is being piped in.
+ if (!process.stdout.isTTY) {
+ // The script was called from a non-TTY context. This is a rather uncommon
+ // use case we don’t actively support. However, we don’t want the script
+ // to wait forever in such cases, so…
+ timeout = setTimeout(function() {
+ // …if no piped data arrived after a whole minute, handle shell
+ // arguments instead.
+ main();
+ }, 60000);
+ }
+ data = '';
+ stdin.on('data', function(chunk) {
+ clearTimeout(timeout);
+ data += chunk;
+ });
+ stdin.on('end', function() {
+ strings.push(data.trim());
+ main();
+ });
+ stdin.resume();
+ }
+
+}());
diff --git a/bower.json b/bower.json
new file mode 100644
index 0000000..3894338
--- /dev/null
+++ b/bower.json
@@ -0,0 +1,16 @@
+{
+ "name": "bacon-cipher",
+ "version": "0.1.0",
+ "main": "bacon.js",
+ "ignore": [
+ "bin",
+ "coverage",
+ "man",
+ "tests",
+ ".*",
+ "component.json",
+ "Gruntfile.js",
+ "node_modules",
+ "package.json"
+ ]
+}
diff --git a/component.json b/component.json
new file mode 100644
index 0000000..d776a0e
--- /dev/null
+++ b/component.json
@@ -0,0 +1,25 @@
+{
+ "name": "bacon-cipher",
+ "version": "0.1.0",
+ "description": "A robust JavaScript implementation of Bacon’s cipher, a.k.a. the Baconian cipher.",
+ "repo": "mathiasbynens/bacon-cipher",
+ "license": "MIT",
+ "scripts": [
+ "bacon.js"
+ ],
+ "main": "bacon.js",
+ "keywords": [
+ "string",
+ "bacon",
+ "baconian",
+ "cipher",
+ "crypto",
+ "cryptography",
+ "stegano",
+ "steganography",
+ "encode",
+ "encrypt",
+ "decode",
+ "decrypt"
+ ]
+}
diff --git a/man/bacon.1 b/man/bacon.1
new file mode 100644
index 0000000..d325895
--- /dev/null
+++ b/man/bacon.1
@@ -0,0 +1,66 @@
+.Dd April 30, 2014
+.Dt bacon 1
+.Sh NAME
+.Nm bacon
+.Nd encode or decode messages using Bacon's cipher
+.Sh SYNOPSIS
+.Nm
+.Op Fl -encode Ar string
+.br
+.Op Fl -encode Fl -alphabet=alphabet Ar string
+.br
+.Op Fl -decode Ar string
+.br
+.Op Fl -decode Fl -alphabet=alphabet Ar string
+.br
+.Op Fl v | -version
+.br
+.Op Fl h | -help
+.Sh DESCRIPTION
+.Nm
+encode or decode messages using Bacon's cipher.
+.Sh OPTIONS
+.Bl -ohang -offset
+.It Sy "--encode"
+Encode a string of text using Bacon's cipher.
+.It Sy "--encode --alphabet=alphabet"
+Specify a custom cipher alphabet to be used during encoding. By default the common 24-letter Bacon alphabet is used, in which case instances of `J` and `V` in the input are replaced with `I` and `U` respectively before further encoding the message.
+.It Sy "--decode"
+Decode a string of ciphertext using Bacon's cipher.
+.It Sy "--decode --alphabet=alphabet"
+Specify a custom cipher alphabet to be used during decoding. By default the common 24-letter Bacon alphabet is used.
+.It Sy "-v, --version"
+Print bacon's version.
+.It Sy "-h, --help"
+Show the help screen.
+.El
+.Sh EXIT STATUS
+The
+.Nm bacon
+utility exits with one of the following values:
+.Pp
+.Bl -tag -width flag -compact
+.It Li 0
+.Nm
+successfully encoded/decoded the input and printed the result.
+.It Li 1
+.Nm
+wasn't instructed to encode/decode anything (for example, the
+.Ar --help
+flag was set); or, an error occurred.
+.El
+.Sh EXAMPLES
+.Bl -ohang -offset
+.It Sy "bacon --encode 'foo bar baz'"
+Print an encoded version of the given string.
+.It Sy "bacon --decode 'AABABABBABABBAB AAAABAAAAABAAAA AAAABAAAAABABBB'"
+Print the decoded version of the given ciphertext.
+.It Sy "echo\ 'foo bar baz'\ |\ bacon --encode"
+Print the encoded version of the string that gets piped in.
+.El
+.Sh BUGS
+bacon's bug tracker is located at .
+.Sh AUTHOR
+Mathias Bynens
+.Sh WWW
+
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..d671204
--- /dev/null
+++ b/package.json
@@ -0,0 +1,64 @@
+{
+ "name": "bacon-cipher",
+ "version": "0.1.0",
+ "description": "A robust JavaScript implementation of Bacon’s cipher, a.k.a. the Baconian cipher.",
+ "homepage": "http://mths.be/bacon",
+ "main": "bacon.js",
+ "bin": {
+ "bacon": "bin/bacon"
+ },
+ "man": "man/bacon.1",
+ "keywords": [
+ "string",
+ "bacon",
+ "baconian",
+ "cipher",
+ "crypto",
+ "cryptography",
+ "stegano",
+ "steganography",
+ "encode",
+ "encrypt",
+ "decode",
+ "decrypt"
+ ],
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "http://mths.be/mit"
+ }
+ ],
+ "author": {
+ "name": "Mathias Bynens",
+ "url": "http://mathiasbynens.be/"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/mathiasbynens/bacon-cipher.git"
+ },
+ "bugs": {
+ "url": "https://github.com/mathiasbynens/bacon-cipher/issues"
+ },
+ "files": [
+ "LICENSE-MIT.txt",
+ "bacon.js",
+ "bin/",
+ "man/"
+ ],
+ "directories": {
+ "bin": "bin",
+ "man": "man",
+ "test": "tests"
+ },
+ "scripts": {
+ "test": "node tests/tests.js"
+ },
+ "devDependencies": {
+ "grunt": "~0.4.4",
+ "grunt-shell": "~0.7.0",
+ "istanbul": "~0.2.7",
+ "qunit-extras": "~1.1.0",
+ "qunitjs": "~1.11.0",
+ "requirejs": "~2.1.11"
+ }
+}
diff --git a/tests/index.html b/tests/index.html
new file mode 100644
index 0000000..543fb0a
--- /dev/null
+++ b/tests/index.html
@@ -0,0 +1,35 @@
+
+
+
+
+ bacon test suite
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/tests.js b/tests/tests.js
new file mode 100644
index 0000000..1652a81
--- /dev/null
+++ b/tests/tests.js
@@ -0,0 +1,130 @@
+(function(root) {
+ 'use strict';
+
+ var noop = Function.prototype;
+
+ var load = (typeof require == 'function' && !(root.define && define.amd)) ?
+ require :
+ (!root.document && root.java && root.load) || noop;
+
+ var QUnit = (function() {
+ return root.QUnit || (
+ root.addEventListener || (root.addEventListener = noop),
+ root.setTimeout || (root.setTimeout = noop),
+ root.QUnit = load('../node_modules/qunitjs/qunit/qunit.js') || root.QUnit,
+ addEventListener === noop && delete root.addEventListener,
+ root.QUnit
+ );
+ }());
+
+ var qe = load('../node_modules/qunit-extras/qunit-extras.js');
+ if (qe) {
+ qe.runInContext(root);
+ }
+
+ // The `bacon` object to test
+ var bacon = root.bacon || (root.bacon = (
+ bacon = load('../bacon.js') || root.bacon,
+ bacon = bacon.bacon || bacon
+ ));
+
+ /*--------------------------------------------------------------------------*/
+
+ function forEach(array, fn) {
+ var index = -1;
+ var length = array.length;
+ while (++index < length) {
+ fn(array[index]);
+ }
+ }
+
+ function forOwn(object, fn) {
+ for (var key in object) {
+ if (object.hasOwnProperty(key)) {
+ fn(key, object[key]);
+ }
+ }
+ }
+
+ // `throws` is a reserved word in ES3; alias it to avoid errors
+ var raises = QUnit.assert['throws'];
+
+ // explicitly call `QUnit.module()` instead of `module()`
+ // in case we are in a CLI environment
+ QUnit.module('bacon');
+
+ test('bacon.encode', function() {
+ equal(
+ bacon.encode('ABCDEFGHIJKLMNOPQRSTUVWXYZ'),
+ 'AAAAAAAAABAAABAAAABBAABAAAABABAABBAAABBBABAAAABAAAABAABABABAABABBABBAAABBABABBBAABBBBBAAAABAAABBAABABAABBBAABBBABAABABABBABBABABBB',
+ 'A-Z'
+ );
+ equal(
+ bacon.encode('abcdefghijklmnopqrstuvwxyz'),
+ 'AAAAAAAAABAAABAAAABBAABAAAABABAABBAAABBBABAAAABAAAABAABABABAABABBABBAAABBABABBBAABBBBBAAAABAAABBAABABAABBBAABBBABAABABABBABBABABBB',
+ 'a-z'
+ );
+ equal(
+ bacon.encode('foo bar'),
+ 'AABABABBABABBAB AAAABAAAAABAAAA',
+ 'single space character'
+ );
+ equal(
+ bacon.encode('fOo bAr'),
+ 'AABABABBABABBAB AAAABAAAAABAAAA',
+ 'uppercase vs. lowercase doesn’t matter'
+ );
+ equal(
+ bacon.encode('!"#$%&\'()*+,-./0123456789:;<=>?@[\]^_`{|}~'),
+ '',
+ '[^a-zA-Z] are ignored'
+ );
+ equal(
+ bacon.encode('foo!"#$%&\'()*+,-./0123456789:;<=>?@[\]^_`{|}~bar'),
+ 'AABABABBABABBAB AAAABAAAAABAAAA',
+ '[^a-zA-Z] are ignored and become spaces if they occur between [a-zA-Z]'
+ );
+ equal(
+ bacon.encode('ABCDEFGHIJKLMNOPQRSTUVWXYZ', { 'alphabet': 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' }),
+ 'AAAAAAAAABAAABAAAABBAABAAAABABAABBAAABBBABAAAABAABABABAABABBABBAAABBABABBBAABBBBBAAAABAAABBAABABAABBBABAABABABBABBABABBBBBAAABBAAB',
+ 'custom cipher alphabet'
+ );
+ equal(
+ bacon.encode('ABCDEFGHIJKLMNOPQRSTUVWXYZ', { 'alphabet': 'abcdefghijklmnopqrstuvwxyz' }),
+ 'AAAAAAAAABAAABAAAABBAABAAAABABAABBAAABBBABAAAABAABABABAABABBABBAAABBABABBBAABBBBBAAAABAAABBAABABAABBBABAABABABBABBABABBBBBAAABBAAB',
+ 'custom cipher alphabet is uppercased before use'
+ );
+ });
+
+ test('bacon.decode', function() {
+ equal(
+ bacon.decode('AAAAAAAAABAAABAAAABBAABAAAABABAABBAAABBBABAAAABAAAABAABABABAABABBABBAAABBABABBBAABBBBBAAAABAAABBAABABAABBBAABBBABAABABABBABBABABBB'),
+ 'ABCDEFGHIIKLMNOPQRSTUUWXYZ',
+ 'A-Z'
+ );
+ equal(
+ bacon.decode('AA AA AA AAA BAAAB AAAABBA ABAAA.ABAB#AABBA@AABB+BABAA/AABAAAABAABABABAAB_ABBABBAAABBABABBB AABBBBBAAAABAAABBAABABAABBBABABBB'),
+ 'A B CD E F G H IIKL MNO PQRST UUWX YZ',
+ '[^a-zA-Z] are ignored and become spaces if they occur between [a-zA-Z]'
+ );
+ equal(
+ bacon.decode('AAAAAAAAABAAABAAAABBAABAAAABABAABBAAABBBABAAAABAABABABAABABBABBAAABBABABBBAABBBBBAAAABAAABBAABABAABBBABAABABABBABBABABBBBBAAABBAAB', { 'alphabet': 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' }),
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
+ 'custom cipher alphabet'
+ );
+ equal(
+ bacon.decode('AAAAAAAAABAAABAAAABBAABAAAABABAABBAAABBBABAAAABAABABABAABABBABBAAABBABABBBAABBBBBAAAABAAABBAABABAABBBABAABABABBABBABABBBBBAAABBAAB', { 'alphabet': 'abcdefghijklmnopqrstuvwxyz' }),
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
+ 'custom cipher alphabet is uppercased before use'
+ );
+ });
+
+ /*--------------------------------------------------------------------------*/
+
+ // configure QUnit and call `QUnit.start()` for
+ // Narwhal, Node.js, PhantomJS, Rhino, and RingoJS
+ if (!root.document || root.phantom) {
+ QUnit.config.noglobals = true;
+ QUnit.start();
+ }
+}(typeof global == 'object' && global || this));