Compare commits

..

135 Commits

Author SHA1 Message Date
John Crepezzi
9b0a5ff0a3
Merge pull request #291 from j3parker/s3-document-store
Add an Amazon S3 document store
2020-02-27 11:39:12 -05:00
Jacob Parker
1fff48568f Document the IAM permissions 2019-07-08 16:59:04 +01:00
Jacob Parker
b4c666fbcf Add an Amazon S3 document store 2019-06-28 19:25:49 +01:00
John Crepezzi
b866c33c93
Merge pull request #173 from sebastiansterk/master
removed padding for #box for correct view
2019-04-05 17:30:42 -04:00
John Crepezzi
f3838ab4a8
Merge pull request #251 from seejohnrun/handle-redis-disconnect
Handle redis error and re-establish connection
2018-09-19 10:38:34 -04:00
John Crepezzi
bf2b1c957a Handle redis error and re-establish connection 2018-09-19 10:37:34 -04:00
John Crepezzi
ee1c1c0856
Merge pull request #231 from seejohnrun/ensure-raw-utf8
Added charset to raw content type
2018-07-12 14:26:05 -04:00
John Crepezzi
b087ac8dd1 Added charset to raw content type
Closes #230
2018-07-12 14:25:27 -04:00
John Crepezzi
d922667f56
Merge pull request #221 from PassTheMayo/patch-1
Fixed unnecessary logging when document not found
2018-04-30 19:45:16 -04:00
Jacob Gunther
5f6fefa7a6
Fixed unnecessary logging when document not found 2018-04-30 16:40:28 -05:00
John Crepezzi
faa7e679ca
Merge pull request #216 from PassTheMayo/master
Fixed RethinkDB document store
2018-04-16 11:53:28 -04:00
Jacob Gunther
cd3bf26dbe
Use local method for md5 2018-04-16 10:52:53 -05:00
Jacob Gunther
830dc1bc43 Use uploads table 2018-04-15 23:16:39 -05:00
Jacob Gunther
dc0f151a7f Fixed bug in RethinkDB document store and use classes 2018-04-15 23:16:08 -05:00
John Crepezzi
7f625e22f7
Merge pull request #203 from seejohnrun/rewrite_memcached
Rewrite the memcached client
2018-04-13 16:10:42 -04:00
John Crepezzi
528b7b07a8
Merge pull request #215 from Razzeee/patch-1
Update docs to real defaults
2018-04-09 13:08:36 -04:00
Razzeee
2b81e67ce7
Update docs to real defaults 2018-04-09 19:05:21 +02:00
John Crepezzi
827e7b51b5 Rewrite the memcached client
* Update syntax to ES6
* Use `memcached` instead of `memcache`
* Fix restrictions where expirations weren't pushed forward on GET
* Fix a bug where we were unnecessarily bumping expirations on key search

Closes #201
2018-02-16 09:52:44 -05:00
John Crepezzi
ad7702aaf4
Merge pull request #194 from szepeviktor/patch-1
Change to HTTPS in about.md
2018-01-22 10:30:12 -05:00
Viktor Szépe
f5fbc8d19e
Change to HTTPS in about.md
Please also change the repo URL here on GitHub
2018-01-22 14:51:15 +01:00
John Crepezzi
0a8923bf12
Merge pull request #192 from C0rn3j/master
Add note about paste expiration, cosmetic fixes.
2017-12-30 20:24:01 -05:00
Martin
4d572a2ec0
convert relative path to absolute 2017-12-30 17:11:29 +01:00
Martin
d9a53d3e6e
Add note about paste expiration, cosmetic fixes. 2017-12-29 20:42:11 +01:00
John Crepezzi
8da37ea5de
Merge pull request #190 from PassTheMayo/patch-1
Oh noes! I didn't even notice that I had a typo...
2017-12-11 10:51:12 -05:00
Jacob Gunther
ff0fccd6c2
Oh noes! I didn't even notice that I had a typo... 2017-12-11 09:50:50 -06:00
John Crepezzi
63c4576633
Merge pull request #189 from PassTheMayo/master
Added RethinkDB storage option & fixed config to use proper JSON
2017-12-11 10:46:45 -05:00
Jacob Gunther
b31d143bcd
Revert config.js to previous state 2017-12-11 09:45:37 -06:00
Jacob Gunther
0d8aec8d61
Oops, forgot to fix that file name 2017-12-11 09:44:23 -06:00
Jacob Gunther
1f9fdd205d
Undid changes to server.js 2017-12-11 09:28:27 -06:00
Jacob Gunther
cdd0cf3739
Fixed requested changes to RethinkDB handler 2017-12-11 09:27:44 -06:00
PassTheMayo
ba5c6b8d16 Added RethinkDB storage option & fixed config to use proper JSON 2017-12-09 18:34:00 -06:00
John Crepezzi
cfef588283
Merge pull request #181 from seejohnrun/simplify_uglify
Upgrade uglify and simplify usage
2017-10-31 21:20:23 -04:00
John Crepezzi
318c5f7ba6 Upgrade uglify and simplify usage
- Upgrade to the most recent version of uglify
- Use the `UglifyJS.minify(code)` helper which does exactly what we want
2017-10-31 21:19:22 -04:00
John Crepezzi
ee03e7cd78
Merge pull request #180 from seejohnrun/es6_generators
ES6 generators
2017-10-31 21:11:32 -04:00
John Crepezzi
3b6934e348 Phonetic key generator to es6 and add some tests 2017-10-31 21:10:25 -04:00
John Crepezzi
f161cc33b4 Added tests and converted dictionary key generator to es6 2017-10-31 20:55:59 -04:00
John Crepezzi
40f1f2588e Update some es6 2017-10-31 20:55:51 -04:00
John Crepezzi
e4e025f67e Convert random generator to es6 and add some specs for it directly 2017-10-31 20:40:43 -04:00
John Crepezzi
e12805a8aa
Merge pull request #179 from seejohnrun/upgrade_testing
Upgrade testing libraries
2017-10-31 20:04:37 -04:00
John Crepezzi
e76c845f16 Upgrade testing libraries
- Upgrade mocha
- Remove should due to limited usage and old style (at least by rspec standards)
- Move spec -> test which is the default
- Update tests accordingly for the above
2017-10-31 20:03:30 -04:00
John Crepezzi
072418695e
Merge pull request #178 from seejohnrun/upgrade_highlight
Upgrade highlight.js
2017-10-31 19:49:20 -04:00
John Crepezzi
584b66bc66 Upgrade highlight.js 2017-10-31 19:48:55 -04:00
Sebastian Sterk
f8db455f74 removed padding for #box for correct view 2017-10-13 15:13:04 +02:00
John Crepezzi
f19c5d1049 Fix typo in README
Closes #165
2017-08-03 11:45:51 +00:00
John Crepezzi
c5b859ec98 Bump node engine version & fix asset compression on start 2017-07-11 21:13:33 -04:00
John Crepezzi
2ee93a7409 Merge pull request #160 from seejohnrun/complete_eslint
Fix eslint
2017-06-26 12:38:57 -04:00
John Crepezzi
bf1dbb68b8 Fix eslint 2017-06-26 12:38:17 -04:00
John Crepezzi
cf28e23d8e Merge pull request #159 from seejohnrun/add_eslint
Added eslint and fixed an issue from #158
2017-06-26 12:29:10 -04:00
John Crepezzi
5939dec185 Added eslint and fixed an issue from #158 2017-06-26 12:19:36 -04:00
John Crepezzi
3ed1d775ac Merge pull request #158 from KlasafGeijerstam/master
Added dictionary key generator
2017-06-26 12:11:44 -04:00
John Crepezzi
87b1c76aaf One more 2017-06-26 12:11:19 -04:00
John Crepezzi
4599203bdf A few style nit-picks 2017-06-26 12:10:57 -04:00
Klas af Geijerstam
d66bc9a6c4 Removed unused lines 2017-06-26 18:09:13 +02:00
Klas af Geijerstam
80f0618736 Updated dictionary.js
Now expects a newline separated dictionary, supports both \n and \n\r
2017-06-26 18:03:18 +02:00
Klas af Geijerstam
ac2bceefbb Added missing ) 2017-06-26 17:42:24 +02:00
Klas af Geijerstam
dbf4f6b5dd Removed usage of random-js
Replaced random-js with vanilla JS random
2017-06-26 17:39:32 +02:00
Klas af Geijerstam
8e9205cecc Update dictionary.js 2017-06-26 17:37:04 +02:00
Klas af Geijerstam
e54a860172 Added dictionary.js
A key generator that uses a dictionary to create its keys
2017-06-26 17:17:52 +02:00
John Crepezzi
5a8697cdd8 Fix typo in about.md 2017-05-02 17:32:34 -04:00
John Crepezzi
091ea973a8 Merge pull request #138 from Wohlstand/patch-2
Added a note about Redis's password field
2017-04-21 07:51:23 -04:00
John Crepezzi
939b7221ab Merge pull request #146 from seejohnrun/dont_expire_static_documents
Don't expire static documents on raw read
2017-01-28 11:58:12 -05:00
John Crepezzi
934aaf7f51 Don't expire static documents on raw read 2017-01-28 11:57:25 -05:00
Vitaly Novichkov
930e21ccb7 Added a note about Redis's password field 2016-11-06 02:25:51 +04:00
John Crepezzi
eb5c8eef6a Merge pull request #135 from seejohnrun/horizontal_scroll_fix
Fix horizontal scroll overflow
2016-09-30 10:40:15 -05:00
John Crepezzi
03dd611a86 Fix horizontal scroll overflow
Caused by highlight.js upgrade
2016-09-30 09:57:46 -05:00
John Crepezzi
f24376b192 Merge pull request #129 from Wohlstand/patch-1
Fix working error under Firefox
2016-09-19 09:36:56 -04:00
Vitaly Novichkov
eea359d0ec Update index.html
Short charset command better for HTML5
2016-09-13 15:11:20 +04:00
John Crepezzi
af9a71549b Upgrade highlight.js
Also adds swift support

