Permalink
Browse files

Tag the v0.1.0 release

  • Loading branch information...
mathiasbynens committed May 27, 2013
0 parents commit 0ab85f2918c7fbaaf7008cef0dd6e5655e078ac2
Showing with 824 additions and 0 deletions.
  1. +2 −0 .gitattributes
  2. +17 −0 .gitignore
  3. +5 −0 .npmignore
  4. +4 −0 .travis.yml
  5. +77 −0 Gruntfile.js
  6. +171 −0 README.md
  7. +103 −0 bin/esrever
  8. +72 −0 esrever.js
  9. +72 −0 man/esrever.1
  10. +55 −0 package.json
  11. +24 −0 src/data.js
  12. +72 −0 src/esrever.js
  13. +35 −0 tests/index.html
  14. +115 −0 tests/tests.js
@@ -0,0 +1,2 @@
# Automatically normalize line endings for all text-based files
* text=auto
@@ -0,0 +1,17 @@
# JSON version of coverage report
coverage/coverage.json
# 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
@@ -0,0 +1,5 @@
.*
*.md
coverage
tests
Gruntfile.js
@@ -0,0 +1,4 @@
language: node_js
node_js:
- "0.10"
- "0.8"
@@ -0,0 +1,77 @@
module.exports = function(grunt) {
var commandOptions = {
'stdout': true,
'stderr': true,
'failOnError': true
};
grunt.initConfig({
'meta': {
'testFile': 'tests/tests.js'
},
'shell': {
'options': {
'stdout': true,
'stderr': true,
'failOnError': true
},
'cover': {
'command': 'istanbul cover --report "html" --verbose --dir "coverage" "<%= meta.testFile %>"'
},
// Rhino 1.7R4 has a bug that makes it impossible to test Esrever.
// 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 "<%= meta.testFile %>"'
},
'test-narwhal': {
'command': 'echo "Testing in Narwhal..."; export NARWHAL_OPTIMIZATION=-1; narwhal "<%= meta.testFile %>"'
},
'test-node': {
'command': 'echo "Testing in Node..."; node "<%= meta.testFile %>"'
},
'test-browser': {
'command': 'echo "Testing in a browser..."; open "tests/index.html"'
}
},
'template': {
'build-esrever': {
'options': {
// Generate the regular expressions dynamically using Regenerate
'data': require('./src/data.js')
},
'files': {
'esrever.js': ['src/esrever.js']
}
}
}
});
grunt.loadNpmTasks('grunt-template');
grunt.loadNpmTasks('grunt-shell');
grunt.registerTask('cover', 'shell:cover');
grunt.registerTask('test', [
'shell:test-rhino',
'shell:test-ringo',
'shell:test-narwhal',
'shell:test-node',
'shell:test-browser'
]);
grunt.registerTask('default', [
'template',
'shell:test-node',
'cover'
]);
};
171 README.md
@@ -0,0 +1,171 @@
# Esrever
[![Build status](https://travis-ci.org/mathiasbynens/esrever.png?branch=master)](https://travis-ci.org/mathiasbynens/esrever)
[![Dependency status](https://gemnasium.com/mathiasbynens/esrever.png)](https://gemnasium.com/mathiasbynens/esrever)
_Esrever_ is a Unicode-aware string reverser written in JavaScript. It allows you to easily reverse any string of Unicode symbols, while handling combining marks and astral symbols just fine.
## Why not just use `string.split('').reverse().join('')`?
The following code snippet is commonly used to reverse a string in JavaScript:
```js
// Don’t use this!
var naiveReverse = function(string) {
return string.split('').reverse().join('');
}
```
However, there are some problems with this solution. For example:
```js
naiveReverse('foo 𝌆 bar');
// → 'rab �� oof'
// Where did the `𝌆` symbol go? Whoops!
```
If you’re wondering why this happens, [read up on JavaScript’s internal character encoding](http://mathiasbynens.be/notes/javascript-encoding).
But there’s more:
```js
naiveReverse('mañana mañana');
// → 'anãnam anañam'
// Wait, so now the tilde is applied to the `a` instead of the `n`? WAT.
```
In order to correctly reverse any given string, Esrever implements an algorithm that was [originally developed](http://www.youtube.com/watch?v=UODX_pYpVxk&t=33s "Work It") by Missy ‘Misdemeanor’ Elliot in September 2002:
> _I put my thang down, flip it, and reverse it.
> I put my thang down, flip it, and reverse it._
And indeed: by swapping the position of any combining marks with the symbol they belong to, as well as reversing any surrogate pairs before further processing the string, the above issues are avoided successfully. Thanks, Missy!
## Installation
In a browser:
```html
<script src="esrever.js"></script>
```
Via [npm](http://npmjs.org/):
```bash
npm install esrever
```
In [Narwhal](http://narwhaljs.org/), [Node.js](http://nodejs.org/), and [RingoJS](http://ringojs.org/):
```js
var esrever = require('esrever');
```
In [Rhino](http://www.mozilla.org/rhino/):
```js
load('esrever.js');
```
Using an AMD loader like [RequireJS](http://requirejs.org/):
```js
require(
{
'paths': {
'esrever': 'path/to/esrever'
}
},
['esrever'],
function(esrever) {
console.log(esrever);
}
);
```
## API
### `esrever.version`
A string representing the semantic version number.
### `esrever.reverse(string)`
This function takes a string and returns the reversed version of that string, correctly accounting for Unicode combining marks and astral symbols.
## Usage example
```js
var input = 'Lorem ipsum 𝌆 dolor sit ameͨ͆t.';
var reversed = esrever.reverse(input);
console.log(reversed);
// → '.teͨ͆ma tis rolod 𝌆 muspi meroL'
reverse(reversed) == input;
// → true
```
### Using the `esrever` binary
To use the `esrever` binary in your shell, simply install Esrever globally using npm:
```bash
npm install -g esrever
```
After that you will be able to reverse strings from the command line:
```bash
$ esrever 'I put my thang down, flip it, and reverse it.'
.ti esrever dna ,ti pilf ,nwod gnaht ym tup I
$ esrever 'H̹̙̦̮͉̩̗̗ͧ̇̏̊̾Eͨ͆͒̆ͮ̃͏̷̮̣̫̤̣ ̵̞̹̻̀̉̓ͬ͑͡ͅCͯ̂͐͏̨̛͔̦̟͈̻O̜͎͍͙͚̬̝̣̽ͮ͐͗̀ͤ̍̀͢M̴̡̲̭͍͇̼̟̯̦̉̒͠Ḛ̛̙̞̪̗ͥͤͩ̾͑̔͐ͅṮ̴̷̷̗̼͍̿̿̓̽͐H̙̙̔̄͜'
H̙̙̔̄͜Ṯ̴̷̷̗̼͍̿̿̓̽͐Ḛ̛̙̞̪̗ͥͤͩ̾͑̔͐ͅM̴̡̲̭͍͇̼̟̯̦̉̒͠O̜͎͍͙͚̬̝̣̽ͮ͐͗̀ͤ̍̀͢Cͯ̂͐͏̨̛͔̦̟͈̻ ̵̞̹̻̀̉̓ͬ͑͡ͅEͨ͆͒̆ͮ̃͏̷̮̣̫̤̣H̹̙̦̮͉̩̗̗ͧ̇̏̊̾
$ cat foo.txt
These are the contents of `foo.txt`.
This is line two.
$ esrever -f foo.txt
.owt enil si sihT
.`txt.oof` fo stnetnoc eht era esehT
$ esrever -l foo.txt
.`txt.oof` fo stnetnoc eht era esehT
.owt enil si sihT
```
Why not just use the good old `rev` command instead? Glad you asked. `rev` doesn’t account for Unicode combining marks:
```bash
$ rev <<< 'mañana mañana'
anãnam anañam
```
On the other hand, the `esrever` binary returns the expected result:
```
$ esrever 'mañana mañana'
anañam anañam
```
See `esrever --help` for the full list of options.
## Unit tests & code coverage
After cloning this repository, run `npm install` to install the dependencies needed for Esrever 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](http://rawgithub.com/mathiasbynens/esrever/master/coverage/esrever/esrever.js.html), use `grunt cover`.
## Author
| [![twitter/mathias](http://gravatar.com/avatar/24e08a9ea84deb17ae121074d0f17125?s=70)](http://twitter.com/mathias "Follow @mathias on Twitter") |
|---|
| [Mathias Bynens](http://mathiasbynens.be/) |
## License
Esrever is dual licensed under the [MIT](http://mths.be/mit) and [GPL](http://mths.be/gpl) licenses.
@@ -0,0 +1,103 @@
#!/usr/bin/env node
(function() {
var fs = require('fs');
var esrever = require('../esrever.js');
var reverse = esrever.reverse;
var strings = process.argv.splice(2);
var option = strings.shift();
var isFile = false;
var maintainLineOrder = false;
var stdin = process.stdin;
var data;
var log = console.log;
var main = function() {
if (/^(?:-h|--help)$/.test(option) || option == null && !strings.length) {
log('esrever v%s - http://mths.be/esrever', esrever.version);
log('\nUsage:\n');
log('\tesrever [string ...]');
log('\tesrever [-f | --file] [file ...]');
log('\tesrever [-l | --lines] [file ...]');
log('\tesrever [-v | --version]');
log('\tesrever [-h | --help]');
log('\nExamples:\n');
log('\tesrever \'ma\xF1ana man\u0303ana\'');
log('\tesrever -f foo.txt');
log('\tesrever -l foo.txt');
log('\techo \'I put my thang down, flip it, and reverse it.\' | esrever');
log('\n\n\tesrever "H\u0339\u0319\u0326\u032E\u0349\u0329\u0317\u0317\u0367\u0307\u030F\u030A\u033EE\u0368\u0346\u0352\u0306\u036E\u0303\u034F\u0337\u032E\u0323\u032B\u0324\u0323 \u0335\u031E\u0339\u033B\u0300\u0309\u0313\u036C\u0351\u0361\u0345C\u036F\u0302\u0350\u034F\u0328\u031B\u0354\u0326\u031F\u0348\u033BO\u031C\u034E\u034D\u0359\u035A\u032C\u031D\u0323\u033D\u036E\u0350\u0357\u0300\u0364\u030D\u0300\u0362M\u0334\u0321\u0332\u032D\u034D\u0347\u033C\u031F\u032F\u0326\u0309\u0312\u0360\u1E1A\u031B\u0319\u031E\u032A\u0317\u0365\u0364\u0369\u033E\u0351\u0314\u0350\u0345\u1E6E\u0334\u0337\u0337\u0317\u033C\u034D\u033F\u033F\u0313\u033D\u0350H\u0319\u0319\u0314\u0304\u035C"\n\n');
return process.exit(1);
}
if (/^(?:-v|--version)$/.test(option)) {
log('v%s', esrever.version);
return process.exit(1);
}
if (/^(?:-f|--file)$/.test(option)) {
isFile = true;
} else if (/^(?:-l|--lines)$/.test(option)) {
maintainLineOrder = true;
isFile = true; // TODO: enable this option for strings too
} else {
// The first argument is not a recognized option; treat it as a string
option && strings.unshift(option);
}
if (!strings.length) {
log('Error: option `%s` requires an argument.', option);
log('Try `esrever --help` for more information.');
return process.exit(1);
}
strings.forEach(function(string) {
var result;
if (isFile) {
try {
string = fs.readFileSync(string, 'utf8');
} catch(error) {
log('Error: no such file. (`%s`)', string);
return process.exit(1);
}
}
try {
if (maintainLineOrder) {
result = string.split(/\r\n?|\n/g).map(function(line) {
return reverse(line);
}).join(require('os').EOL);
} else {
result = reverse(string);
}
log(result);
} catch(error) {
log('Error: failed to reverse.');
log('If you think this is a bug in esrever, please report it:');
log('https://github.com/mathiasbynens/esrever/issues/new')
return process.exit(1);
}
});
// Return with exit status 0 outside of the `forEach` loop, in case
// multiple strings or files were passed in.
return process.exit(0);
};
if (stdin.isTTY) {
// handle shell arguments
main();
} else {
// handle pipe
data = '';
stdin.on('data', function(chunk) {
data += chunk;
});
stdin.on('end', function() {
strings.unshift(data.trim());
main();
});
stdin.resume();
}
}());
Oops, something went wrong.

0 comments on commit 0ab85f2

Please sign in to comment.