├── .gitignore ├── .travis.yml ├── mime.json ├── emojis.json ├── package.json ├── LICENSE ├── index.js ├── README.md └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '5' 4 | - '4' 5 | - '0.12' 6 | - '0.10' 7 | -------------------------------------------------------------------------------- /mime.json: -------------------------------------------------------------------------------- 1 | [ 2 | "A", 3 | "B", 4 | "C", 5 | "D", 6 | "E", 7 | "F", 8 | "G", 9 | "H", 10 | "I", 11 | "J", 12 | "K", 13 | "L", 14 | "M", 15 | "N", 16 | "O", 17 | "P", 18 | "Q", 19 | "R", 20 | "S", 21 | "T", 22 | "U", 23 | "V", 24 | "W", 25 | "X", 26 | "Y", 27 | "Z", 28 | "a", 29 | "b", 30 | "c", 31 | "d", 32 | "e", 33 | "f", 34 | "g", 35 | "h", 36 | "i", 37 | "j", 38 | "k", 39 | "l", 40 | "m", 41 | "n", 42 | "o", 43 | "p", 44 | "q", 45 | "r", 46 | "s", 47 | "t", 48 | "u", 49 | "v", 50 | "w", 51 | "x", 52 | "y", 53 | "z", 54 | "0", 55 | "1", 56 | "2", 57 | "3", 58 | "4", 59 | "5", 60 | "6", 61 | "7", 62 | "8", 63 | "9", 64 | "+", 65 | "/", 66 | "=" 67 | ] 68 | -------------------------------------------------------------------------------- /emojis.json: -------------------------------------------------------------------------------- 1 | [ 2 | "😬", 3 | "🍍", 4 | "📦", 5 | "🍏", 6 | "🎳", 7 | "💩", 8 | "📙", 9 | "📵", 10 | "🙌", 11 | "😍", 12 | "🐠", 13 | "🐇", 14 | "🏠", 15 | "🅰", 16 | "😱", 17 | "💏", 18 | "🕖", 19 | "🌗", 20 | "🍕", 21 | "🚼", 22 | "💖", 23 | "🕡", 24 | "📄", 25 | "💭", 26 | "📶", 27 | "💍", 28 | "🔆", 29 | "🎎", 30 | "📓", 31 | "🔎", 32 | "💀", 33 | "🕘", 34 | "😮", 35 | "🛀", 36 | "🐎", 37 | "🐷", 38 | "🐒", 39 | "🍺", 40 | "🍑", 41 | "👔", 42 | "🚔", 43 | "💡", 44 | "🚒", 45 | "💽", 46 | "🌵", 47 | "🌜", 48 | "🐀", 49 | "🚸", 50 | "👈", 51 | "🌋", 52 | "🕤", 53 | "👒", 54 | "🐊", 55 | "🍟", 56 | "🐗", 57 | "🕠", 58 | "🍴", 59 | "🍌", 60 | "🌺", 61 | "😳", 62 | "🚢", 63 | "🏦", 64 | "🍙", 65 | "📤", 66 | "📫" 67 | ] 68 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "base64-emoji", 3 | "version": "2.1.0", 4 | "description": "Like base64 but encoding into emojis", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "standard && tape test.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/watson/base64-emoji.git" 12 | }, 13 | "dependencies": {}, 14 | "devDependencies": { 15 | "standard": "^4.3.2", 16 | "tape": "^4.0.0" 17 | }, 18 | "keywords": [ 19 | "emoji", 20 | "emojicon", 21 | "emojicons", 22 | "base", 23 | "base64", 24 | "encode", 25 | "encoding", 26 | "decode", 27 | "decoding", 28 | "data" 29 | ], 30 | "author": "Thomas Watson Steen (https://twitter.com/wa7son)", 31 | "license": "MIT", 32 | "bugs": { 33 | "url": "https://github.com/watson/base64-emoji/issues" 34 | }, 35 | "homepage": "https://github.com/watson/base64-emoji", 36 | "coordinates": [ 37 | 37.4121528, 38 | -122.0951204 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Thomas Watson Steen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var mime = require('./mime') 4 | var emojis = require('./emojis') 5 | var EMOJI_BYTE_SIZE = 4 6 | 7 | exports.encode = function (obj, buf, offset) { 8 | if (typeof obj === 'string') obj = new Buffer(obj) 9 | else if (!Buffer.isBuffer(obj)) throw new Error('Invalid argument! Expected string or Buffer') 10 | 11 | if (!offset) offset = 0 12 | var oldOffset = offset 13 | 14 | var base64 = obj.toString('base64') 15 | if (!buf) buf = new Buffer(base64.length * EMOJI_BYTE_SIZE) 16 | var index 17 | 18 | for (var i = 0, l = base64.length; i < l; i++) { 19 | index = mime.indexOf(base64[i]) 20 | if (index === -1) throw new Error('Invalid MIME base64 character: ' + base64[i]) 21 | offset += buf.write(emojis[index], offset) 22 | } 23 | 24 | exports.encode.bytes = offset - oldOffset 25 | 26 | return buf 27 | } 28 | 29 | exports.decode = function (buf, offset, len) { 30 | if (typeof buf === 'string') buf = new Buffer(buf) 31 | else if (!Buffer.isBuffer(buf)) throw new Error('Invalid argument! Expected string or Buffer') 32 | 33 | if (offset === undefined) offset = 0 34 | if (len === undefined) len = buf.length 35 | 36 | var oldOffset = offset 37 | var base64 = '' 38 | var emoji, index 39 | 40 | while (offset < len) { 41 | emoji = buf.slice(offset, offset + EMOJI_BYTE_SIZE).toString() 42 | index = emojis.indexOf(emoji) 43 | if (index === -1) throw new Error('Invalid base64-emoji character: ' + emoji) 44 | base64 += mime[index] 45 | offset += EMOJI_BYTE_SIZE 46 | } 47 | 48 | exports.decode.bytes = offset - oldOffset 49 | 50 | return new Buffer(base64, 'base64') 51 | } 52 | 53 | exports.encodingLength = function (obj) { 54 | if (typeof obj === 'string') obj = new Buffer(obj) 55 | else if (!Buffer.isBuffer(obj)) throw new Error('Invalid argument! Expected string or Buffer') 56 | return obj.toString('base64').length * EMOJI_BYTE_SIZE 57 | } 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # base64-emoji 2 | 3 | Inspired by [base-emoji](https://github.com/pfraze/base-emoji) this 4 | module allows for transformation of any binary data to and from emoji 5 | using only 64 different emojicons (+1 for padding). 6 | 7 | ![emojo-all-the-things](https://cloud.githubusercontent.com/assets/10602/8368864/31a7982c-1b7e-11e5-8731-d1728ddfbafa.jpg) 8 | 9 | [![Build status](https://travis-ci.org/watson/base64-emoji.svg?branch=master)](https://travis-ci.org/watson/base64-emoji) 10 | [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](https://github.com/feross/standard) 11 | [![abstract-encoding](https://img.shields.io/badge/abstract--encoding-compliant-brightgreen.svg?style=flat)](https://github.com/mafintosh/abstract-encoding) 12 | 13 | ## Installation 14 | 15 | ``` 16 | npm install base64-emoji 17 | ``` 18 | 19 | ## Usage 20 | 21 | ```js 22 | var emoji = require('base64-emoji') 23 | 24 | var encoded = emoji.encode('Hello World') 25 | var decoded = emoji.decode(encoded) 26 | 27 | console.log(encoded.toString()) // => 🍕📙🕡🌵🎎📙🚢😮🕡🐗🏦🕤🎎📙🕖📫 28 | console.log(decoded.toString()) // => Hello World 29 | ``` 30 | 31 | ## API 32 | 33 | **`buffer = emoji.encode(buffer|string, [buffer], [offset])`** 34 | 35 | The `encode` function takes a buffer or a string and returns a buffer 36 | containing the encoded bytes. The optional 2nd buffer argument will be 37 | used to store the encoded result. If not provided a new buffer will be 38 | allocated. If an offset is passed as the 3rd argument the input will be 39 | encoded into the buffer at that byte offset. The offset defauls to `0`. 40 | 41 | **`buffer = emoji.decode(buffer|string, [offset], [length])`** 42 | 43 | The `decode` function takes a buffer or a string and returns a buffer 44 | containing the decoded bytes. If an offset is passed as the 2nd 45 | argumetn, the input will be decoded from that byte offset. Tye byte 46 | offset defaults to `0`. A length can be passed as the 3rd argument 47 | specifying the number of bytes that should be decoded. The length 48 | defaults to the input byte length. 49 | 50 | **`length = emoji.encodingLength(buffer|string)`** 51 | 52 | Returns the amount of bytes needed to encode the `buffer` or `string` 53 | given as input. 54 | 55 | ## License 56 | 57 | MIT 58 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var test = require('tape') 4 | var base64emoji = require('./') 5 | 6 | test('encode - string (no padding)', function (t) { 7 | base64emoji.encode.bytes = 0 8 | t.deepEqual(base64emoji.encode('foo'), new Buffer('💍🍑🏦🚸')) 9 | t.equal(base64emoji.encode.bytes, 16) 10 | t.end() 11 | }) 12 | 13 | test('decode - string (no padding)', function (t) { 14 | base64emoji.decode.bytes = 0 15 | t.deepEqual(base64emoji.decode('💍🍑🏦🚸'), new Buffer('foo')) 16 | t.equal(base64emoji.decode.bytes, 16) 17 | t.end() 18 | }) 19 | 20 | test('encode - buffer (no padding)', function (t) { 21 | base64emoji.encode.bytes = 0 22 | t.deepEqual(base64emoji.encode(new Buffer('foo')), new Buffer('💍🍑🏦🚸')) 23 | t.equal(base64emoji.encode.bytes, 16) 24 | t.end() 25 | }) 26 | 27 | test('decode - buffer (no padding)', function (t) { 28 | base64emoji.decode.bytes = 0 29 | t.deepEqual(base64emoji.decode(new Buffer('💍🍑🏦🚸')), new Buffer('foo')) 30 | t.equal(base64emoji.decode.bytes, 16) 31 | t.end() 32 | }) 33 | 34 | test('encode - string (with padding)', function (t) { 35 | base64emoji.encode.bytes = 0 36 | t.deepEqual(base64emoji.encode('fo'), new Buffer('💍🍑🚢📫')) 37 | t.equal(base64emoji.encode.bytes, 16) 38 | t.end() 39 | }) 40 | 41 | test('decode - string (with padding)', function (t) { 42 | base64emoji.decode.bytes = 0 43 | t.deepEqual(base64emoji.decode('💍🍑🚢📫'), new Buffer('fo')) 44 | t.equal(base64emoji.decode.bytes, 16) 45 | t.end() 46 | }) 47 | 48 | test('encode - custom output buffer', function (t) { 49 | base64emoji.encode.bytes = 0 50 | var buf = new Buffer(20) 51 | buf.fill(0) 52 | base64emoji.encode('foo', buf) 53 | t.deepEqual(buf, new Buffer('f09f928df09f8d91f09f8fa6f09f9ab800000000', 'hex')) 54 | t.equal(base64emoji.encode.bytes, 16) 55 | t.end() 56 | }) 57 | 58 | test('encode - custom output offset', function (t) { 59 | base64emoji.encode.bytes = 0 60 | var buf = new Buffer(20) 61 | buf.fill(0) 62 | base64emoji.encode('foo', buf, 2) 63 | t.deepEqual(buf, new Buffer('0000f09f928df09f8d91f09f8fa6f09f9ab80000', 'hex')) 64 | t.equal(base64emoji.encode.bytes, 16) 65 | t.end() 66 | }) 67 | 68 | test('decode - custom offset', function (t) { 69 | base64emoji.decode.bytes = 0 70 | var buf = new Buffer('00000000f09f928df09f8d91f09f8fa6f09f9ab8', 'hex') 71 | t.deepEqual(base64emoji.decode(buf, 4), new Buffer('foo')) 72 | t.equal(base64emoji.decode.bytes, 16) 73 | t.end() 74 | }) 75 | 76 | test('decode - custom length', function (t) { 77 | base64emoji.decode.bytes = 0 78 | var buf = new Buffer('0000f09f928df09f8d91f09f8fa6f09f9ab80000', 'hex') 79 | t.deepEqual(base64emoji.decode(buf, 2, 18), new Buffer('foo')) 80 | t.equal(base64emoji.decode.bytes, 16) 81 | t.end() 82 | }) 83 | 84 | test('encode -> decode', function (t) { 85 | base64emoji.encode.bytes = 0 86 | base64emoji.decode.bytes = 0 87 | t.deepEqual(base64emoji.decode(base64emoji.encode(new Buffer('foo'))), new Buffer('foo')) 88 | t.equal(base64emoji.encode.bytes, 16) 89 | t.equal(base64emoji.decode.bytes, 16) 90 | t.end() 91 | }) 92 | 93 | test('encode - number', function (t) { 94 | t.throws(base64emoji.encode.bind(null, 42)) 95 | t.end() 96 | }) 97 | 98 | test('decode - number', function (t) { 99 | t.throws(base64emoji.decode.bind(null, 42)) 100 | t.end() 101 | }) 102 | 103 | test('encode - no input', function (t) { 104 | t.throws(base64emoji.encode) 105 | t.end() 106 | }) 107 | 108 | test('decode - no input', function (t) { 109 | t.throws(base64emoji.decode) 110 | t.end() 111 | }) 112 | 113 | test('encode - empty string', function (t) { 114 | base64emoji.encode.bytes = 42 115 | t.equal(base64emoji.encode('').toString(), '') 116 | t.equal(base64emoji.encode.bytes, 0) 117 | t.end() 118 | }) 119 | 120 | test('decode - empty string', function (t) { 121 | base64emoji.decode.bytes = 42 122 | t.equal(base64emoji.decode('').toString(), '') 123 | t.equal(base64emoji.decode.bytes, 0) 124 | t.end() 125 | }) 126 | 127 | test('encodingLength - string (no padding)', function (t) { 128 | t.deepEqual(base64emoji.encodingLength('foo'), 16) 129 | t.end() 130 | }) 131 | 132 | test('encodingLength - buffer (no padding)', function (t) { 133 | t.deepEqual(base64emoji.encodingLength(new Buffer('foo')), 16) 134 | t.end() 135 | }) 136 | 137 | test('encodingLength - string (with padding)', function (t) { 138 | t.deepEqual(base64emoji.encodingLength('fo'), 16) 139 | t.end() 140 | }) 141 | 142 | test('encodingLength - number', function (t) { 143 | t.throws(base64emoji.encodingLength.bind(null, 42)) 144 | t.end() 145 | }) 146 | 147 | test('encodingLength - no input', function (t) { 148 | t.throws(base64emoji.encodingLength) 149 | t.end() 150 | }) 151 | 152 | test('encodingLength - empty string', function (t) { 153 | t.equal(base64emoji.encodingLength(''), 0) 154 | t.end() 155 | }) 156 | --------------------------------------------------------------------------------