Closes #69
2016-09-12 20:10:15 -04:00
John Crepezzi
3178676fba Merge pull request #131 from seejohnrun/update_doc
Update documentation to mention redis-server
2016-08-22 13:23:11 -04:00
John Crepezzi
3bdfab8219 Update documentation to mention redis-server 2016-08-22 13:22:49 -04:00
John Crepezzi
a3a24d9765 Merge pull request #127 from Gwemox/master
Compatibility with screen reader
2016-08-17 14:03:13 -04:00
Vitaly Novichkov
8afb53e77e Fix working error under Firefox
On some computers because undeclared charset, highlight.min.js parsing incorrectly while some charsets (For example, Cyrillic-1251) are toggled. Problem is going away when I manually toggling Europan or UTF-8 charset.
2016-08-03 14:15:58 +04:00
Thibault Buathier
d6d9cf40f9 Compatibility with screen reader 2016-07-30 16:15:53 +02:00
Thibault Buathier
1010a142e2 Compatibility with screen reader 2016-07-30 16:15:26 +02:00
John Crepezzi
0209375865 Revert "Added cloudron external provider"
This reverts commit 00a9d9c3124ff34cfdcb917e1ff66f798f13fa7a.
2016-03-12 13:09:28 -05:00
John Crepezzi
00a9d9c312 Added cloudron external provider 2016-03-12 13:07:58 -05:00
John Crepezzi
6835eef468 Merge pull request #109 from seejohnrun/rate_limiting
Added user-configurable rate limiting
2016-03-10 11:44:57 -10:00
John Crepezzi
4626fd9c8d Merge pull request #81 from pangeacake/master
Add postgres information to README.md
2016-03-06 19:06:04 -05:00
John Crepezzi
fbb6e63c37 Added a note to the README 2016-03-06 16:34:33 -05:00
John Crepezzi
84c909a5db Added user-configurable rate limiting 2016-03-06 16:20:40 -05:00
John Crepezzi
45e19bc7cc fix indentation 2015-12-27 12:59:59 -05:00
John Crepezzi
233bc6ff16 Merge pull request #77 from abn/redis-auth
Support authentication for redis store if password provided
2015-12-27 12:58:24 -05:00
PangeaCake
360b325ced Smaller typo 2015-01-07 14:30:12 -08:00
PangeaCake
e93f98112b Add pg as dependency and update node version
One of the dependencies seemed to be broken with the previous node version, but this node version worked perfectly
2015-01-07 14:27:46 -08:00
PangeaCake
05cb051bc8 Tiny fix 2015-01-07 14:25:43 -08:00
PangeaCake
031cdd738a Add postgres information to README.md 2015-01-07 14:24:50 -08:00
Arun Babu Neelicattu
c92ab077c0 Support authentication for redis store if password provided 2014-11-21 23:17:19 +10:00
John Crepezzi
6c31389327 Merge pull request #64 from lidl/master
Make table creation comment a one-liner.
2014-07-16 07:54:00 -04:00
lidl
a8d4f3c300 Make table creation comment a one-liner. 2014-06-27 18:40:05 -04:00
John Crepezzi
ab029eae2f Added postgres adapter 2014-06-09 16:50:43 -04:00
John Crepezzi
447d0aae76 PG basis 2014-06-09 14:48:35 -04:00
John Crepezzi
4870158430 Merge branch 'master' of github.com:seejohnrun/haste-server 2014-04-21 14:17:09 -04:00
John Crepezzi
0471b059a0 Support a form-data POST API
Closes #54
2014-04-21 14:16:23 -04:00
John Crepezzi
5bbe50b481 Merge pull request #59 from jomo/phonetic
Phonetic key improvements
2014-04-15 11:00:49 -04:00
JonApps
bda2749879 oops 🍺 2014-03-25 02:23:31 +01:00
JonApps
028aa96b13 phonetic keys can begin with vowel + added missing \'z\' to consontants 2014-03-25 02:20:05 +01:00
John Crepezzi
2deda5b68a Merge pull request #56 from joeykrim/patch-1
Small typo
2014-02-27 19:26:31 -05:00
joeykrim
ee7098457e Small typo
Changed "its all open source" to "it's all open source"
2014-02-27 17:57:37 -05:00
John Crepezzi
7a08960414 Merge branch 'master' of github.com:seejohnrun/haste-server 2013-11-24 11:54:34 -05:00
John Crepezzi
89909747f1 Don't depend on err.message for redis errors [#49] 2013-11-24 11:54:01 -05:00
John Crepezzi
202e695e07 Remove GA from index.html on Master 2013-10-31 08:44:33 -04:00
John Crepezzi
48e8e79659 Remove support from README 2013-08-13 13:06:37 -04:00
John Crepezzi
abb49f2cf3 update about.md 2013-03-12 21:59:10 -04:00
John Crepezzi
d1cd2a5213 Proper 2013-01-11 10:00:35 -08:00
John Crepezzi
27317844e0 Remove nl 2013-01-11 09:57:10 -08:00
John Crepezzi
ee74e2fa90 Merge branch 'production' of github.com:seejohnrun/haste-server into production 2012-12-29 18:15:45 -05:00
John Crepezzi
5d8bd2e6f8 Merge branch 'master' into production 2012-12-29 18:15:32 -05:00
John Crepezzi
cd4c7aeab8 Merge pull request #37 from naftis/patch-1
Bugfix to solarized_dark.css
2012-12-28 07:10:36 -08:00
naftis
e37c3cf1b9 Bugfix to solarized_dark.css
Fixed bug with font-style: italic and WebKit.
WebKit makes line-height bigger, when italic is used.
2012-12-28 16:11:21 +02:00
John Crepezzi
8858bab985 Merge branch 'master' into production 2012-12-23 10:54:15 -05:00
John Crepezzi
afb0c332cc Added shift modifier to twitter shortcut
Closes #29
2012-12-23 10:53:53 -05:00
John Crepezzi
82c58c5c0c Merge branch 'master' into production 2012-12-19 08:18:14 -05:00
John Crepezzi
46bdd27431 Fix for type name ;)
Closes #28
2012-12-19 08:17:52 -05:00
John Crepezzi
1adfba1a37 Merge branch 'master' into production 2012-12-19 08:13:36 -05:00
John Crepezzi
54e55b1b0d Added JSON to extension map (JS)
Closes #28
2012-12-19 08:12:08 -05:00
John Crepezzi
08d37cc7f7 Added support section 2012-10-22 14:40:52 -04:00
John Crepezzi
aa781957e8 Update Copyright 2012-09-27 13:46:27 -04:00
John Crepezzi
c00477c93c Update Copyright 2012-09-27 13:46:09 -04:00
John Crepezzi
035f09ac05 GA 2012-09-27 13:43:53 -04:00
John Crepezzi
36e00bb29e Remove 'localhost' references 2012-09-27 12:03:52 -04:00
John Crepezzi
10623873e8 Allow host setting by ENV 2012-09-27 12:01:00 -04:00
John Crepezzi
e536ba1019 Move to an available npm version 2012-09-27 11:56:49 -04:00
John Crepezzi
85fc36d710 Update npm version 2012-09-27 11:56:15 -04:00
John Crepezzi
5d5ae164f3 Set up node engine version 2012-09-27 11:54:40 -04:00
John Crepezzi
79309c75df Bump version to 0.1.0 2012-09-27 11:51:15 -04:00
John Crepezzi
4b58c8d356 Added more loggin 2012-09-27 11:50:56 -04:00
John Crepezzi
8f0d6260b0 change how redistogo install works 2012-09-27 11:50:12 -04:00
John Crepezzi
93a83a35da Logging 2012-09-27 11:47:23 -04:00
John Crepezzi
4efc5d47d9 Allow redistogo 2012-09-27 11:46:53 -04:00
John Crepezzi
ff8ef54e34 Procfile 2012-09-27 11:38:14 -04:00
John Crepezzi
814a49812a Update server config path 2012-09-19 14:28:52 -04:00
John Crepezzi
e0610bc1be Fix multiple document loading
Closes #32
2012-08-13 11:33:20 -04:00
John Crepezzi
962976c204 Pad the right 2012-06-22 15:33:07 -04:00
John Crepezzi
16080bdc16 Update description - preparing for npm push 2012-04-21 23:49:39 -04:00
John Crepezzi
20ce741341 Fix indentation 2012-04-07 23:51:48 -04:00
32 changed files with 1976 additions and 678 deletions

2
.eslintignore Normal file
View File

@ -0,0 +1,2 @@
**/*.min.js
config.js

25
.eslintrc.json Normal file
View File

@ -0,0 +1,25 @@
{
"env": {
"es6": true,
"node": true
},
"extends": "eslint:recommended",
"rules": {
"indent": [
"error",
2
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
]
}
}

1
Procfile Normal file
View File

@ -0,0 +1 @@
web: node server.js

115
README.md
View File

@ -38,14 +38,24 @@ STDOUT. Check the README there for more details and usages.
* `host` - the host the server runs on (default localhost)
* `port` - the port the server runs on (default 7777)
* `keyLength` - the length of the keys to user (default 10)
* `maxLength` - maximum length of a paste (default none)
* `maxLength` - maximum length of a paste (default 400000)
* `staticMaxAge` - max age for static assets (86400)
* `recompressStatisAssets` - whether or not to compile static js assets (true)
* `recompressStaticAssets` - whether or not to compile static js assets (true)
* `documents` - static documents to serve (ex: http://hastebin.com/about.com)
in addition to static assets. These will never expire.
* `storage` - storage options (see below)
* `logging` - logging preferences
* `keyGenerator` - key generator options (see below)
* `rateLimits` - settings for rate limiting (see below)
## Rate Limiting
When present, the `rateLimits` option enables built-in rate limiting courtesy
of `connect-ratelimit`. Any of the options supported by that library can be
used and set in `config.json`.
See the README for [connect-ratelimit](https://github.com/dharmafly/connect-ratelimit)
for more information!
## Key Generation
@ -87,11 +97,14 @@ something like:
}
```
Where `path` represents where you want the files stored
where `path` represents where you want the files stored.
File storage currently does not support paste expiration, you can follow [#191](https://github.com/seejohnrun/haste-server/issues/191) for status updates.
### Redis
To use redis storage you must install the redis package in npm
To use redis storage you must install the `redis` package in npm, and have
`redis-server` running on the machine.
`npm install redis`
@ -112,11 +125,40 @@ or post.
All of which are optional except `type` with very logical default values.
If your Redis server is configured for password authentification, use the `password` field.
### Postgres
To use postgres storage you must install the `pg` package in npm
`npm install pg`
Once you've done that, your config section should look like:
``` json
{
"type": "postgres",
"connectionUrl": "postgres://user:password@host:5432/database"
}
```
You can also just set the environment variable for `DATABASE_URL` to your database connection url.
You will have to manually add a table to your postgres database:
`create table entries (id serial primary key, key varchar(255) not null, value text not null, expiration int, unique(key));`
You can also set an `expire` option to the number of seconds to expire keys in.
This is off by default, but will constantly kick back expirations on each view
or post.
All of which are optional except `type` with very logical default values.
### Memcached
To use memcached storage you must install the `memcache` package via npm
To use memcache storage you must install the `memcached` package via npm
`npm install memcache`
`npm install memcached`
Once you've done that, your config section should look like:
@ -134,6 +176,65 @@ forward on GETs.
All of which are optional except `type` with very logical default values.
### RethinkDB
To use the RethinkDB storage system, you must install the `rethinkdbdash` package via npm
`npm install rethinkdbdash`
Once you've done that, your config section should look like this:
``` json
{
"type": "rethinkdb",
"host": "127.0.0.1",
"port": 28015,
"db": "haste"
}
```
In order for this to work, the database must be pre-created before the script is ran.
Also, you must create an `uploads` table, which will store all the data for uploads.
You can optionally add the `user` and `password` properties to use a user system.
### Amazon S3
To use [Amazon S3](https://aws.amazon.com/s3/) as a storage system, you must
install the `aws-sdk` package via npm:
`npm install aws-sdk`
Once you've done that, your config section should look like this:
```json
{
"type": "amazon-s3",
"bucket": "your-bucket-name",
"region": "us-east-1"
}
```
Authentication is handled automatically by the client. Check
[Amazon's documentation](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/setting-credentials-node.html)
for more information. You will need to grant your role these permissions to
your bucket:
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Effect": "Allow",
"Resource": "arn:aws:s3:::your-bucket-name-goes-here/*"
}
]
}
```
## Author
@ -143,7 +244,7 @@ John Crepezzi <john.crepezzi@gmail.com>
(The MIT License)
Copyright © 2011 John Crepezzi
Copyright © 2011-2012 John Crepezzi
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

View File

@ -15,11 +15,11 @@ To make a new entry, click "New" (or type 'control + n')
## From the Console
Most of the time I want to show you some text, its coming from my current
Most of the time I want to show you some text, it's coming from my current
console session. We should make it really easy to take code from the console
and send it to people.
`cat something | haste` # http://hastebin.com/1238193
`cat something | haste` # https://hastebin.com/1238193
You can even take this a step further, and cut out the last step of copying the
URL with:
@ -38,7 +38,8 @@ right now.
## Duration
Pastes will stay for 30 days from their last view.
Pastes will stay for 30 days from their last view. They may be removed earlier
and without notice.
## Privacy
@ -49,7 +50,7 @@ pastes.
## Open Source
Haste can easily be installed behind your network, and its all open source!
Haste can easily be installed behind your network, and it's all open source!
* [haste-client](https://github.com/seejohnrun/haste-client)
* [haste-server](https://github.com/seejohnrun/haste-server)

View File

@ -1,6 +1,6 @@
{
"host": "localhost",
"host": "0.0.0.0",
"port": 7777,
"keyLength": 10,
@ -23,11 +23,19 @@
"type": "phonetic"
},
"rateLimits": {
"categories": {
"normal": {
"totalRequests": 500,
"every": 60000
}
}
},
"storage": {
"type": "redis",
"host": "localhost",
"port": 6379,
"db": 2,
"type": "memcached",
"host": "127.0.0.1",
"port": 11211,
"expire": 2592000
},

View File

@ -1,4 +1,5 @@
var winston = require('winston');
var Busboy = require('busboy');
// For handling serving stored documents
@ -35,7 +36,7 @@ DocumentHandler.prototype.handleRawGet = function(key, response, skipExpire) {
this.store.get(key, function(ret) {
if (ret) {
winston.verbose('retrieved raw document', { key: key });
response.writeHead(200, { 'content-type': 'text/plain' });
response.writeHead(200, { 'content-type': 'text/plain; charset=UTF-8' });
response.end(ret);
}
else {
@ -51,11 +52,10 @@ DocumentHandler.prototype.handlePost = function(request, response) {
var _this = this;
var buffer = '';
var cancelled = false;
request.on('data', function(data) {
if (!buffer) {
response.writeHead(200, { 'content-type': 'application/json' });
}
buffer += JSON.parse(data.toString()).data;
// What to do when done
var onSuccess = function () {
// Check length
if (_this.maxLength && buffer.length > _this.maxLength) {
cancelled = true;
winston.warn('document >maxLength', { maxLength: _this.maxLength });
@ -63,14 +63,14 @@ DocumentHandler.prototype.handlePost = function(request, response) {
response.end(
JSON.stringify({ message: 'Document exceeds maximum length.' })
);
return;
}
});
request.on('end', function(end) {
if (cancelled) return;
// And then save if we should
_this.chooseKey(function (key) {
_this.store.set(key, buffer, function (res) {
if (res) {
winston.verbose('added document', { key: key });
response.writeHead(200, { 'content-type': 'application/json' });
response.end(JSON.stringify({ key: key }));
}
else {
@ -80,12 +80,37 @@ DocumentHandler.prototype.handlePost = function(request, response) {
}
});
});
};
// If we should, parse a form to grab the data
var ct = request.headers['content-type'];
if (ct && ct.split(';')[0] === 'multipart/form-data') {
var busboy = new Busboy({ headers: request.headers });
busboy.on('field', function (fieldname, val) {
if (fieldname === 'data') {
buffer = val;
}
});
busboy.on('finish', function () {
onSuccess();
});
request.pipe(busboy);
// Otherwise, use our own and just grab flat data from POST body
} else {
request.on('data', function (data) {
buffer += data.toString();
});
request.on('end', function () {
if (cancelled) { return; }
onSuccess();
});
request.on('error', function (error) {
winston.error('connection error: ' + error.message);
response.writeHead(500, { 'content-type': 'application/json' });
response.end(JSON.stringify({ message: 'Connection error.' }));
cancelled = true;
});
}
};
// Keep choosing keys until one isn't taken
@ -98,7 +123,7 @@ DocumentHandler.prototype.chooseKey = function(callback) {
} else {
callback(key);
}
});
}, true); // Don't bump expirations when key searching
};
DocumentHandler.prototype.acceptableKey = function() {

View File

@ -0,0 +1,56 @@
/*global require,module,process*/
var AWS = require('aws-sdk');
var winston = require('winston');
var AmazonS3DocumentStore = function(options) {
this.expire = options.expire;
this.bucket = options.bucket;
this.client = new AWS.S3({region: options.region});
};
AmazonS3DocumentStore.prototype.get = function(key, callback, skipExpire) {
var _this = this;
var req = {
Bucket: _this.bucket,
Key: key
};
_this.client.getObject(req, function(err, data) {
if(err) {
callback(false);
}
else {
callback(data.Body.toString('utf-8'));
if (_this.expire && !skipExpire) {
winston.warn('amazon s3 store cannot set expirations on keys');
}
}
});
}
AmazonS3DocumentStore.prototype.set = function(key, data, callback, skipExpire) {
var _this = this;
var req = {
Bucket: _this.bucket,
Key: key,
Body: data,
ContentType: 'text/plain'
};
_this.client.putObject(req, function(err, data) {
if (err) {
callback(false);
}
else {
callback(true);
if (_this.expire && !skipExpire) {
winston.warn('amazon s3 store cannot set expirations on keys');
}
}
});
}
module.exports = AmazonS3DocumentStore;

View File

@ -1,45 +1,52 @@
var memcached = require('memcache');
var winston = require('winston');
const memcached = require('memcached');
const winston = require('winston');
class MemcachedDocumentStore {
// Create a new store with options
var MemcachedDocumentStore = function(options) {
constructor(options) {
this.expire = options.expire;
if (!MemcachedDocumentStore.client) {
MemcachedDocumentStore.connect(options);
const host = options.host || '127.0.0.1';
const port = options.port || 11211;
const url = `${host}:${port}`;
this.connect(url);
}
};
// Create a connection
MemcachedDocumentStore.connect = function(options) {
var host = options.host || '127.0.0.1';
var port = options.port || 11211;
this.client = new memcached.Client(port, host);
this.client.connect();
this.client.on('connect', function() {
winston.info('connected to memcached on ' + host + ':' + port);
connect(url) {
this.client = new memcached(url);
winston.info(`connecting to memcached on ${url}`);
this.client.on('failure', function(error) {
winston.info('error connecting to memcached', {error});
});
this.client.on('error', function(e) {
winston.info('error connecting to memcached', { error: e });
});
};
}
// Save file in a key
MemcachedDocumentStore.prototype.set =
function(key, data, callback, skipExpire) {
MemcachedDocumentStore.client.set(key, data, function(err, reply) {
err ? callback(false) : callback(true);
}, skipExpire ? 0 : this.expire);
};
set(key, data, callback, skipExpire) {
this.client.set(key, data, skipExpire ? 0 : this.expire, (error) => {
callback(!error);
});
}
// Get a file from a key
MemcachedDocumentStore.prototype.get = function(key, callback, skipExpire) {
var _this = this;
MemcachedDocumentStore.client.get(key, function(err, reply) {
callback(err ? false : reply);
if (_this.expire && !skipExpire) {
winston.warn('store does not currently push forward expirations on GET');
get(key, callback, skipExpire) {
this.client.get(key, (error, data) => {
callback(error ? false : data);
// Update the key so that the expiration is pushed forward
if (!skipExpire) {
this.set(key, data, (updateSucceeded) => {
if (!updateSucceeded) {
winston.error('failed to update expiration on GET', {key});
}
}, skipExpire);
}
});
};
}
}
module.exports = MemcachedDocumentStore;

View File

@ -0,0 +1,79 @@
/*global require,module,process*/
var postgres = require('pg');
var winston = require('winston');
// create table entries (id serial primary key, key varchar(255) not null, value text not null, expiration int, unique(key));
// A postgres document store
var PostgresDocumentStore = function (options) {
this.expireJS = options.expire;
this.connectionUrl = process.env.DATABASE_URL || options.connectionUrl;
};
PostgresDocumentStore.prototype = {
// Set a given key
set: function (key, data, callback, skipExpire) {
var now = Math.floor(new Date().getTime() / 1000);
var that = this;
this.safeConnect(function (err, client, done) {
if (err) { return callback(false); }
client.query('INSERT INTO entries (key, value, expiration) VALUES ($1, $2, $3)', [
key,
data,
that.expireJS && !skipExpire ? that.expireJS + now : null
], function (err) {
if (err) {
winston.error('error persisting value to postgres', { error: err });
return callback(false);
}
callback(true);
done();
});
});
},
// Get a given key's data
get: function (key, callback, skipExpire) {
var now = Math.floor(new Date().getTime() / 1000);
var that = this;
this.safeConnect(function (err, client, done) {
if (err) { return callback(false); }
client.query('SELECT id,value,expiration from entries where KEY = $1 and (expiration IS NULL or expiration > $2)', [key, now], function (err, result) {
if (err) {
winston.error('error retrieving value from postgres', { error: err });
return callback(false);
}
callback(result.rows.length ? result.rows[0].value : false);
if (result.rows.length && that.expireJS && !skipExpire) {
client.query('UPDATE entries SET expiration = $1 WHERE ID = $2', [
that.expireJS + now,
result.rows[0].id
], function (err) {
if (!err) {
done();
}
});
} else {
done();
}
});
});
},
// A connection wrapper
safeConnect: function (callback) {
postgres.connect(this.connectionUrl, function (err, client, done) {
if (err) {
winston.error('error connecting to postgres', { error: err });
callback(err);
} else {
callback(undefined, client, done);
}
});
}
};
module.exports = PostgresDocumentStore;

View File

@ -8,9 +8,13 @@ var winston = require('winston');
// options[db] - The db to use (default 0)
// options[expire] - The time to live for each key set (default never)
var RedisDocumentStore = function(options) {
var RedisDocumentStore = function(options, client) {
this.expire = options.expire;
if (!RedisDocumentStore.client) {
if (client) {
winston.info('using predefined redis client');
RedisDocumentStore.client = client;
} else if (!RedisDocumentStore.client) {
winston.info('configuring redis');
RedisDocumentStore.connect(options);
}
};
@ -21,11 +25,20 @@ RedisDocumentStore.connect = function(options) {
var port = options.port || 6379;
var index = options.db || 0;
RedisDocumentStore.client = redis.createClient(port, host);
RedisDocumentStore.client.select(index, function(err, reply) {
// authenticate if password is provided
if (options.password) {
RedisDocumentStore.client.auth(options.password);
}
RedisDocumentStore.client.on('error', function(err) {
winston.error('redis disconnected', err);
});
RedisDocumentStore.client.select(index, function(err) {
if (err) {
winston.error(
'error connecting to redis index ' + index,
{ error: err.message }
{ error: err }
);
process.exit(1);
}
@ -38,7 +51,7 @@ RedisDocumentStore.connect = function(options) {
// Save file in a key
RedisDocumentStore.prototype.set = function(key, data, callback, skipExpire) {
var _this = this;
RedisDocumentStore.client.set(key, data, function(err, reply) {
RedisDocumentStore.client.set(key, data, function(err) {
if (err) {
callback(false);
}
@ -54,7 +67,7 @@ RedisDocumentStore.prototype.set = function(key, data, callback, skipExpire) {
// Expire a key in expire time if set
RedisDocumentStore.prototype.setExpiration = function(key) {
if (this.expire) {
RedisDocumentStore.client.expire(key, this.expire, function(err, reply) {
RedisDocumentStore.client.expire(key, this.expire, function(err) {
if (err) {
winston.error('failed to set expiry on key: ' + key);
}

View File

@ -0,0 +1,46 @@
const crypto = require('crypto');
const rethink = require('rethinkdbdash');
const winston = require('winston');
const md5 = (str) => {
const md5sum = crypto.createHash('md5');
md5sum.update(str);
return md5sum.digest('hex');
};
class RethinkDBStore {
constructor(options) {
this.client = rethink({
silent: true,
host: options.host || '127.0.0.1',
port: options.port || 28015,
db: options.db || 'haste',
user: options.user || 'admin',
password: options.password || ''
});
}
set(key, data, callback) {
this.client.table('uploads').insert({ id: md5(key), data: data }).run((error) => {
if (error) {
callback(false);
winston.error('failed to insert to table', error);
return;
}
callback(true);
});
}
get(key, callback) {
this.client.table('uploads').get(md5(key)).run((error, result) => {
if (error || !result) {
callback(false);
if (error) winston.error('failed to insert to table', error);
return;
}
callback(result.data);
});
}
}
module.exports = RethinkDBStore;

View File

@ -0,0 +1,32 @@
const fs = require('fs');
module.exports = class DictionaryGenerator {
constructor(options, readyCallback) {
// Check options format
if (!options) throw Error('No options passed to generator');
if (!options.path) throw Error('No dictionary path specified in options');
// Load dictionary
fs.readFile(options.path, 'utf8', (err, data) => {
if (err) throw err;
this.dictionary = data.split(/[\n\r]+/);
if (readyCallback) readyCallback();
});
}
// Generates a dictionary-based key, of keyLength words
createKey(keyLength) {
let text = '';
for (let i = 0; i < keyLength; i++) {
const index = Math.floor(Math.random() * this.dictionary.length);
text += this.dictionary[index];
}
return text;
}
};

View File

@ -1,32 +1,27 @@
// Draws inspiration from pwgen and http://tools.arantius.com/password
var PhoneticKeyGenerator = function(options) {
// No options
const randOf = (collection) => {
return () => {
return collection[Math.floor(Math.random() * collection.length)];
};
};
// Generate a phonetic key
PhoneticKeyGenerator.prototype.createKey = function(keyLength) {
var text = '';
for (var i = 0; i < keyLength; i++) {
text += (i % 2 == 0) ? this.randConsonant() : this.randVowel();
// Helper methods to get an random vowel or consonant
const randVowel = randOf('aeiou');
const randConsonant = randOf('bcdfghjklmnpqrstvwxyz');
module.exports = class PhoneticKeyGenerator {
// Generate a phonetic key of alternating consonant & vowel
createKey(keyLength) {
let text = '';
const start = Math.round(Math.random());
for (let i = 0; i < keyLength; i++) {
text += (i % 2 == start) ? randConsonant() : randVowel();
}
return text;
}
};
PhoneticKeyGenerator.consonants = 'bcdfghjklmnpqrstvwxy';
PhoneticKeyGenerator.vowels = 'aeiou';
// Get an random vowel
PhoneticKeyGenerator.prototype.randVowel = function() {
return PhoneticKeyGenerator.vowels[
Math.floor(Math.random() * PhoneticKeyGenerator.vowels.length)
];
};
// Get an random consonant
PhoneticKeyGenerator.prototype.randConsonant = function() {
return PhoneticKeyGenerator.consonants[
Math.floor(Math.random() * PhoneticKeyGenerator.consonants.length)
];
};
module.exports = PhoneticKeyGenerator;

View File

@ -1,19 +1,20 @@
var RandomKeyGenerator = function(options) {
if (!options) {
options = {};
}
this.keyspace = options.keyspace || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
};
module.exports = class RandomKeyGenerator {
// Generate a random key
RandomKeyGenerator.prototype.createKey = function(keyLength) {
// Initialize a new generator with the given keySpace
constructor(options = {}) {
this.keyspace = options.keyspace || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
}
// Generate a key of the given length
createKey(keyLength) {
var text = '';
var index;
for (var i = 0; i < keyLength; i++) {
index = Math.floor(Math.random() * this.keyspace.length);
const index = Math.floor(Math.random() * this.keyspace.length);
text += this.keyspace.charAt(index);
}
return text;
};
module.exports = RandomKeyGenerator;
return text;
}
};

548
package-lock.json generated Normal file
View File

@ -0,0 +1,548 @@
{
"name": "haste",
"version": "0.1.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"async": {
"version": "0.1.22",
"resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz",
"integrity": "sha1-D8GqoIig4+8Ovi2IMbqw3PiEUGE="
},
"async-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/async-cache/-/async-cache-1.0.0.tgz",
"integrity": "sha1-yH9tgMcrOU7g+QYe3rJNjEtiKto=",
"requires": {
"lru-cache": "2.3.1"
}
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true
},
"bl": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/bl/-/bl-1.0.3.tgz",
"integrity": "sha1-/FQhoo/UImA2w7OJGmaiW8ZNIm4=",
"requires": {
"readable-stream": "2.0.6"
},
"dependencies": {
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"readable-stream": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
"integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=",
"requires": {
"core-util-is": "1.0.2",
"inherits": "2.0.3",
"isarray": "1.0.0",
"process-nextick-args": "1.0.7",
"string_decoder": "0.10.31",
"util-deprecate": "1.0.2"
}
}
}
},
"brace-expansion": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
"integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
"dev": true,
"requires": {
"balanced-match": "1.0.0",
"concat-map": "0.0.1"
}
},
"browser-stdout": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz",
"integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=",
"dev": true
},
"buffer-writer": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-1.0.0.tgz",
"integrity": "sha1-bCnDst6gyeRVofJhoZmkigT4iwg="
},
"busboy": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.4.tgz",
"integrity": "sha1-GXfpbh7ohGSWUevfVIypAHWLp/M=",
"requires": {
"dicer": "0.2.3",
"readable-stream": "1.1.14"
}
},
"colors": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz",
"integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w="
},
"commander": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
"integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=",
"dev": true,
"requires": {
"graceful-readlink": "1.0.1"
}
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
},
"connect": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/connect/-/connect-3.4.1.tgz",
"integrity": "sha1-ohNh0/QJnvdhzabcSpc7seuwo00=",
"requires": {
"debug": "2.2.0",
"finalhandler": "0.4.1",
"parseurl": "1.3.1",
"utils-merge": "1.0.0"
}
},
"connect-ratelimit": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/connect-ratelimit/-/connect-ratelimit-0.0.7.tgz",
"integrity": "sha1-5uCclQZJ6ElJnKsYcKQVoH9zFWg="
},
"connect-route": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/connect-route/-/connect-route-0.1.5.tgz",
"integrity": "sha1-48IYMZ0uiKiprgsOD+Cacpw5dEo="
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"cycle": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz",
"integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI="
},
"debug": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
"integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
"requires": {
"ms": "0.7.1"
}
},
"dicer": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.3.tgz",
"integrity": "sha1-8AKBGJpVwjUe+ASQpP6fssWcSTk=",
"requires": {
"readable-stream": "1.1.14",
"streamsearch": "0.1.2"
}
},
"diff": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz",
"integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=",
"dev": true
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true
},
"eyes": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
"integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A="
},
"fd": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/fd/-/fd-0.0.2.tgz",
"integrity": "sha1-4O2yvXqIzIbdnxY5HLqDJBj9h+4="
},
"finalhandler": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.4.1.tgz",
"integrity": "sha1-haF8bFmpRxfSYtYSMNSw6+PUoU0=",
"requires": {
"debug": "2.2.0",
"escape-html": "1.0.3",
"on-finished": "2.3.0",
"unpipe": "1.0.0"
}
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true
},
"generic-pool": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.1.1.tgz",
"integrity": "sha1-rwTcLDJc/Ll1Aj+lK/zpYXp0Nf0="
},
"glob": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz",
"integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=",
"dev": true,
"requires": {
"fs.realpath": "1.0.0",
"inflight": "1.0.6",
"inherits": "2.0.3",
"minimatch": "3.0.4",
"once": "1.4.0",
"path-is-absolute": "1.0.1"
}
},
"graceful-fs": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
"optional": true
},
"graceful-readlink": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
"integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=",
"dev": true
},
"growl": {
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz",
"integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=",
"dev": true
},
"has-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
"integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
"dev": true
},
"he": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
"integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
"dev": true
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"dev": true,
"requires": {
"once": "1.4.0",
"wrappy": "1.0.2"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
},
"lru-cache": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.3.1.tgz",
"integrity": "sha1-s632s9hW6VTiw5DmzvIggSRaU9Y="
},
"mime": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.3.6.tgz",
"integrity": "sha1-WR2E02U6awtKO5343lqoEI5y5eA="
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"requires": {
"brace-expansion": "1.1.8"
}
},
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true,
"requires": {
"minimist": "0.0.8"
}
},
"mocha": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-4.0.1.tgz",
"integrity": "sha512-evDmhkoA+cBNiQQQdSKZa2b9+W2mpLoj50367lhy+Klnx9OV8XlCIhigUnn1gaTFLQCa0kdNhEGDr0hCXOQFDw==",
"dev": true,
"requires": {
"browser-stdout": "1.3.0",
"commander": "2.9.0",
"debug": "2.2.0",
"diff": "3.2.0",
"escape-string-regexp": "1.0.5",
"glob": "7.1.1",
"growl": "1.9.2",
"he": "1.1.1",
"mkdirp": "0.5.1",
"supports-color": "3.1.2"
}
},
"ms": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
"integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg="
},
"negotiator": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
"integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"requires": {
"ee-first": "1.1.1"
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
"requires": {
"wrappy": "1.0.2"
}
},
"packet-reader": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-0.2.0.tgz",
"integrity": "sha1-gZ300BC4LV6lZx+KGjrPA5vNdwA="
},
"parseurl": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz",
"integrity": "sha1-yKuMkiO6NIiKpkopeyiFO+wY2lY="
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true
},
"pg": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/pg/-/pg-4.1.1.tgz",
"integrity": "sha1-mEgKz8089qP5Yhyl1FiUFVgqVzI=",
"requires": {
"buffer-writer": "1.0.0",
"generic-pool": "2.1.1",
"packet-reader": "0.2.0",
"pg-connection-string": "0.1.3",
"pg-types": "1.6.0",
"pgpass": "0.0.3",
"semver": "4.3.6"
}
},
"pg-connection-string": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-0.1.3.tgz",
"integrity": "sha1-2hhHsglA5C7hSSvq9l1J2RskXfc="
},
"pg-types": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-1.6.0.tgz",
"integrity": "sha1-OHKg8ZkUMCVJf07ipl/a8A1+qLM="
},
"pgpass": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-0.0.3.tgz",
"integrity": "sha1-EuZ+NDsxicLzEgbrycwL7//PkUA=",
"requires": {
"split": "0.3.3"
}
},
"pkginfo": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.2.3.tgz",
"integrity": "sha1-cjnEKl72wwuPMoQ52bn/cQQkkPg="
},
"process-nextick-args": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
"integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
},
"readable-stream": {
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
"requires": {
"core-util-is": "1.0.2",
"inherits": "2.0.3",
"isarray": "0.0.1",
"string_decoder": "0.10.31"
}
},
"redis": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/redis/-/redis-0.8.1.tgz",
"integrity": "sha1-FZ8hMFmaL3GeRLA/C0t2EvmS/LI="
},
"redis-url": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/redis-url/-/redis-url-0.1.0.tgz",
"integrity": "sha1-TaXlsYG2wMrW4aVcf1Co5u53ebs=",
"requires": {
"redis": "0.8.1"
}
},
"request": {
"version": "2.9.203",
"resolved": "https://registry.npmjs.org/request/-/request-2.9.203.tgz",
"integrity": "sha1-bBcRpUB/uUoRQhlWPkQUW8v0cjo="
},
"semver": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz",
"integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto="
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"split": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz",
"integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=",
"requires": {
"through": "2.3.8"
}
},
"st": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/st/-/st-1.1.0.tgz",
"integrity": "sha1-c7ltsLdkTZp4zjg0o+T37G6Hz3Y=",
"requires": {
"async-cache": "1.0.0",
"bl": "1.0.3",
"fd": "0.0.2",
"graceful-fs": "4.1.11",
"mime": "1.3.6",
"negotiator": "0.6.1"
}
},
"stack-trace": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
"integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA="
},
"streamsearch": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
"integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
},
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
},
"supports-color": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz",
"integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=",
"dev": true,
"requires": {
"has-flag": "1.0.0"
}
},
"through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
},
"uglify-js": {
"version": "3.1.6",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.1.6.tgz",
"integrity": "sha512-/rseyxEKEVMBo8279lqpoJgD6C/i/CIi+9TJDvWmb+Xo6mqMKwjA8Io3IMHlcXQzj99feR6zrN8m3wqqvm/nYA==",
"requires": {
"commander": "2.11.0",
"source-map": "0.6.1"
},
"dependencies": {
"commander": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz",
"integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ=="
}
}
},
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"utils-merge": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz",
"integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg="
},
"winston": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/winston/-/winston-0.6.2.tgz",
"integrity": "sha1-QUT+JYbNwZphK/jANVkBMskGS9I=",
"requires": {
"async": "0.1.22",
"colors": "0.6.2",
"cycle": "1.0.3",
"eyes": "0.1.8",
"pkginfo": "0.2.3",
"request": "2.9.203",
"stack-trace": "0.0.10"
}
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
}
}
}

View File

@ -1,52 +1,51 @@
{
"name": "haste",
"version": "0.0.1",
"version": "0.1.0",
"private": true,
"description": "Private Paste",
"keywords": [ "paste", "pastebin" ],
"description": "Private Pastebin Server",
"keywords": [
"paste",
"pastebin"
],
"author": {
"name": "John Crepezzi",
"email": "john.crepezzi@gmail.com",
"url": "http://seejohncode.com/"
},
"main": "haste",
"dependencies": {
"winston": "*",
"connect": "< 2",
"uglify-js": "*"
"connect-ratelimit": "0.0.7",
"connect-route": "0.1.5",
"connect": "3.4.1",
"st": "1.1.0",
"winston": "0.6.2",
"redis-url": "0.1.0",
"redis": "0.8.1",
"uglify-js": "3.1.6",
"busboy": "0.2.4",
"pg": "4.1.1"
},
"devDependencies": {
"mocha": "*",
"should": "*"
"mocha": "^4.0.1"
},
"bundledDependencies": [],
"engines": {
"node": "*"
"node": "8.1.4",
"npm": "5.2.0"
},
"bin": {
"haste-server": "./server.js"
},
"files": [ "server.js", "lib", "static" ],
"files": [
"server.js",
"lib",
"static"
],
"directories": {
"lib": "./lib"
},
"scripts": {
"start": "node server.js",
"test": "mocha -r should spec/*"
"test": "mocha --recursive"
}
}

133
server.js
View File

@ -1,22 +1,28 @@
var http = require('http');
var url = require('url');
var fs = require('fs');
var uglify = require('uglify-js');
var winston = require('winston');
var connect = require('connect');
var route = require('connect-route');
var connect_st = require('st');
var connect_rate_limit = require('connect-ratelimit');
var DocumentHandler = require('./lib/document_handler');
// Load the configuration and set some defaults
var config = JSON.parse(fs.readFileSync('config.js', 'utf8'));
config.port = config.port || 7777;
config.host = config.host || 'localhost';
var config = JSON.parse(fs.readFileSync('./config.js', 'utf8'));
config.port = process.env.PORT || config.port || 7777;
config.host = process.env.HOST || config.host || 'localhost';
// Set up the logger
if (config.logging) {
try {
winston.remove(winston.transports.Console);
} catch(er) { }
} catch(e) {
/* was not present */
}
var detail, type;
for (var i = 0; i < config.logging.length; i++) {
detail = config.logging[i];
@ -34,47 +40,48 @@ if (!config.storage) {
if (!config.storage.type) {
config.storage.type = 'file';
}
var Store = require('./lib/document_stores/' + config.storage.type);
var preferredStore = new Store(config.storage);
var Store, preferredStore;
if (process.env.REDISTOGO_URL && config.storage.type === 'redis') {
var redisClient = require('redis-url').connect(process.env.REDISTOGO_URL);
Store = require('./lib/document_stores/redis');
preferredStore = new Store(config.storage, redisClient);
}
else {
Store = require('./lib/document_stores/' + config.storage.type);
preferredStore = new Store(config.storage);
}
// Compress the static javascript assets
if (config.recompressStaticAssets) {
var jsp = require("uglify-js").parser;
var pro = require("uglify-js").uglify;
var list = fs.readdirSync('./static');
for (var i = 0; i < list.length; i++) {
var item = list[i];
var orig_code, ast;
if ((item.indexOf('.js') === item.length - 3) &&
(item.indexOf('.min.js') === -1)) {
dest = item.substring(0, item.length - 3) + '.min' +
item.substring(item.length - 3);
orig_code = fs.readFileSync('./static/' + item, 'utf8');
ast = jsp.parse(orig_code);
ast = pro.ast_mangle(ast);
ast = pro.ast_squeeze(ast);
fs.writeFileSync('./static/' + dest, pro.gen_code(ast), 'utf8');
for (var j = 0; j < list.length; j++) {
var item = list[j];
if ((item.indexOf('.js') === item.length - 3) && (item.indexOf('.min.js') === -1)) {
var dest = item.substring(0, item.length - 3) + '.min' + item.substring(item.length - 3);
var orig_code = fs.readFileSync('./static/' + item, 'utf8');
fs.writeFileSync('./static/' + dest, uglify.minify(orig_code).code, 'utf8');
winston.info('compressed ' + item + ' into ' + dest);
}
}
}
// Send the static documents into the preferred store, skipping expirations
var path, data;
for (var name in config.documents) {
var path = config.documents[name];
fs.readFile(path, 'utf8', function(err, data) {
if (data && !err) {
path = config.documents[name];
data = fs.readFileSync(path, 'utf8');
winston.info('loading static document', { name: name, path: path });
if (data) {
preferredStore.set(name, data, function(cb) {
winston.info('loaded static document', { name: name, path: path });
winston.debug('loaded static document', { success: cb });
}, true);
}
else {
winston.warn(
'failed to load static document',
{ name: name, path: path }
);
winston.warn('failed to load static document', { name: name, path: path });
}
});
}
// Pick up a key generator
@ -91,42 +98,58 @@ var documentHandler = new DocumentHandler({
keyGenerator: keyGenerator
});
// Set the server up with a static cache
connect.createServer(
// First look for api calls
connect.router(function(app) {
var app = connect();
// Rate limit all requests
if (config.rateLimits) {
config.rateLimits.end = true;
app.use(connect_rate_limit(config.rateLimits));
}
// first look at API calls
app.use(route(function(router) {
// get raw documents - support getting with extension
app.get('/raw/:id', function(request, response, next) {
var skipExpire = !!config.documents[request.params.id];
router.get('/raw/:id', function(request, response) {
var key = request.params.id.split('.')[0];
var skipExpire = !!config.documents[key];
return documentHandler.handleRawGet(key, response, skipExpire);
});
// add documents
app.post('/documents', function(request, response, next) {
router.post('/documents', function(request, response) {
return documentHandler.handlePost(request, response);
});
// get documents
app.get('/documents/:id', function(request, response, next) {
var skipExpire = !!config.documents[request.params.id];
return documentHandler.handleGet(
request.params.id,
response,
skipExpire
);
router.get('/documents/:id', function(request, response) {
var key = request.params.id.split('.')[0];
var skipExpire = !!config.documents[key];
return documentHandler.handleGet(key, response, skipExpire);
});
}),
// Otherwise, static
connect.staticCache(),
connect.static(__dirname + '/static', { maxAge: config.staticMaxAge }),
}));
// Otherwise, try to match static files
app.use(connect_st({
path: __dirname + '/static',
content: { maxAge: config.staticMaxAge },
passthrough: true,
index: false
}));
// Then we can loop back - and everything else should be a token,
// so route it back to /index.html
connect.router(function(app) {
app.get('/:id', function(request, response, next) {
request.url = request.originalUrl = '/index.html';
// so route it back to /
app.use(route(function(router) {
router.get('/:id', function(request, response, next) {
request.sturl = '/';
next();
});
}),
connect.static(__dirname + '/static', { maxAge: config.staticMaxAge })
).listen(config.port, config.host);
}));
// And match index
app.use(connect_st({
path: __dirname + '/static',
content: { maxAge: config.staticMaxAge },
index: 'index.html'
}));
http.createServer(app).listen(config.port, config.host);
winston.info('listening on ' + config.host + ':' + config.port);

View File

@ -1,201 +1,171 @@
html, body, div, pre, textarea, header, h1, a, nav, ul, li {
margin: 0;
padding: 0;
}
body {
font: 13px monospace;
background: #002B36;
padding: 20px 50px;
margin: 0px;
}
header {
/* textarea */
textarea {
background: transparent;
border: 0px;
color: #fff;
padding: 0px;
width: 100%;
height: 100%;
font-family: monospace;
outline: none;
resize: none;
font-size: 13px;
}
/* the line numbers */
#linenos {
color: #7d7d7d;
z-index: -1000;
position: absolute;
top: 20px;
left: 0px;
width: 30px; /* 30 to get 20 away from box */
font-size: 13px;
font-family: monospace;
text-align: right;
}
/* code box when locked */
#box {
padding: 0px;
margin: 0px;
width: 100%;
border: 0px;
outline: none;
font-size: 13px;
overflow: inherit;
}
#box code {
padding: 0px;
background: transparent !important; /* don't hide hastebox */
}
/* key */
#key {
position: fixed;
top: 0;
right: 0;
z-index: 1000;
top: 0px;
right: 0px;
z-index: +1000; /* watch out */
}
header h1 {
#box1 {
padding: 5px;
text-align: center;
background: #00222b;
padding: 5px 22px;
}
header h1 a {
background: transparent url('logo.png') no-repeat top center;
display: block;
overflow: hidden;
text-indent: -9999px;
#box2 {
background: #08323c;
font-size: 0px;
padding: 0px 5px;
}
#box1 a.logo, #box1 a.logo:visited {
display: inline-block;
background: url(logo.png);
width: 126px;
height: 42px;
}
header h1 a:hover {
background-position: bottom center;
#box1 a.logo:hover {
background-position: 0 bottom;
}
header ul {
background: #08323c;
font-size: 0;
list-style: none;
/*overflow: hidden;*/
text-align: center;
}
header ul li {
display: inline-block;
position: relative;
}
header ul li .pointer {
background: transparent url('hover-dropdown-tip.png') no-repeat;
display: inline-block;
text-align: center;
width: 10px;
height: 5px;
}
header ul li a {
background: transparent url('function-icons.png');
display: block;
overflow: hidden;
text-indent: -9999px;
#box2 .function {
background: url(function-icons.png);
width: 32px;
height: 37px;
}
header ul li a.disabled {
cursor: default;
}
header li a.save { background-position: -5px center; }
header li a.save:hover { background-position: -5px bottom; }
header li a.save.disabled { background-position: -5px top; }
header li a.new { background-position: -42px center; }
header li a.new:hover { background-position: -42px bottom; }
header li a.new.disabled { background-position: -42px top; }
header li a.edit { background-position: -79px center; }
header li a.edit:hover { background-position: -79px bottom; }
header li a.edit.disabled { background-position: -79px top; }
header li a.raw { background-position: -116px center; }
header li a.raw:hover { background-position: -116px bottom; }
header li a.raw.disabled { background-position: -116px top; }
header li a.twitter { background-position: -153px center; }
header li a.twitter:hover { background-position: -153px bottom; }
header li a.twitter.disabled { background-position: -153px top; }
#editor {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.CodeMirror {
line-height: 1em;
height: 100%;
}
.CodeMirror-scroll {
height: 100%;
overflow: auto;
display: inline-block;
position: relative;
}
.CodeMirror-gutter {
height: 100%;
min-width: 2em;
#box2 .link embed {
vertical-align: bottom; /* fix for zeroClipboard style */
}
#box2 .function.enabled:hover {
cursor: hand;
cursor: pointer;
}
#pointer {
display: block;
height: 5px;
width: 10px;
background: url(hover-dropdown-tip.png);
bottom: 0px;
position: absolute;
top: 0;
left: 0;
margin: auto;
left: 0px;
right: 0px;
}
.CodeMirror-gutter-text {
text-align: right;
padding: 0.4em 0.2em 0.4em 0.4em;
white-space: pre;
#box3, #messages li {
background: #173e48;
font-family: Helvetica, sans-serif;
font-size: 12px;
line-height: 14px;
padding: 10px 15px;
}
.CodeMirror-lines {
padding: 0.4em;
}
.CodeMirror textarea {
outline: 0;
}
.CodeMirror pre.CodeMirror-cursor {
position: absolute;
visibility: hidden;
z-index: 10;
}
.CodeMirror-focused pre.CodeMirror-cursor {
visibility: visible;
}
span.cm-header, span.cm-strong {
#box3 .label, #messages li {
color: #fff;
font-weight: bold;
}
span.cm-em {
font-style: italic;
#box3 .shortcut {
color: #c4dce3;
font-weight: normal;
}
span.cm-emstrong {
font-style: italic; font-weight: bold;
#box2 .function.save { background-position: -5px top; }
#box2 .function.enabled.save { background-position: -5px center; }
#box2 .function.enabled.save:hover { background-position: -5px bottom; }
#box2 .function.new { background-position: -42px top; }
#box2 .function.enabled.new { background-position: -42px center; }
#box2 .function.enabled.new:hover { background-position: -42px bottom; }
#box2 .function.duplicate { background-position: -79px top; }
#box2 .function.enabled.duplicate { background-position: -79px center; }
#box2 .function.enabled.duplicate:hover { background-position: -79px bottom; }
#box2 .function.raw { background-position: -116px top; }
#box2 .function.enabled.raw { background-position: -116px center; }
#box2 .function.enabled.raw:hover { background-position: -116px bottom; }
#box2 .function.twitter { background-position: -153px top; }
#box2 .function.enabled.twitter { background-position: -153px center; }
#box2 .function.enabled.twitter:hover { background-position: -153px bottom; }
#box2 .button-picture{ border-width: 0; font-size: inherit; }
#messages {
position:fixed;
top:0px;
right:138px;
margin:0;
padding:0;
width:400px;
}
span.cm-link {
text-decoration: underline;
#messages li {
background:rgba(23,62,72,0.8);
margin:0 auto;
list-style:none;
}
/* Solarized (dark) theme */
.cm-s-solarized-dark {
background: #002b36;
color: #839496;
#messages li.error {
background:rgba(102,8,0,0.8);
}
.cm-s-solarized-dark div.CodeMirror-selected {
background: #586e75;
}
.cm-s-solarized-dark .CodeMirror-gutter {
background: #073642;
}
.cm-s-solarized-dark .CodeMirror-gutter-text {
color: #586e75;
}
.cm-s-solarized-dark .CodeMirror-cursor {
border-left: 1px solid #839496;
}
.cm-s-solarized-dark span.cm-keyword { color: #268bd2; }
.cm-s-solarized-dark span.cm-atom { color: #b58900; }
.cm-s-solarized-dark span.cm-number { color: #2aa198; }
.cm-s-solarized-dark span.cm-def { color: #839496; }
.cm-s-solarized-dark span.cm-variable { color: #839496; }
.cm-s-solarized-dark span.cm-variable-2 { color: #b58900; }
.cm-s-solarized-dark span.cm-variable-3 { color: #268bd2; }
.cm-s-solarized-dark span.cm-property { color: #859900; }
.cm-s-solarized-dark span.cm-operator { color: #2aa198; }
.cm-s-solarized-dark span.cm-comment { color: #586e75; }
.cm-s-solarized-dark span.cm-string { color: #2aa198; }
.cm-s-solarized-dark span.cm-string-2 { color: #2aa198; }
.cm-s-solarized-dark span.cm-meta { color: #586e75; }
.cm-s-solarized-dark span.cm-error { color: #dc322f; }
.cm-s-solarized-dark span.cm-qualifier { color: #268bd2; }
.cm-s-solarized-dark span.cm-builtin { color: #b58900; }
.cm-s-solarized-dark span.cm-bracket { color: #dc322f; }
.cm-s-solarized-dark span.cm-tag { color: #268bd2; }
.cm-s-solarized-dark span.cm-attribute { color: #839496; }
.cm-s-solarized-dark span.cm-header { color: #cb4b16; }
.cm-s-solarized-dark span.cm-quote { color: #586e75; }
.cm-s-solarized-dark span.cm-hr { color: #cb4b16; }
.cm-s-solarized-dark span.cm-link { color: #6c71c4; }

View File

@ -1,171 +1,399 @@
window.Haste = {
Models: {},
Views: {},
Routers: {},
/* global $, hljs, window, document */
extensionMap: {
clj: 'clojure', coffee: 'coffeescript', css: 'css', diff: 'diff', go: 'go',
hs: 'haskell', html: 'htmlmixed', js: 'javascript', lua: 'lua',
md: 'markdown', markdown: 'markdown', sql: 'mysql', pl: 'perl', php: 'php',
py: 'python', r: 'r', rb: 'ruby', scm: 'scheme', xml: 'xml', yml: 'yaml'
///// represents a single document
var haste_document = function() {
this.locked = false;
};
// Escapes HTML tag characters
haste_document.prototype.htmlEscape = function(s) {
return s
.replace(/&/g, '&amp;')
.replace(/>/g, '&gt;')
.replace(/</g, '&lt;')
.replace(/"/g, '&quot;');
};
// Get this document from the server and lock it here
haste_document.prototype.load = function(key, callback, lang) {
var _this = this;
$.ajax('/documents/' + key, {
type: 'get',
dataType: 'json',
success: function(res) {
_this.locked = true;
_this.key = key;
_this.data = res.data;
try {
var high;
if (lang === 'txt') {
high = { value: _this.htmlEscape(res.data) };
}
else if (lang) {
high = hljs.highlight(lang, res.data);
}
else {
high = hljs.highlightAuto(res.data);
}
} catch(err) {
// failed highlight, fall back on auto
high = hljs.highlightAuto(res.data);
}
callback({
value: high.value,
key: key,
language: high.language || lang,
lineCount: res.data.split('\n').length
});
},
error: function() {
callback(false);
}
});
};
init: function() {
new Haste.Routers.Document();
Backbone.history.start({ pushState: true });
// Save this document to the server and lock it here
haste_document.prototype.save = function(data, callback) {
if (this.locked) {
return false;
}
this.data = data;
var _this = this;
$.ajax('/documents', {
type: 'post',
data: data,
dataType: 'json',
contentType: 'application/json; charset=utf-8',
success: function(res) {
_this.locked = true;
_this.key = res.key;
var high = hljs.highlightAuto(data);
callback(null, {
value: high.value,
key: res.key,
language: high.language,
lineCount: data.split('\n').length
});
},
error: function(res) {
try {
callback($.parseJSON(res.responseText));
}
catch (e) {
callback({message: 'Something went wrong!'});
}
}
});
};
///// represents the paste application
var haste = function(appName, options) {
this.appName = appName;
this.$textarea = $('textarea');
this.$box = $('#box');
this.$code = $('#box code');
this.$linenos = $('#linenos');
this.options = options;
this.configureShortcuts();
this.configureButtons();
// If twitter is disabled, hide the button
if (!options.twitter) {
$('#box2 .twitter').hide();
}
};
Haste.Models.Document = Backbone.Model.extend({
idAttribute: 'key',
urlRoot: '/documents'
// Set the page title - include the appName
haste.prototype.setTitle = function(ext) {
var title = ext ? this.appName + ' - ' + ext : this.appName;
document.title = title;
};
// Show a message box
haste.prototype.showMessage = function(msg, cls) {
var msgBox = $('<li class="'+(cls || 'info')+'">'+msg+'</li>');
$('#messages').prepend(msgBox);
setTimeout(function() {
msgBox.slideUp('fast', function() { $(this).remove(); });
}, 3000);
};
// Show the light key
haste.prototype.lightKey = function() {
this.configureKey(['new', 'save']);
};
// Show the full key
haste.prototype.fullKey = function() {
this.configureKey(['new', 'duplicate', 'twitter', 'raw']);
};
// Set the key up for certain things to be enabled
haste.prototype.configureKey = function(enable) {
var $this, i = 0;
$('#box2 .function').each(function() {
$this = $(this);
for (i = 0; i < enable.length; i++) {
if ($this.hasClass(enable[i])) {
$this.addClass('enabled');
return true;
}
}
$this.removeClass('enabled');
});
};
Haste.Routers.Document = Backbone.Router.extend({
routes: {
':id.:extension': 'show',
':id': 'show',
'': 'new'
},
// Remove the current document (if there is one)
// and set up for a new one
haste.prototype.newDocument = function(hideHistory) {
this.$box.hide();
this.doc = new haste_document();
if (!hideHistory) {
window.history.pushState(null, this.appName, '/');
}
this.setTitle();
this.lightKey();
this.$textarea.val('').show('fast', function() {
this.focus();
});
this.removeLineNumbers();
};
initialize: function() {
this.editor = new Haste.Views.EditorView();
},
// Map of common extensions
// Note: this list does not need to include anything that IS its extension,
// due to the behavior of lookupTypeByExtension and lookupExtensionByType
// Note: optimized for lookupTypeByExtension
haste.extensionMap = {
rb: 'ruby', py: 'python', pl: 'perl', php: 'php', scala: 'scala', go: 'go',
xml: 'xml', html: 'xml', htm: 'xml', css: 'css', js: 'javascript', vbs: 'vbscript',
lua: 'lua', pas: 'delphi', java: 'java', cpp: 'cpp', cc: 'cpp', m: 'objectivec',
vala: 'vala', sql: 'sql', sm: 'smalltalk', lisp: 'lisp', ini: 'ini',
diff: 'diff', bash: 'bash', sh: 'bash', tex: 'tex', erl: 'erlang', hs: 'haskell',
md: 'markdown', txt: '', coffee: 'coffee', json: 'javascript',
swift: 'swift'
};
show: function(id, extension) {
this.editor.load(id, extension);
},
// Look up the extension preferred for a type
// If not found, return the type itself - which we'll place as the extension
haste.prototype.lookupExtensionByType = function(type) {
for (var key in haste.extensionMap) {
if (haste.extensionMap[key] === type) return key;
}
return type;
};
new: function() {
this.editor.new();
// Look up the type for a given extension
// If not found, return the extension - which we'll attempt to use as the type
haste.prototype.lookupTypeByExtension = function(ext) {
return haste.extensionMap[ext] || ext;
};
// Add line numbers to the document
// For the specified number of lines
haste.prototype.addLineNumbers = function(lineCount) {
var h = '';
for (var i = 0; i < lineCount; i++) {
h += (i + 1).toString() + '<br/>';
}
$('#linenos').html(h);
};
// Remove the line numbers
haste.prototype.removeLineNumbers = function() {
$('#linenos').html('&gt;');
};
// Load a document and show it
haste.prototype.loadDocument = function(key) {
// Split the key up
var parts = key.split('.', 2);
// Ask for what we want
var _this = this;
_this.doc = new haste_document();
_this.doc.load(parts[0], function(ret) {
if (ret) {
_this.$code.html(ret.value);
_this.setTitle(ret.key);
_this.fullKey();
_this.$textarea.val('').hide();
_this.$box.show().focus();
_this.addLineNumbers(ret.lineCount);
}
else {
_this.newDocument();
}
}, this.lookupTypeByExtension(parts[1]));
};
// Duplicate the current document - only if locked
haste.prototype.duplicateDocument = function() {
if (this.doc.locked) {
var currentData = this.doc.data;
this.newDocument();
this.$textarea.val(currentData);
}
};
// Lock the current document
haste.prototype.lockDocument = function() {
var _this = this;
this.doc.save(this.$textarea.val(), function(err, ret) {
if (err) {
_this.showMessage(err.message, 'error');
}
else if (ret) {
_this.$code.html(ret.value);
_this.setTitle(ret.key);
var file = '/' + ret.key;
if (ret.language) {
file += '.' + _this.lookupExtensionByType(ret.language);
}
window.history.pushState(null, _this.appName + '-' + ret.key, file);
_this.fullKey();
_this.$textarea.val('').hide();
_this.$box.show().focus();
_this.addLineNumbers(ret.lineCount);
}
});
};
Haste.Views.ActionsView = Backbone.View.extend({
el: 'header',
events: {
'click .new': 'new',
'click .save': 'save',
'click .edit': 'edit',
'click .raw': 'raw',
'click .twitter': 'raw'
haste.prototype.configureButtons = function() {
var _this = this;
this.buttons = [
{
$where: $('#box2 .save'),
label: 'Save',
shortcutDescription: 'control + s',
shortcut: function(evt) {
return evt.ctrlKey && (evt.keyCode === 83);
},
initialize: function() {
this.parent = this.options.parent;
},
toggleActions: function() {
var klass = 'disabled';
if (this.parent.model.isNew()) {
$('.save', this.el).removeClass(klass);
$('.edit, .raw, .twitter', this.el).addClass(klass);
} else {
$('.save', this.el).addClass(klass);
$('.edit, .raw, .twitter', this.el).removeClass(klass);
action: function() {
if (_this.$textarea.val().replace(/^\s+|\s+$/g, '') !== '') {
_this.lockDocument();
}
this.setLink('.raw', 'raw/' + this.parent.model.id);
this.setLink('.twitter', 'https://twitter.com/share?url=' + encodeURI(window.location.href));
},
setLink: function(el, href) {
if (this.parent.model.isNew()) {
href = '#';
}
$(el, this.el).attr('href', href);
},
new: function(event) {
event.preventDefault();
this.parent.new();
Backbone.history.navigate('');
},
save: function(event) {
event.preventDefault();
if (!this.parent.model.isNew()) { return; }
this.parent.save();
},
edit: function(event) {
event.preventDefault();
if (this.parent.model.isNew()) { return; }
this.parent.model.set('key', null);
Backbone.history.navigate('/');
},
raw: function(event) {
if (this.model.isNew()) {
event.preventDefault();
}
},
});
Haste.Views.EditorView = Backbone.View.extend({
el: 'textarea',
initialize: function() {
this.codeMirror = CodeMirror.fromTextArea(this.el, {
mode: 'null',
lineNumbers: true,
theme: 'solarized-dark'
});
this.actionsView = new Haste.Views.ActionsView({ parent: this });
{
$where: $('#box2 .new'),
label: 'New',
shortcut: function(evt) {
return evt.ctrlKey && evt.keyCode === 78;
},
render: function() {
this.codeMirror.setOption('mode', this.model.get('mode') || 'null');
this.codeMirror.setValue(this.model.get('data') || '');
return this;
shortcutDescription: 'control + n',
action: function() {
_this.newDocument(!_this.doc.key);
}
},
new: function() {
this.model = new Haste.Models.Document();
this.model.on('change', this.render, this);
this.model.on('change', this.toggleLock, this);
this.model.on('change', this.actionsView.toggleActions, this.actionsView);
this.model.trigger('change');
{
$where: $('#box2 .duplicate'),
label: 'Duplicate & Edit',
shortcut: function(evt) {
return _this.doc.locked && evt.ctrlKey && evt.keyCode === 68;
},
load: function(key, extension) {
this.new();
var mode = Haste.extensionMap[extension];
this.model.set({ key: key, mode: mode }, { silent: true });
this.model.fetch();
shortcutDescription: 'control + d',
action: function() {
_this.duplicateDocument();
}
},
{
$where: $('#box2 .raw'),
label: 'Just Text',
shortcut: function(evt) {
return evt.ctrlKey && evt.shiftKey && evt.keyCode === 82;
},
shortcutDescription: 'control + shift + r',
action: function() {
window.location.href = '/raw/' + _this.doc.key;
}
},
{
$where: $('#box2 .twitter'),
label: 'Twitter',
shortcut: function(evt) {
return _this.options.twitter && _this.doc.locked && evt.shiftKey && evt.ctrlKey && evt.keyCode == 84;
},
shortcutDescription: 'control + shift + t',
action: function() {
window.open('https://twitter.com/share?url=' + encodeURI(window.location.href));
}
}
];
for (var i = 0; i < this.buttons.length; i++) {
this.configureButton(this.buttons[i]);
}
};
save: function() {
var data = this.codeMirror.getValue();
if (!data) { return; }
this.model.save('data', data, {
success: function(model, response) {
Backbone.history.navigate(model.id);
haste.prototype.configureButton = function(options) {
// Handle the click action
options.$where.click(function(evt) {
evt.preventDefault();
if (!options.clickDisabled && $(this).hasClass('enabled')) {
options.action();
}
});
},
// Show the label
options.$where.mouseenter(function() {
$('#box3 .label').text(options.label);
$('#box3 .shortcut').text(options.shortcutDescription || '');
$('#box3').show();
$(this).append($('#pointer').remove().show());
});
// Hide the label
options.$where.mouseleave(function() {
$('#box3').hide();
$('#pointer').hide();
});
};
toggleLock: function() {
this.codeMirror.setOption('readOnly', !this.model.isNew());
this.actionsView.toggleActions();
// Configure keyboard shortcuts for the textarea
haste.prototype.configureShortcuts = function() {
var _this = this;
$(document.body).keydown(function(evt) {
var button;
for (var i = 0 ; i < _this.buttons.length; i++) {
button = _this.buttons[i];
if (button.shortcut && button.shortcut(evt)) {
evt.preventDefault();
button.action();
return;
}
}
});
};
///// Tab behavior in the textarea - 2 spaces per tab
$(function() {
Haste.init();
$('textarea').keydown(function(evt) {
if (evt.keyCode === 9) {
evt.preventDefault();
var myValue = ' ';
// http://stackoverflow.com/questions/946534/insert-text-into-textarea-with-jquery
// For browsers like Internet Explorer
if (document.selection) {
this.focus();
var sel = document.selection.createRange();
sel.text = myValue;
this.focus();
}
// Mozilla and Webkit
else if (this.selectionStart || this.selectionStart == '0') {
var startPos = this.selectionStart;
var endPos = this.selectionEnd;
var scrollTop = this.scrollTop;
this.value = this.value.substring(0, startPos) + myValue +
this.value.substring(endPos,this.value.length);
this.focus();
this.selectionStart = startPos + myValue.length;
this.selectionEnd = startPos + myValue.length;
this.scrollTop = scrollTop;
}
else {
this.value += myValue;
this.focus();
}
}
});
});

File diff suppressed because one or more lines are too long

View File

@ -1,37 +0,0 @@
// Backbone.js 0.9.1
// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Backbone may be freely distributed under the MIT license.
// For all details and documentation:
// http://backbonejs.org
(function(){var i=this,r=i.Backbone,s=Array.prototype.slice,t=Array.prototype.splice,g;g="undefined"!==typeof exports?exports:i.Backbone={};g.VERSION="0.9.1";var f=i._;!f&&"undefined"!==typeof require&&(f=require("underscore"));var h=i.jQuery||i.Zepto||i.ender;g.setDomLibrary=function(a){h=a};g.noConflict=function(){i.Backbone=r;return this};g.emulateHTTP=!1;g.emulateJSON=!1;g.Events={on:function(a,b,c){for(var d,a=a.split(/\s+/),e=this._callbacks||(this._callbacks={});d=a.shift();){d=e[d]||(e[d]=
{});var f=d.tail||(d.tail=d.next={});f.callback=b;f.context=c;d.tail=f.next={}}return this},off:function(a,b,c){var d,e,f;if(a){if(e=this._callbacks)for(a=a.split(/\s+/);d=a.shift();)if(f=e[d],delete e[d],b&&f)for(;(f=f.next)&&f.next;)if(!(f.callback===b&&(!c||f.context===c)))this.on(d,f.callback,f.context)}else delete this._callbacks;return this},trigger:function(a){var b,c,d,e;if(!(d=this._callbacks))return this;e=d.all;for((a=a.split(/\s+/)).push(null);b=a.shift();)e&&a.push({next:e.next,tail:e.tail,
event:b}),(c=d[b])&&a.push({next:c.next,tail:c.tail});for(e=s.call(arguments,1);c=a.pop();){b=c.tail;for(d=c.event?[c.event].concat(e):e;(c=c.next)!==b;)c.callback.apply(c.context||this,d)}return this}};g.Events.bind=g.Events.on;g.Events.unbind=g.Events.off;g.Model=function(a,b){var c;a||(a={});b&&b.parse&&(a=this.parse(a));if(c=j(this,"defaults"))a=f.extend({},c,a);b&&b.collection&&(this.collection=b.collection);this.attributes={};this._escapedAttributes={};this.cid=f.uniqueId("c");if(!this.set(a,
{silent:!0}))throw Error("Can't create an invalid model");delete this._changed;this._previousAttributes=f.clone(this.attributes);this.initialize.apply(this,arguments)};f.extend(g.Model.prototype,g.Events,{idAttribute:"id",initialize:function(){},toJSON:function(){return f.clone(this.attributes)},get:function(a){return this.attributes[a]},escape:function(a){var b;if(b=this._escapedAttributes[a])return b;b=this.attributes[a];return this._escapedAttributes[a]=f.escape(null==b?"":""+b)},has:function(a){return null!=
this.attributes[a]},set:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c||(c={});if(!d)return this;d instanceof g.Model&&(d=d.attributes);if(c.unset)for(e in d)d[e]=void 0;if(!this._validate(d,c))return!1;this.idAttribute in d&&(this.id=d[this.idAttribute]);var b=this.attributes,k=this._escapedAttributes,n=this._previousAttributes||{},h=this._setting;this._changed||(this._changed={});this._setting=!0;for(e in d)if(a=d[e],f.isEqual(b[e],a)||delete k[e],c.unset?delete b[e]:b[e]=
a,this._changing&&!f.isEqual(this._changed[e],a)&&(this.trigger("change:"+e,this,a,c),this._moreChanges=!0),delete this._changed[e],!f.isEqual(n[e],a)||f.has(b,e)!=f.has(n,e))this._changed[e]=a;h||(!c.silent&&this.hasChanged()&&this.change(c),this._setting=!1);return this},unset:function(a,b){(b||(b={})).unset=!0;return this.set(a,null,b)},clear:function(a){(a||(a={})).unset=!0;return this.set(f.clone(this.attributes),a)},fetch:function(a){var a=a?f.clone(a):{},b=this,c=a.success;a.success=function(d,
e,f){if(!b.set(b.parse(d,f),a))return!1;c&&c(b,d)};a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},save:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c=c?f.clone(c):{};c.wait&&(e=f.clone(this.attributes));a=f.extend({},c,{silent:!0});if(d&&!this.set(d,c.wait?a:c))return!1;var k=this,h=c.success;c.success=function(a,b,e){b=k.parse(a,e);c.wait&&(b=f.extend(d||{},b));if(!k.set(b,c))return!1;h?h(k,a):k.trigger("sync",k,a,c)};c.error=g.wrapError(c.error,
k,c);b=this.isNew()?"create":"update";b=(this.sync||g.sync).call(this,b,this,c);c.wait&&this.set(e,a);return b},destroy:function(a){var a=a?f.clone(a):{},b=this,c=a.success,d=function(){b.trigger("destroy",b,b.collection,a)};if(this.isNew())return d();a.success=function(e){a.wait&&d();c?c(b,e):b.trigger("sync",b,e,a)};a.error=g.wrapError(a.error,b,a);var e=(this.sync||g.sync).call(this,"delete",this,a);a.wait||d();return e},url:function(){var a=j(this.collection,"url")||j(this,"urlRoot")||o();return this.isNew()?
a:a+("/"==a.charAt(a.length-1)?"":"/")+encodeURIComponent(this.id)},parse:function(a){return a},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return null==this.id},change:function(a){if(this._changing||!this.hasChanged())return this;this._moreChanges=this._changing=!0;for(var b in this._changed)this.trigger("change:"+b,this,this._changed[b],a);for(;this._moreChanges;)this._moreChanges=!1,this.trigger("change",this,a);this._previousAttributes=f.clone(this.attributes);
delete this._changed;this._changing=!1;return this},hasChanged:function(a){return!arguments.length?!f.isEmpty(this._changed):this._changed&&f.has(this._changed,a)},changedAttributes:function(a){if(!a)return this.hasChanged()?f.clone(this._changed):!1;var b,c=!1,d=this._previousAttributes,e;for(e in a)if(!f.isEqual(d[e],b=a[e]))(c||(c={}))[e]=b;return c},previous:function(a){return!arguments.length||!this._previousAttributes?null:this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},
isValid:function(){return!this.validate(this.attributes)},_validate:function(a,b){if(b.silent||!this.validate)return!0;var a=f.extend({},this.attributes,a),c=this.validate(a,b);if(!c)return!0;b&&b.error?b.error(this,c,b):this.trigger("error",this,c,b);return!1}});g.Collection=function(a,b){b||(b={});b.comparator&&(this.comparator=b.comparator);this._reset();this.initialize.apply(this,arguments);a&&this.reset(a,{silent:!0,parse:b.parse})};f.extend(g.Collection.prototype,g.Events,{model:g.Model,initialize:function(){},
toJSON:function(){return this.map(function(a){return a.toJSON()})},add:function(a,b){var c,d,e,g,h,i={},j={};b||(b={});a=f.isArray(a)?a.slice():[a];for(c=0,d=a.length;c<d;c++){if(!(e=a[c]=this._prepareModel(a[c],b)))throw Error("Can't add an invalid model to a collection");if(i[g=e.cid]||this._byCid[g]||null!=(h=e.id)&&(j[h]||this._byId[h]))throw Error("Can't add the same model to a collection twice");i[g]=j[h]=e}for(c=0;c<d;c++)(e=a[c]).on("all",this._onModelEvent,this),this._byCid[e.cid]=e,null!=
e.id&&(this._byId[e.id]=e);this.length+=d;t.apply(this.models,[null!=b.at?b.at:this.models.length,0].concat(a));this.comparator&&this.sort({silent:!0});if(b.silent)return this;for(c=0,d=this.models.length;c<d;c++)if(i[(e=this.models[c]).cid])b.index=c,e.trigger("add",e,this,b);return this},remove:function(a,b){var c,d,e,g;b||(b={});a=f.isArray(a)?a.slice():[a];for(c=0,d=a.length;c<d;c++)if(g=this.getByCid(a[c])||this.get(a[c]))delete this._byId[g.id],delete this._byCid[g.cid],e=this.indexOf(g),this.models.splice(e,
1),this.length--,b.silent||(b.index=e,g.trigger("remove",g,this,b)),this._removeReference(g);return this},get:function(a){return null==a?null:this._byId[null!=a.id?a.id:a]},getByCid:function(a){return a&&this._byCid[a.cid||a]},at:function(a){return this.models[a]},sort:function(a){a||(a={});if(!this.comparator)throw Error("Cannot sort a set without a comparator");var b=f.bind(this.comparator,this);1==this.comparator.length?this.models=this.sortBy(b):this.models.sort(b);a.silent||this.trigger("reset",
this,a);return this},pluck:function(a){return f.map(this.models,function(b){return b.get(a)})},reset:function(a,b){a||(a=[]);b||(b={});for(var c=0,d=this.models.length;c<d;c++)this._removeReference(this.models[c]);this._reset();this.add(a,{silent:!0,parse:b.parse});b.silent||this.trigger("reset",this,b);return this},fetch:function(a){a=a?f.clone(a):{};void 0===a.parse&&(a.parse=!0);var b=this,c=a.success;a.success=function(d,e,f){b[a.add?"add":"reset"](b.parse(d,f),a);c&&c(b,d)};a.error=g.wrapError(a.error,
b,a);return(this.sync||g.sync).call(this,"read",this,a)},create:function(a,b){var c=this,b=b?f.clone(b):{},a=this._prepareModel(a,b);if(!a)return!1;b.wait||c.add(a,b);var d=b.success;b.success=function(e,f){b.wait&&c.add(e,b);d?d(e,f):e.trigger("sync",a,f,b)};a.save(null,b);return a},parse:function(a){return a},chain:function(){return f(this.models).chain()},_reset:function(){this.length=0;this.models=[];this._byId={};this._byCid={}},_prepareModel:function(a,b){a instanceof g.Model?a.collection||
(a.collection=this):(b.collection=this,a=new this.model(a,b),a._validate(a.attributes,b)||(a=!1));return a},_removeReference:function(a){this==a.collection&&delete a.collection;a.off("all",this._onModelEvent,this)},_onModelEvent:function(a,b,c,d){("add"==a||"remove"==a)&&c!=this||("destroy"==a&&this.remove(b,d),b&&a==="change:"+b.idAttribute&&(delete this._byId[b.previous(b.idAttribute)],this._byId[b.id]=b),this.trigger.apply(this,arguments))}});f.each("forEach,each,map,reduce,reduceRight,find,detect,filter,select,reject,every,all,some,any,include,contains,invoke,max,min,sortBy,sortedIndex,toArray,size,first,initial,rest,last,without,indexOf,shuffle,lastIndexOf,isEmpty,groupBy".split(","),
function(a){g.Collection.prototype[a]=function(){return f[a].apply(f,[this.models].concat(f.toArray(arguments)))}});g.Router=function(a){a||(a={});a.routes&&(this.routes=a.routes);this._bindRoutes();this.initialize.apply(this,arguments)};var u=/:\w+/g,v=/\*\w+/g,w=/[-[\]{}()+?.,\\^$|#\s]/g;f.extend(g.Router.prototype,g.Events,{initialize:function(){},route:function(a,b,c){g.history||(g.history=new g.History);f.isRegExp(a)||(a=this._routeToRegExp(a));c||(c=this[b]);g.history.route(a,f.bind(function(d){d=
this._extractParameters(a,d);c&&c.apply(this,d);this.trigger.apply(this,["route:"+b].concat(d));g.history.trigger("route",this,b,d)},this));return this},navigate:function(a,b){g.history.navigate(a,b)},_bindRoutes:function(){if(this.routes){var a=[],b;for(b in this.routes)a.unshift([b,this.routes[b]]);b=0;for(var c=a.length;b<c;b++)this.route(a[b][0],a[b][1],this[a[b][1]])}},_routeToRegExp:function(a){a=a.replace(w,"\\$&").replace(u,"([^/]+)").replace(v,"(.*?)");return RegExp("^"+a+"$")},_extractParameters:function(a,
b){return a.exec(b).slice(1)}});g.History=function(){this.handlers=[];f.bindAll(this,"checkUrl")};var m=/^[#\/]/,x=/msie [\w.]+/,l=!1;f.extend(g.History.prototype,g.Events,{interval:50,getFragment:function(a,b){if(null==a)if(this._hasPushState||b){var a=window.location.pathname,c=window.location.search;c&&(a+=c)}else a=window.location.hash;a=decodeURIComponent(a);a.indexOf(this.options.root)||(a=a.substr(this.options.root.length));return a.replace(m,"")},start:function(a){if(l)throw Error("Backbone.history has already been started");
this.options=f.extend({},{root:"/"},this.options,a);this._wantsHashChange=!1!==this.options.hashChange;this._wantsPushState=!!this.options.pushState;this._hasPushState=!(!this.options.pushState||!window.history||!window.history.pushState);var a=this.getFragment(),b=document.documentMode;if(b=x.exec(navigator.userAgent.toLowerCase())&&(!b||7>=b))this.iframe=h('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo("body")[0].contentWindow,this.navigate(a);this._hasPushState?h(window).bind("popstate",
this.checkUrl):this._wantsHashChange&&"onhashchange"in window&&!b?h(window).bind("hashchange",this.checkUrl):this._wantsHashChange&&(this._checkUrlInterval=setInterval(this.checkUrl,this.interval));this.fragment=a;l=!0;a=window.location;b=a.pathname==this.options.root;if(this._wantsHashChange&&this._wantsPushState&&!this._hasPushState&&!b)return this.fragment=this.getFragment(null,!0),window.location.replace(this.options.root+"#"+this.fragment),!0;this._wantsPushState&&this._hasPushState&&b&&a.hash&&
(this.fragment=a.hash.replace(m,""),window.history.replaceState({},document.title,a.protocol+"//"+a.host+this.options.root+this.fragment));if(!this.options.silent)return this.loadUrl()},stop:function(){h(window).unbind("popstate",this.checkUrl).unbind("hashchange",this.checkUrl);clearInterval(this._checkUrlInterval);l=!1},route:function(a,b){this.handlers.unshift({route:a,callback:b})},checkUrl:function(){var a=this.getFragment();a==this.fragment&&this.iframe&&(a=this.getFragment(this.iframe.location.hash));
if(a==this.fragment||a==decodeURIComponent(this.fragment))return!1;this.iframe&&this.navigate(a);this.loadUrl()||this.loadUrl(window.location.hash)},loadUrl:function(a){var b=this.fragment=this.getFragment(a);return f.any(this.handlers,function(a){if(a.route.test(b))return a.callback(b),!0})},navigate:function(a,b){if(!l)return!1;if(!b||!0===b)b={trigger:b};var c=(a||"").replace(m,"");this.fragment==c||this.fragment==decodeURIComponent(c)||(this._hasPushState?(0!=c.indexOf(this.options.root)&&(c=
this.options.root+c),this.fragment=c,window.history[b.replace?"replaceState":"pushState"]({},document.title,c)):this._wantsHashChange?(this.fragment=c,this._updateHash(window.location,c,b.replace),this.iframe&&c!=this.getFragment(this.iframe.location.hash)&&(b.replace||this.iframe.document.open().close(),this._updateHash(this.iframe.location,c,b.replace))):window.location.assign(this.options.root+a),b.trigger&&this.loadUrl(a))},_updateHash:function(a,b,c){c?a.replace(a.toString().replace(/(javascript:|#).*$/,
"")+"#"+b):a.hash=b}});g.View=function(a){this.cid=f.uniqueId("view");this._configure(a||{});this._ensureElement();this.initialize.apply(this,arguments);this.delegateEvents()};var y=/^(\S+)\s*(.*)$/,p="model,collection,el,id,attributes,className,tagName".split(",");f.extend(g.View.prototype,g.Events,{tagName:"div",$:function(a){return this.$el.find(a)},initialize:function(){},render:function(){return this},remove:function(){this.$el.remove();return this},make:function(a,b,c){a=document.createElement(a);
b&&h(a).attr(b);c&&h(a).html(c);return a},setElement:function(a,b){this.$el=h(a);this.el=this.$el[0];!1!==b&&this.delegateEvents();return this},delegateEvents:function(a){if(a||(a=j(this,"events"))){this.undelegateEvents();for(var b in a){var c=a[b];f.isFunction(c)||(c=this[a[b]]);if(!c)throw Error('Event "'+a[b]+'" does not exist');var d=b.match(y),e=d[1],d=d[2],c=f.bind(c,this),e=e+(".delegateEvents"+this.cid);""===d?this.$el.bind(e,c):this.$el.delegate(d,e,c)}}},undelegateEvents:function(){this.$el.unbind(".delegateEvents"+
this.cid)},_configure:function(a){this.options&&(a=f.extend({},this.options,a));for(var b=0,c=p.length;b<c;b++){var d=p[b];a[d]&&(this[d]=a[d])}this.options=a},_ensureElement:function(){if(this.el)this.setElement(this.el,!1);else{var a=j(this,"attributes")||{};this.id&&(a.id=this.id);this.className&&(a["class"]=this.className);this.setElement(this.make(this.tagName,a),!1)}}});g.Model.extend=g.Collection.extend=g.Router.extend=g.View.extend=function(a,b){var c=z(this,a,b);c.extend=this.extend;return c};
var A={create:"POST",update:"PUT","delete":"DELETE",read:"GET"};g.sync=function(a,b,c){var d=A[a],e={type:d,dataType:"json"};c.url||(e.url=j(b,"url")||o());if(!c.data&&b&&("create"==a||"update"==a))e.contentType="application/json",e.data=JSON.stringify(b.toJSON());g.emulateJSON&&(e.contentType="application/x-www-form-urlencoded",e.data=e.data?{model:e.data}:{});if(g.emulateHTTP&&("PUT"===d||"DELETE"===d))g.emulateJSON&&(e.data._method=d),e.type="POST",e.beforeSend=function(a){a.setRequestHeader("X-HTTP-Method-Override",
d)};"GET"!==e.type&&!g.emulateJSON&&(e.processData=!1);return h.ajax(f.extend(e,c))};g.wrapError=function(a,b,c){return function(d,e){e=d===b?e:d;a?a(b,e,c):b.trigger("error",b,e,c)}};var q=function(){},z=function(a,b,c){var d;d=b&&b.hasOwnProperty("constructor")?b.constructor:function(){a.apply(this,arguments)};f.extend(d,a);q.prototype=a.prototype;d.prototype=new q;b&&f.extend(d.prototype,b);c&&f.extend(d,c);d.prototype.constructor=d;d.__super__=a.prototype;return d},j=function(a,b){return!a||!a[b]?
null:f.isFunction(a[b])?a[b]():a[b]},o=function(){throw Error('A "url" property or function must be specified');}}).call(this);

File diff suppressed because one or more lines are too long

3
static/highlight.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,35 +1,68 @@
<!DOCTYPE html>
<html lang="en">
<html>
<head>
<title>hastebin</title>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="solarized_dark.css"/>
<link rel="stylesheet" type="text/css" href="application.css"/>
<title>Hastebin</title>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript" src="highlight.min.js"></script>
<script type="text/javascript" src="application.min.js"></script>
<link rel="stylesheet" href="application.css" />
<meta name="robots" content="noindex,nofollow"/>
<script type="text/javascript">
var app = null;
// Handle pops
var handlePop = function(evt) {
var path = evt.target.location.pathname;
if (path === '/') { app.newDocument(true); }
else { app.loadDocument(path.substring(1, path.length)); }
};
// Set up the pop state to handle loads, skipping the first load
// to make chrome behave like others:
// http://code.google.com/p/chromium/issues/detail?id=63040
setTimeout(function() {
window.onpopstate = function(evt) {
try { handlePop(evt); } catch(err) { /* not loaded yet */ }
};
}, 1000);
// Construct app and load initial path
$(function() {
app = new haste('hastebin', { twitter: true });
handlePop({ target: window });
});
</script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="codemirror.min.js"></script>
<script src="underscore.min.js"></script>
<script src="backbone.min.js"></script>
<script src="application.min.js"></script>
</head>
<body>
<header>
<h1><a href="about.md">Hastebin</a></h1>
<ul id="messages"></ul>
<nav>
<ul>
<li><a href="#" class="save">Save</a></li>
<li><a href="#" class="new">New</a></li>
<li><a href="#" class="edit">Edit</a></li>
<li><a href="#" class="raw">Raw</a></li>
<li><a href="#" class="twitter">Twitter</a></li>
</ul>
</nav>
</header>
<div id="editor">
<textarea></textarea>
<div id="key">
<div id="pointer" style="display:none;"></div>
<div id="box1">
<a href="/about.md" class="logo"></a>
</div>
<div id="box2">
<button class="save function button-picture">Save</button>
<button class="new function button-picture">New</button>
<button class="duplicate function button-picture">Duplicate & Edit</button>
<button class="raw function button-picture">Just Text</button>
<button class="twitter function button-picture">Twitter</button>
</div>
<div id="box3" style="display:none;">
<div class="label"></div>
<div class="shortcut"></div>
</div>
</div>
<div id="linenos"></div>
<pre id="box" style="display:none;" class="hljs" tabindex="0"><code></code></pre>
<textarea spellcheck="false" style="display:none;"></textarea>
</body>
</html>

84
static/solarized_dark.css Normal file
View File

@ -0,0 +1,84 @@
/*
Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull <sourdrums@gmail.com>
*/
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
background: #002b36;
color: #839496;
}
.hljs-comment,
.hljs-quote {
color: #586e75;
}
/* Solarized Green */
.hljs-keyword,
.hljs-selector-tag,
.hljs-addition {
color: #859900;
}
/* Solarized Cyan */
.hljs-number,
.hljs-string,
.hljs-meta .hljs-meta-string,
.hljs-literal,
.hljs-doctag,
.hljs-regexp {
color: #2aa198;
}
/* Solarized Blue */
.hljs-title,
.hljs-section,
.hljs-name,
.hljs-selector-id,
.hljs-selector-class {
color: #268bd2;
}
/* Solarized Yellow */
.hljs-attribute,
.hljs-attr,
.hljs-variable,
.hljs-template-variable,
.hljs-class .hljs-title,
.hljs-type {
color: #b58900;
}
/* Solarized Orange */
.hljs-symbol,
.hljs-bullet,
.hljs-subst,
.hljs-meta,
.hljs-meta .hljs-keyword,
.hljs-selector-attr,
.hljs-selector-pseudo,
.hljs-link {
color: #cb4b16;
}
/* Solarized Red */
.hljs-built_in,
.hljs-deletion {
color: #dc322f;
}
.hljs-formula {
background: #073642;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}

View File

@ -1,31 +0,0 @@
// Underscore.js 1.3.1
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore is freely distributable under the MIT license.
// Portions of Underscore are inspired or borrowed from Prototype,
// Oliver Steele's Functional, and John Resig's Micro-Templating.
// For all details and documentation:
// http://documentcloud.github.com/underscore
(function(){function q(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return false;switch(e){case "[object String]":return a==String(c);case "[object Number]":return a!=+a?c!=+c:a==0?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source==
c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){if(f=a.length,g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&q(a[f],c[f],d)))break}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return false;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&q(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c,
h)&&!f--)break;g=!f}}d.pop();return g}var r=this,G=r._,n={},k=Array.prototype,o=Object.prototype,i=k.slice,H=k.unshift,l=o.toString,I=o.hasOwnProperty,w=k.forEach,x=k.map,y=k.reduce,z=k.reduceRight,A=k.filter,B=k.every,C=k.some,p=k.indexOf,D=k.lastIndexOf,o=Array.isArray,J=Object.keys,s=Function.prototype.bind,b=function(a){return new m(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=b;exports._=b}else r._=b;b.VERSION="1.3.1";var j=b.each=
b.forEach=function(a,c,d){if(a!=null)if(w&&a.forEach===w)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(e in a&&c.call(d,a[e],e,a)===n)break}else for(e in a)if(b.has(a,e)&&c.call(d,a[e],e,a)===n)break};b.map=b.collect=function(a,c,b){var e=[];if(a==null)return e;if(x&&a.map===x)return a.map(c,b);j(a,function(a,g,h){e[e.length]=c.call(b,a,g,h)});if(a.length===+a.length)e.length=a.length;return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var f=arguments.length>2;a==
null&&(a=[]);if(y&&a.reduce===y)return e&&(c=b.bind(c,e)),f?a.reduce(c,d):a.reduce(c);j(a,function(a,b,i){f?d=c.call(e,d,a,b,i):(d=a,f=true)});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(z&&a.reduceRight===z)return e&&(c=b.bind(c,e)),f?a.reduceRight(c,d):a.reduceRight(c);var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect=
function(a,c,b){var e;E(a,function(a,g,h){if(c.call(b,a,g,h))return e=a,true});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(A&&a.filter===A)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(B&&a.every===B)return a.every(c,b);j(a,function(a,g,h){if(!(e=
e&&c.call(b,a,g,h)))return n});return e};var E=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(C&&a.some===C)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return n});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;return p&&a.indexOf===p?a.indexOf(c)!=-1:b=E(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck=
function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b<e.computed&&(e={value:a,computed:b})});
return e.value};b.shuffle=function(a){var b=[],d;j(a,function(a,f){f==0?b[0]=a:(d=Math.floor(Math.random()*(f+1)),b[f]=b[d],b[d]=a)});return b};b.sortBy=function(a,c,d){return b.pluck(b.map(a,function(a,b,g){return{value:a,criteria:c.call(d,a,b,g)}}).sort(function(a,b){var c=a.criteria,d=b.criteria;return c<d?-1:c>d?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a,
c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:a.toArray?a.toArray():b.isArray(a)?i.call(a):b.isArguments(a)?i.call(a):b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=b.head=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};b.initial=function(a,b,d){return i.call(a,0,a.length-(b==null||d?1:b))};b.last=function(a,b,d){return b!=null&&!d?i.call(a,Math.max(a.length-b,0)):a[a.length-1]};b.rest=
b.tail=function(a,b,d){return i.call(a,b==null||d?1:b)};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a,c){return b.reduce(a,function(a,e){if(b.isArray(e))return a.concat(c?e:b.flatten(e));a[a.length]=e;return a},[])};b.without=function(a){return b.difference(a,i.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,e=[];b.reduce(d,function(d,g,h){if(0==h||(c===true?b.last(d)!=g:!b.include(d,g)))d[d.length]=g,e[e.length]=a[h];return d},[]);
return e};b.union=function(){return b.uniq(b.flatten(arguments,true))};b.intersection=b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1));return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c,
d){if(a==null)return-1;var e;if(d)return d=b.sortedIndex(a,c),a[d]===c?d:-1;if(p&&a.indexOf===p)return a.indexOf(c);for(d=0,e=a.length;d<e;d++)if(d in a&&a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(D&&a.lastIndexOf===D)return a.lastIndexOf(b);for(var d=a.length;d--;)if(d in a&&a[d]===b)return d;return-1};b.range=function(a,b,d){arguments.length<=1&&(b=a||0,a=0);for(var d=arguments[2]||1,e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;)g[f++]=a,a+=d;return g};
var F=function(){};b.bind=function(a,c){var d,e;if(a.bind===s&&s)return s.apply(a,i.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=i.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(i.call(arguments)));F.prototype=a.prototype;var b=new F,g=a.apply(b,e.concat(i.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=i.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,
c){var d={};c||(c=b.identity);return function(){var e=c.apply(this,arguments);return b.has(d,e)?d[e]:d[e]=a.apply(this,arguments)}};b.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(a,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,h,i=b.debounce(function(){h=g=false},c);return function(){d=this;e=arguments;var b;f||(f=setTimeout(function(){f=null;h&&a.apply(d,e);i()},c));g?h=true:
a.apply(d,e);i();g=true}};b.debounce=function(a,b){var d;return function(){var e=this,f=arguments;clearTimeout(d);d=setTimeout(function(){d=null;a.apply(e,f)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=[a].concat(i.call(arguments,0));return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};
b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=J||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.defaults=function(a){j(i.call(arguments,
1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return q(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=o||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};
b.isArguments=function(a){return l.call(a)=="[object Arguments]"};if(!b.isArguments(arguments))b.isArguments=function(a){return!(!a||!b.has(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"};
b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,b){return I.call(a,b)};b.noConflict=function(){r._=G;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.escape=function(a){return(""+a).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;").replace(/\//g,"&#x2F;")};b.mixin=function(a){j(b.functions(a),
function(c){K(c,b[c]=a[c])})};var L=0;b.uniqueId=function(a){var b=L++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var t=/.^/,u=function(a){return a.replace(/\\\\/g,"\\").replace(/\\'/g,"'")};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape||t,function(a,b){return"',_.escape("+
u(b)+"),'"}).replace(d.interpolate||t,function(a,b){return"',"+u(b)+",'"}).replace(d.evaluate||t,function(a,b){return"');"+u(b).replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e.call(this,a,b)}};b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var v=function(a,c){return c?b(a).chain():a},K=function(a,c){m.prototype[a]=
function(){var a=i.call(arguments);H.call(a,this._wrapped);return v(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return v(d,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return v(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain=
true;return this};m.prototype.value=function(){return this._wrapped}}).call(this);

View File

@ -1,3 +1,7 @@
/* global describe, it */
var assert = require('assert');
var DocumentHandler = require('../lib/document_handler');
var Generator = require('../lib/key_generators/random');
@ -8,13 +12,13 @@ describe('document_handler', function() {
it('should choose a key of the proper length', function() {
var gen = new Generator();
var dh = new DocumentHandler({ keyLength: 6, keyGenerator: gen });
dh.acceptableKey().length.should.equal(6);
assert.equal(6, dh.acceptableKey().length);
});
it('should choose a default key length', function() {
var gen = new Generator();
var dh = new DocumentHandler({ keyGenerator: gen });
dh.keyLength.should.equal(DocumentHandler.defaultKeyLength);
assert.equal(dh.keyLength, DocumentHandler.defaultKeyLength);
});
});

View File

@ -0,0 +1,33 @@
/* global describe, it */
const assert = require('assert');
const fs = require('fs');
const Generator = require('../../lib/key_generators/dictionary');
describe('RandomKeyGenerator', function() {
describe('randomKey', function() {
it('should throw an error if given no options', () => {
assert.throws(() => {
new Generator();
}, Error);
});
it('should throw an error if given no path', () => {
assert.throws(() => {
new Generator({});
}, Error);
});
it('should return a key of the proper number of words from the given dictionary', () => {
const path = '/tmp/haste-server-test-dictionary';
const words = ['cat'];
fs.writeFileSync(path, words.join('\n'));
const gen = new Generator({path}, () => {
assert.equal('catcatcat', gen.createKey(3));
});
});
});
});

View File

@ -0,0 +1,27 @@
/* global describe, it */
const assert = require('assert');
const Generator = require('../../lib/key_generators/phonetic');
const vowels = 'aeiou';
const consonants = 'bcdfghjklmnpqrstvwxyz';
describe('RandomKeyGenerator', () => {
describe('randomKey', () => {
it('should return a key of the proper length', () => {
const gen = new Generator();
assert.equal(6, gen.createKey(6).length);
});
it('should alternate consonants and vowels', () => {
const gen = new Generator();
const key = gen.createKey(3);
assert.ok(consonants.includes(key[0]));
assert.ok(consonants.includes(key[2]));
assert.ok(vowels.includes(key[1]));
});
});
});

View File

@ -0,0 +1,19 @@
/* global describe, it */
const assert = require('assert');
const Generator = require('../../lib/key_generators/random');
describe('RandomKeyGenerator', () => {
describe('randomKey', () => {
it('should return a key of the proper length', () => {
const gen = new Generator();
assert.equal(6, gen.createKey(6).length);
});
it('should use a key from the given keyset if given', () => {
const gen = new Generator({keyspace: 'A'});
assert.equal('AAAAAA', gen.createKey(6));
});
});
});

View File

@ -1,8 +1,12 @@
var RedisDocumentStore = require('../lib/document_stores/redis');
/* global it, describe, afterEach */
var assert = require('assert');
var winston = require('winston');
winston.remove(winston.transports.Console);
var RedisDocumentStore = require('../lib/document_stores/redis');
describe('redis_document_store', function() {
/* reconnect to redis on each test */
@ -19,7 +23,7 @@ describe('redis_document_store', function() {
var store = new RedisDocumentStore({ expire: 10 });
store.set('hello1', 'world', function() {
RedisDocumentStore.client.ttl('hello1', function(err, res) {
res.should.be.above(1);
assert.ok(res > 1);
done();
});
});
@ -29,7 +33,7 @@ describe('redis_document_store', function() {
var store = new RedisDocumentStore({ expire: 10 });
store.set('hello2', 'world', function() {
RedisDocumentStore.client.ttl('hello2', function(err, res) {
res.should.equal(-1);
assert.equal(-1, res);
done();
});
}, true);
@ -37,9 +41,9 @@ describe('redis_document_store', function() {
it('should not set an expiration when expiration is off', function(done) {
var store = new RedisDocumentStore({ expire: false });
store.set('hello3', 'world', function(worked) {
store.set('hello3', 'world', function() {
RedisDocumentStore.client.ttl('hello3', function(err, res) {
res.should.equal(-1);
assert.equal(-1, res);
done();
});
});