Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5de2a52d4d |
@@ -1,6 +0,0 @@
|
||||
npm-debug.log
|
||||
node_modules
|
||||
*.swp
|
||||
*.swo
|
||||
data
|
||||
*.DS_Store
|
||||
@@ -1,177 +0,0 @@
|
||||
# Haste
|
||||
|
||||
Haste is an open-source pastebin software written in node.js, which is easily
|
||||
installable in any network. It can be backed by either redis or filesystem,
|
||||
and has a very easy adapter interface for other stores. A publicly available
|
||||
version can be found at [hastebin.com](http://hastebin.com)
|
||||
|
||||
Major design objectives:
|
||||
|
||||
* Be really pretty
|
||||
* Be really simple
|
||||
* Be easy to set up and use
|
||||
|
||||
Haste works really well with a little utility called
|
||||
[haste-client](https://github.com/seejohnrun/haste-client), allowing you
|
||||
to do things like:
|
||||
|
||||
`cat something | haste`
|
||||
|
||||
which will output a URL to share containing the contents of `cat something`'s
|
||||
STDOUT. Check the README there for more details and usages.
|
||||
|
||||
## Support
|
||||
|
||||
Please consider paying what you feel this project is worth:
|
||||
|
||||
<a href="https://www.stripeme.com/pay/1r2f">
|
||||
<img alt="Pay" src="https://www.stripeme.com/pay.jpg" />
|
||||
</a>
|
||||
|
||||
## Tested Browsers
|
||||
|
||||
* Firefox 8
|
||||
* Chrome 17
|
||||
* Safari 5.3
|
||||
|
||||
## Installation
|
||||
|
||||
1. Download the package, and expand it
|
||||
2. Explore the settings inside of config.js, but the defaults should be good
|
||||
3. `npm install`
|
||||
4. `npm start`
|
||||
|
||||
## Settings
|
||||
|
||||
* `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)
|
||||
* `staticMaxAge` - max age for static assets (86400)
|
||||
* `recompressStatisAssets` - 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)
|
||||
|
||||
## Key Generation
|
||||
|
||||
### Phonetic
|
||||
|
||||
Attempts to generate phonetic keys, similar to `pwgen`
|
||||
|
||||
``` json
|
||||
{
|
||||
"type": "phonetic"
|
||||
}
|
||||
```
|
||||
|
||||
### Random
|
||||
|
||||
Generates a random key
|
||||
|
||||
``` json
|
||||
{
|
||||
"type": "random",
|
||||
"keyspace": "abcdef"
|
||||
}
|
||||
```
|
||||
|
||||
The _optional_ keySpace argument is a string of acceptable characters
|
||||
for the key.
|
||||
|
||||
## Storage
|
||||
|
||||
### File
|
||||
|
||||
To use file storage (the default) change the storage section in `config.js` to
|
||||
something like:
|
||||
|
||||
``` json
|
||||
{
|
||||
"path": "./data",
|
||||
"type": "file"
|
||||
}
|
||||
```
|
||||
|
||||
Where `path` represents where you want the files stored
|
||||
|
||||
### Redis
|
||||
|
||||
To use redis storage you must install the redis package in npm
|
||||
|
||||
`npm install redis`
|
||||
|
||||
Once you've done that, your config section should look like:
|
||||
|
||||
``` json
|
||||
{
|
||||
"type": "redis",
|
||||
"host": "localhost",
|
||||
"port": 6379,
|
||||
"db": 2
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
`npm install memcache`
|
||||
|
||||
Once you've done that, your config section should look like:
|
||||
|
||||
``` json
|
||||
{
|
||||
"type": "memcached",
|
||||
"host": "127.0.0.1",
|
||||
"port": 11211
|
||||
}
|
||||
```
|
||||
|
||||
You can also set an `expire` option to the number of seconds to expire keys in.
|
||||
This behaves just like the redis expirations, but does not push expirations
|
||||
forward on GETs.
|
||||
|
||||
All of which are optional except `type` with very logical default values.
|
||||
|
||||
## Author
|
||||
|
||||
John Crepezzi <john.crepezzi@gmail.com>
|
||||
|
||||
## License
|
||||
|
||||
(The MIT License)
|
||||
|
||||
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
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE
|
||||
|
||||
### Other components:
|
||||
|
||||
* jQuery: MIT/GPL license
|
||||
* highlight.js: Copyright © 2006, Ivan Sagalaev
|
||||
* highlightjs-coffeescript: WTFPL - Copyright © 2011, Dmytrii Nagirniak
|
||||
@@ -1,61 +0,0 @@
|
||||
# Haste
|
||||
|
||||
Sharing code is a good thing, and it should be _really_ easy to do it.
|
||||
A lot of times, I want to show you something I'm seeing - and that's where we
|
||||
use pastebins.
|
||||
|
||||
Haste is the prettiest, easiest to use pastebin ever made.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
Type what you want me to see, click "Save", and then copy the URL. Send that
|
||||
URL to someone and they'll see what you see.
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
You can even take this a step further, and cut out the last step of copying the
|
||||
URL with:
|
||||
|
||||
* osx: `cat something | haste | pbcopy`
|
||||
* linux: `cat something | haste | xsel`
|
||||
* windows: check out [WinHaste](https://github.com/ajryan/WinHaste)
|
||||
|
||||
After running that, the STDOUT output of `cat something` will show up at a URL
|
||||
which has been conveniently copied to your clipboard.
|
||||
|
||||
That's all there is to that, and you can install it with `gem install haste`
|
||||
right now.
|
||||
* osx: you will need to have an up to date version of Xcode
|
||||
* linux: you will need to have rubygems and ruby-devel installed
|
||||
|
||||
## Duration
|
||||
|
||||
Pastes will stay for 30 days from their last view. They may be removed earlier
|
||||
and without notice.
|
||||
|
||||
## Privacy
|
||||
|
||||
While the contents of hastebin.com are not directly crawled by any search robot
|
||||
that obeys "robots.txt", there should be no great expectation of privacy. Post
|
||||
things at your own risk. Not responsible for any loss of data or removed
|
||||
pastes.
|
||||
|
||||
## Open Source
|
||||
|
||||
Haste can easily be installed behind your network, and its all open source!
|
||||
|
||||
* [haste-client](https://github.com/seejohnrun/haste-client)
|
||||
* [haste-server](https://github.com/seejohnrun/haste-server)
|
||||
|
||||
## Author
|
||||
|
||||
Code by John Crepezzi <john.crepezzi@gmail.com>
|
||||
Key Design by Brian Dawson <bridawson@gmail.com>
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
|
||||
"host": "0.0.0.0",
|
||||
"port": 7777,
|
||||
|
||||
"keyLength": 10,
|
||||
|
||||
"maxLength": 400000,
|
||||
|
||||
"staticMaxAge": 86400,
|
||||
|
||||
"recompressStaticAssets": true,
|
||||
|
||||
"logging": [
|
||||
{
|
||||
"level": "verbose",
|
||||
"type": "Console",
|
||||
"colorize": true
|
||||
}
|
||||
],
|
||||
|
||||
"keyGenerator": {
|
||||
"type": "phonetic"
|
||||
},
|
||||
|
||||
"storage": {
|
||||
"type": "redis",
|
||||
"host": "0.0.0.0",
|
||||
"port": 6379,
|
||||
"db": 2,
|
||||
"expire": 2592000
|
||||
},
|
||||
|
||||
"documents": {
|
||||
"about": "./about.md"
|
||||
}
|
||||
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="chrome=1">
|
||||
<title>Haste-server by seejohnrun</title>
|
||||
|
||||
<link rel="stylesheet" href="stylesheets/styles.css">
|
||||
<link rel="stylesheet" href="stylesheets/pygment_trac.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<header>
|
||||
<h1>Haste-server</h1>
|
||||
<p>open source pastebin written in node.js</p>
|
||||
<p class="view"><a href="https://github.com/seejohnrun/haste-server">View the Project on GitHub <small>seejohnrun/haste-server</small></a></p>
|
||||
<ul>
|
||||
<li><a href="https://github.com/seejohnrun/haste-server/zipball/master">Download <strong>ZIP File</strong></a></li>
|
||||
<li><a href="https://github.com/seejohnrun/haste-server/tarball/master">Download <strong>TAR Ball</strong></a></li>
|
||||
<li><a href="https://github.com/seejohnrun/haste-server">Fork On <strong>GitHub</strong></a></li>
|
||||
</ul>
|
||||
</header>
|
||||
<section>
|
||||
<h1>Pastebin</h1>
|
||||
|
||||
<p>haste-server is an open source pastebin. You can see an example at <a href="http://hastebin.com">hastebin.com</a>, and it's easy to install locally.</p>
|
||||
|
||||
<h2>GitHub</h2>
|
||||
|
||||
<p>Check out the project on <a href="https://github.com/seejohnrun/haste-server">github</a></p>
|
||||
</section>
|
||||
<footer>
|
||||
<p>This project is maintained by <a href="https://github.com/seejohnrun">seejohnrun</a></p>
|
||||
<p><small>Hosted on GitHub Pages — Theme by <a href="https://github.com/orderedlist">orderedlist</a></small></p>
|
||||
</footer>
|
||||
</div>
|
||||
<script src="javascripts/scale.fix.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,17 @@
|
||||
var metas = document.getElementsByTagName('meta');
|
||||
var i;
|
||||
if (navigator.userAgent.match(/iPhone/i)) {
|
||||
for (i=0; i<metas.length; i++) {
|
||||
if (metas[i].name == "viewport") {
|
||||
metas[i].content = "width=device-width, minimum-scale=1.0, maximum-scale=1.0";
|
||||
}
|
||||
}
|
||||
document.addEventListener("gesturestart", gestureStart, false);
|
||||
}
|
||||
function gestureStart() {
|
||||
for (i=0; i<metas.length; i++) {
|
||||
if (metas[i].name == "viewport") {
|
||||
metas[i].content = "width=device-width, minimum-scale=0.25, maximum-scale=1.6";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
var winston = require('winston');
|
||||
|
||||
// For handling serving stored documents
|
||||
|
||||
var DocumentHandler = function(options) {
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
this.keyLength = options.keyLength || DocumentHandler.defaultKeyLength;
|
||||
this.maxLength = options.maxLength; // none by default
|
||||
this.store = options.store;
|
||||
this.keyGenerator = options.keyGenerator;
|
||||
};
|
||||
|
||||
DocumentHandler.defaultKeyLength = 10;
|
||||
|
||||
// Handle retrieving a document
|
||||
DocumentHandler.prototype.handleGet = function(key, response, skipExpire) {
|
||||
this.store.get(key, function(ret) {
|
||||
if (ret) {
|
||||
winston.verbose('retrieved document', { key: key });
|
||||
response.writeHead(200, { 'content-type': 'application/json' });
|
||||
response.end(JSON.stringify({ data: ret, key: key }));
|
||||
}
|
||||
else {
|
||||
winston.warn('document not found', { key: key });
|
||||
response.writeHead(404, { 'content-type': 'application/json' });
|
||||
response.end(JSON.stringify({ message: 'Document not found.' }));
|
||||
}
|
||||
}, skipExpire);
|
||||
};
|
||||
|
||||
// Handle retrieving the raw version of a document
|
||||
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.end(ret);
|
||||
}
|
||||
else {
|
||||
winston.warn('raw document not found', { key: key });
|
||||
response.writeHead(404, { 'content-type': 'application/json' });
|
||||
response.end(JSON.stringify({ message: 'Document not found.' }));
|
||||
}
|
||||
}, skipExpire);
|
||||
};
|
||||
|
||||
// Handle adding a new Document
|
||||
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 += data.toString();
|
||||
if (_this.maxLength && buffer.length > _this.maxLength) {
|
||||
cancelled = true;
|
||||
winston.warn('document >maxLength', { maxLength: _this.maxLength });
|
||||
response.writeHead(400, { 'content-type': 'application/json' });
|
||||
response.end(
|
||||
JSON.stringify({ message: 'Document exceeds maximum length.' })
|
||||
);
|
||||
}
|
||||
});
|
||||
request.on('end', function(end) {
|
||||
if (cancelled) return;
|
||||
_this.chooseKey(function(key) {
|
||||
_this.store.set(key, buffer, function(res) {
|
||||
if (res) {
|
||||
winston.verbose('added document', { key: key });
|
||||
response.end(JSON.stringify({ key: key }));
|
||||
}
|
||||
else {
|
||||
winston.verbose('error adding document');
|
||||
response.writeHead(500, { 'content-type': 'application/json' });
|
||||
response.end(JSON.stringify({ message: 'Error adding document.' }));
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
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.' }));
|
||||
});
|
||||
};
|
||||
|
||||
// Keep choosing keys until one isn't taken
|
||||
DocumentHandler.prototype.chooseKey = function(callback) {
|
||||
var key = this.acceptableKey();
|
||||
var _this = this;
|
||||
this.store.get(key, function(ret) {
|
||||
if (ret) {
|
||||
_this.chooseKey(callback);
|
||||
} else {
|
||||
callback(key);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
DocumentHandler.prototype.acceptableKey = function() {
|
||||
return this.keyGenerator.createKey(this.keyLength);
|
||||
};
|
||||
|
||||
module.exports = DocumentHandler;
|
||||
@@ -1,63 +0,0 @@
|
||||
var fs = require('fs');
|
||||
var crypto = require('crypto');
|
||||
|
||||
var winston = require('winston');
|
||||
|
||||
// For storing in files
|
||||
// options[type] = file
|
||||
// options[path] - Where to store
|
||||
|
||||
var FileDocumentStore = function(options) {
|
||||
this.basePath = options.path || './data';
|
||||
this.expire = options.expire;
|
||||
};
|
||||
|
||||
// Generate md5 of a string
|
||||
FileDocumentStore.md5 = function(str) {
|
||||
var md5sum = crypto.createHash('md5');
|
||||
md5sum.update(str);
|
||||
return md5sum.digest('hex');
|
||||
};
|
||||
|
||||
// Save data in a file, key as md5 - since we don't know what we could
|
||||
// be passed here
|
||||
FileDocumentStore.prototype.set = function(key, data, callback, skipExpire) {
|
||||
try {
|
||||
var _this = this;
|
||||
fs.mkdir(this.basePath, '700', function() {
|
||||
var fn = _this.basePath + '/' + FileDocumentStore.md5(key);
|
||||
fs.writeFile(fn, data, 'utf8', function(err) {
|
||||
if (err) {
|
||||
callback(false);
|
||||
}
|
||||
else {
|
||||
callback(true);
|
||||
if (_this.expire && !skipExpire) {
|
||||
winston.warn('file store cannot set expirations on keys');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch(err) {
|
||||
callback(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Get data from a file from key
|
||||
FileDocumentStore.prototype.get = function(key, callback, skipExpire) {
|
||||
var _this = this;
|
||||
var fn = this.basePath + '/' + FileDocumentStore.md5(key);
|
||||
fs.readFile(fn, 'utf8', function(err, data) {
|
||||
if (err) {
|
||||
callback(false);
|
||||
}
|
||||
else {
|
||||
callback(data);
|
||||
if (_this.expire && !skipExpire) {
|
||||
winston.warn('file store cannot set expirations on keys');
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = FileDocumentStore;
|
||||
@@ -1,45 +0,0 @@
|
||||
var memcached = require('memcache');
|
||||
var winston = require('winston');
|
||||
|
||||
// Create a new store with options
|
||||
var MemcachedDocumentStore = function(options) {
|
||||
this.expire = options.expire;
|
||||
if (!MemcachedDocumentStore.client) {
|
||||
MemcachedDocumentStore.connect(options);
|
||||
}
|
||||
};
|
||||
|
||||
// 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);
|
||||
});
|
||||
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);
|
||||
};
|
||||
|
||||
// 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');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = MemcachedDocumentStore;
|
||||
@@ -1,80 +0,0 @@
|
||||
var redis = require('redis');
|
||||
var winston = require('winston');
|
||||
|
||||
// For storing in redis
|
||||
// options[type] = redis
|
||||
// options[host] - The host to connect to (default localhost)
|
||||
// options[port] - The port to connect to (default 5379)
|
||||
// options[db] - The db to use (default 0)
|
||||
// options[expire] - The time to live for each key set (default never)
|
||||
|
||||
var RedisDocumentStore = function(options, client) {
|
||||
this.expire = options.expire;
|
||||
if (client) {
|
||||
winston.info('using predefined redis client');
|
||||
RedisDocumentStore.client = client;
|
||||
} else if (!RedisDocumentStore.client) {
|
||||
winston.info('configuring redis');
|
||||
RedisDocumentStore.connect(options);
|
||||
}
|
||||
};
|
||||
|
||||
// Create a connection according to config
|
||||
RedisDocumentStore.connect = function(options) {
|
||||
var host = options.host || '127.0.0.1';
|
||||
var port = options.port || 6379;
|
||||
var index = options.db || 0;
|
||||
RedisDocumentStore.client = redis.createClient(port, host);
|
||||
RedisDocumentStore.client.select(index, function(err, reply) {
|
||||
if (err) {
|
||||
winston.error(
|
||||
'error connecting to redis index ' + index,
|
||||
{ error: err.message }
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
else {
|
||||
winston.info('connected to redis on ' + host + ':' + port + '/' + index);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Save file in a key
|
||||
RedisDocumentStore.prototype.set = function(key, data, callback, skipExpire) {
|
||||
var _this = this;
|
||||
RedisDocumentStore.client.set(key, data, function(err, reply) {
|
||||
if (err) {
|
||||
callback(false);
|
||||
}
|
||||
else {
|
||||
if (!skipExpire) {
|
||||
_this.setExpiration(key);
|
||||
}
|
||||
callback(true);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 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) {
|
||||
if (err) {
|
||||
winston.error('failed to set expiry on key: ' + key);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Get a file from a key
|
||||
RedisDocumentStore.prototype.get = function(key, callback, skipExpire) {
|
||||
var _this = this;
|
||||
RedisDocumentStore.client.get(key, function(err, reply) {
|
||||
if (!err && !skipExpire) {
|
||||
_this.setExpiration(key);
|
||||
}
|
||||
callback(err ? false : reply);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = RedisDocumentStore;
|
||||
@@ -1,32 +0,0 @@
|
||||
// Draws inspiration from pwgen and http://tools.arantius.com/password
|
||||
var PhoneticKeyGenerator = function(options) {
|
||||
// No options
|
||||
};
|
||||
|
||||
// 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();
|
||||
}
|
||||
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;
|
||||
@@ -1,19 +0,0 @@
|
||||
var RandomKeyGenerator = function(options) {
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
this.keyspace = options.keyspace || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
};
|
||||
|
||||
// Generate a random key
|
||||
RandomKeyGenerator.prototype.createKey = function(keyLength) {
|
||||
var text = '';
|
||||
var index;
|
||||
for (var i = 0; i < keyLength; i++) {
|
||||
index = Math.floor(Math.random() * this.keyspace.length);
|
||||
text += this.keyspace.charAt(index);
|
||||
}
|
||||
return text;
|
||||
};
|
||||
|
||||
module.exports = RandomKeyGenerator;
|
||||
@@ -1,47 +0,0 @@
|
||||
{
|
||||
"name": "haste",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"description": "Private Pastebin Server",
|
||||
"keywords": [
|
||||
"paste",
|
||||
"pastebin"
|
||||
],
|
||||
"author": {
|
||||
"name": "John Crepezzi",
|
||||
"email": "john.crepezzi@gmail.com",
|
||||
"url": "http://seejohncode.com/"
|
||||
},
|
||||
"main": "haste",
|
||||
"dependencies": {
|
||||
"winston": "0.6.2",
|
||||
"connect": "1.9.2",
|
||||
"redis-url": "0.1.0",
|
||||
"redis": "0.8.1",
|
||||
"uglify-js": "1.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "*",
|
||||
"should": "*"
|
||||
},
|
||||
"bundledDependencies": [],
|
||||
"engines": {
|
||||
"node": "0.8.10",
|
||||
"npm": "1.1.49"
|
||||
},
|
||||
"bin": {
|
||||
"haste-server": "./server.js"
|
||||
},
|
||||
"files": [
|
||||
"server.js",
|
||||
"lib",
|
||||
"static"
|
||||
],
|
||||
"directories": {
|
||||
"lib": "./lib"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"test": "mocha -r should spec/*"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"name":"Haste-server","body":"# Pastebin\r\n\r\nhaste-server is an open source pastebin. You can see an example at [hastebin.com](http://hastebin.com), and it's easy to install locally.\r\n\r\n## GitHub\r\n\r\nCheck out the project on [github](https://github.com/seejohnrun/haste-server)","tagline":"open source pastebin written in node.js","google":"","note":"Don't delete this file! It's used internally to help with page regeneration."}
|
||||
@@ -1,140 +0,0 @@
|
||||
var http = require('http');
|
||||
var url = require('url');
|
||||
var fs = require('fs');
|
||||
|
||||
var winston = require('winston');
|
||||
var connect = require('connect');
|
||||
|
||||
var DocumentHandler = require('./lib/document_handler');
|
||||
|
||||
// Load the configuration and set some defaults
|
||||
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) { }
|
||||
var detail, type;
|
||||
for (var i = 0; i < config.logging.length; i++) {
|
||||
detail = config.logging[i];
|
||||
type = detail.type;
|
||||
delete detail.type;
|
||||
winston.add(winston.transports[type], detail);
|
||||
}
|
||||
}
|
||||
|
||||
// build the store from the config on-demand - so that we don't load it
|
||||
// for statics
|
||||
if (!config.storage) {
|
||||
config.storage = { type: 'file' };
|
||||
}
|
||||
if (!config.storage.type) {
|
||||
config.storage.type = 'file';
|
||||
}
|
||||
|
||||
var Store, preferredStore;
|
||||
|
||||
if (process.env.REDISTOGO_URL) {
|
||||
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');
|
||||
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) {
|
||||
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.debug('loaded static document', { success: cb });
|
||||
}, true);
|
||||
}
|
||||
else {
|
||||
winston.warn('failed to load static document', { name: name, path: path });
|
||||
}
|
||||
}
|
||||
|
||||
// Pick up a key generator
|
||||
var pwOptions = config.keyGenerator || {};
|
||||
pwOptions.type = pwOptions.type || 'random';
|
||||
var gen = require('./lib/key_generators/' + pwOptions.type);
|
||||
var keyGenerator = new gen(pwOptions);
|
||||
|
||||
// Configure the document handler
|
||||
var documentHandler = new DocumentHandler({
|
||||
store: preferredStore,
|
||||
maxLength: config.maxLength,
|
||||
keyLength: config.keyLength,
|
||||
keyGenerator: keyGenerator
|
||||
});
|
||||
|
||||
// Set the server up with a static cache
|
||||
connect.createServer(
|
||||
// First look for api calls
|
||||
connect.router(function(app) {
|
||||
// get raw documents - support getting with extension
|
||||
app.get('/raw/:id', function(request, response, next) {
|
||||
var skipExpire = !!config.documents[request.params.id];
|
||||
var key = request.params.id.split('.')[0];
|
||||
return documentHandler.handleRawGet(key, response, skipExpire);
|
||||
});
|
||||
// add documents
|
||||
app.post('/documents', function(request, response, next) {
|
||||
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
|
||||
);
|
||||
});
|
||||
}),
|
||||
// Otherwise, static
|
||||
connect.staticCache(),
|
||||
connect.static(__dirname + '/static', { maxAge: config.staticMaxAge }),
|
||||
// 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';
|
||||
next();
|
||||
});
|
||||
}),
|
||||
connect.static(__dirname + '/static', { maxAge: config.staticMaxAge })
|
||||
).listen(config.port, config.host);
|
||||
|
||||
winston.info('listening on ' + config.host + ':' + config.port);
|
||||
@@ -1,22 +0,0 @@
|
||||
var DocumentHandler = require('../lib/document_handler');
|
||||
var Generator = require('../lib/key_generators/random');
|
||||
|
||||
describe('document_handler', function() {
|
||||
|
||||
describe('randomKey', 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);
|
||||
});
|
||||
|
||||
it('should choose a default key length', function() {
|
||||
var gen = new Generator();
|
||||
var dh = new DocumentHandler({ keyGenerator: gen });
|
||||
dh.keyLength.should.equal(DocumentHandler.defaultKeyLength);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,50 +0,0 @@
|
||||
var RedisDocumentStore = require('../lib/document_stores/redis');
|
||||
|
||||
var winston = require('winston');
|
||||
winston.remove(winston.transports.Console);
|
||||
|
||||
describe('redis_document_store', function() {
|
||||
|
||||
/* reconnect to redis on each test */
|
||||
afterEach(function() {
|
||||
if (RedisDocumentStore.client) {
|
||||
RedisDocumentStore.client.quit();
|
||||
RedisDocumentStore.client = false;
|
||||
}
|
||||
});
|
||||
|
||||
describe('set', function() {
|
||||
|
||||
it('should be able to set a key and have an expiration set', function(done) {
|
||||
var store = new RedisDocumentStore({ expire: 10 });
|
||||
store.set('hello1', 'world', function() {
|
||||
RedisDocumentStore.client.ttl('hello1', function(err, res) {
|
||||
res.should.be.above(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should not set an expiration when told not to', function(done) {
|
||||
var store = new RedisDocumentStore({ expire: 10 });
|
||||
store.set('hello2', 'world', function() {
|
||||
RedisDocumentStore.client.ttl('hello2', function(err, res) {
|
||||
res.should.equal(-1);
|
||||
done();
|
||||
});
|
||||
}, true);
|
||||
});
|
||||
|
||||
it('should not set an expiration when expiration is off', function(done) {
|
||||
var store = new RedisDocumentStore({ expire: false });
|
||||
store.set('hello3', 'world', function(worked) {
|
||||
RedisDocumentStore.client.ttl('hello3', function(err, res) {
|
||||
res.should.equal(-1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,169 +0,0 @@
|
||||
body {
|
||||
background: #002B36;
|
||||
padding: 20px 50px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
padding-right: 360px;
|
||||
}
|
||||
|
||||
#box code {
|
||||
padding: 0px;
|
||||
background: transparent !important; /* don't hide hastebox */
|
||||
}
|
||||
|
||||
/* key */
|
||||
|
||||
#key {
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
z-index: +1000; /* watch out */
|
||||
}
|
||||
|
||||
#box1 {
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
background: #00222b;
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
#box1 a.logo:hover {
|
||||
background-position: 0 bottom;
|
||||
}
|
||||
|
||||
#box2 .function {
|
||||
background: url(function-icons.png);
|
||||
width: 32px;
|
||||
height: 37px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#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;
|
||||
margin: auto;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
#box3, #messages li {
|
||||
background: #173e48;
|
||||
font-family: Helvetica, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 14px;
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
#box3 .label, #messages li {
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#box3 .shortcut {
|
||||
color: #c4dce3;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
#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; }
|
||||
|
||||
#messages {
|
||||
position:fixed;
|
||||
top:0px;
|
||||
right:138px;
|
||||
margin:0;
|
||||
padding:0;
|
||||
width:400px;
|
||||
}
|
||||
|
||||
#messages li {
|
||||
background:rgba(23,62,72,0.8);
|
||||
margin:0 auto;
|
||||
list-style:none;
|
||||
}
|
||||
|
||||
#messages li.error {
|
||||
background:rgba(102,8,0,0.8);
|
||||
}
|
||||
@@ -1,401 +0,0 @@
|
||||
///// 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, '&')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/</g, '<')
|
||||
.replace(/"/g, '"');
|
||||
};
|
||||
|
||||
// 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(err) {
|
||||
callback(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 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();
|
||||
}
|
||||
};
|
||||
|
||||
// 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');
|
||||
});
|
||||
};
|
||||
|
||||
// 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();
|
||||
};
|
||||
|
||||
// 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', cs: 'cs', 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'
|
||||
};
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
// 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('>');
|
||||
};
|
||||
|
||||
// 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.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);
|
||||
},
|
||||
action: function() {
|
||||
if (_this.$textarea.val().replace(/^\s+|\s+$/g, '') !== '') {
|
||||
_this.lockDocument();
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
$where: $('#box2 .new'),
|
||||
label: 'New',
|
||||
shortcut: function(evt) {
|
||||
return evt.ctrlKey && evt.keyCode === 78
|
||||
},
|
||||
shortcutDescription: 'control + n',
|
||||
action: function() {
|
||||
_this.newDocument(!_this.doc.key);
|
||||
}
|
||||
},
|
||||
{
|
||||
$where: $('#box2 .duplicate'),
|
||||
label: 'Duplicate & Edit',
|
||||
shortcut: function(evt) {
|
||||
return _this.doc.locked && evt.ctrlKey && evt.keyCode === 68;
|
||||
},
|
||||
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]);
|
||||
}
|
||||
};
|
||||
|
||||
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(evt) {
|
||||
$('#box3 .label').text(options.label);
|
||||
$('#box3 .shortcut').text(options.shortcutDescription || '');
|
||||
$('#box3').show();
|
||||
$(this).append($('#pointer').remove().show());
|
||||
});
|
||||
// Hide the label
|
||||
options.$where.mouseleave(function(evt) {
|
||||
$('#box3').hide();
|
||||
$('#pointer').hide();
|
||||
});
|
||||
};
|
||||
|
||||
// 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() {
|
||||
|
||||
var $key = $('#key');
|
||||
$(document).click(function () {
|
||||
$key.fadeToggle();
|
||||
});
|
||||
|
||||
$('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();
|
||||
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();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
Vendored
-1
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 2.9 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 6.1 KiB |
Vendored
-1
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 2.8 KiB |
@@ -1,79 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
|
||||
<title>hastebin</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="solarized_dark.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="application.css"/>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<ul id="messages"></ul>
|
||||
|
||||
<div id="key">
|
||||
<div id="pointer" style="display:none;"></div>
|
||||
<div id="box1">
|
||||
<a href="/about.md" class="logo"></a>
|
||||
</div>
|
||||
<div id="box2">
|
||||
<div class="save function"></div>
|
||||
<div class="new function"></div>
|
||||
<div class="duplicate function"></div>
|
||||
<div class="raw function"></div>
|
||||
<div class="twitter function"></div>
|
||||
</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;" tabindex="0"><code></code></pre>
|
||||
<textarea spellcheck="false" style="display:none;"></textarea>
|
||||
|
||||
<script type="text/javascript">
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-27329119-1']);
|
||||
_gaq.push(['_trackPageview']);
|
||||
(function() {
|
||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.6 KiB |
@@ -1,4 +0,0 @@
|
||||
User-agent: *
|
||||
Disallow: /*
|
||||
Allow: /?okparam=
|
||||
Allow: /$
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
|
||||
Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull <sourdrums@gmail.com>
|
||||
|
||||
*/
|
||||
|
||||
pre code {
|
||||
display: block; padding: 0.5em;
|
||||
background: #002b36; color: #92a0a0;
|
||||
}
|
||||
|
||||
pre .comment,
|
||||
pre .template_comment,
|
||||
pre .diff .header,
|
||||
pre .doctype,
|
||||
pre .lisp .string,
|
||||
pre .javadoc {
|
||||
color: #586e75;
|
||||
font-style: italic;
|
||||
display: inline-block;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
pre .keyword,
|
||||
pre .css .rule .keyword,
|
||||
pre .winutils,
|
||||
pre .javascript .title,
|
||||
pre .method,
|
||||
pre .addition,
|
||||
pre .css .tag,
|
||||
pre .lisp .title {
|
||||
color: #859900;
|
||||
}
|
||||
|
||||
pre .number,
|
||||
pre .command,
|
||||
pre .string,
|
||||
pre .tag .value,
|
||||
pre .phpdoc,
|
||||
pre .tex .formula,
|
||||
pre .regexp,
|
||||
pre .hexcolor {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
pre .title,
|
||||
pre .localvars,
|
||||
pre .function .title,
|
||||
pre .chunk,
|
||||
pre .decorator,
|
||||
pre .builtin,
|
||||
pre .built_in,
|
||||
pre .lisp .title,
|
||||
pre .identifier,
|
||||
pre .title .keymethods,
|
||||
pre .id,
|
||||
pre .header {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
pre .tag .title,
|
||||
pre .rules .property,
|
||||
pre .django .tag .keyword {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
pre .attribute,
|
||||
pre .variable,
|
||||
pre .instancevar,
|
||||
pre .lisp .body,
|
||||
pre .smalltalk .number,
|
||||
pre .constant,
|
||||
pre .class .title,
|
||||
pre .parent,
|
||||
pre .haskell .label {
|
||||
color: #b58900;
|
||||
}
|
||||
|
||||
pre .preprocessor,
|
||||
pre .pi,
|
||||
pre .shebang,
|
||||
pre .symbol,
|
||||
pre .diff .change,
|
||||
pre .special,
|
||||
pre .keymethods,
|
||||
pre .attr_selector,
|
||||
pre .important,
|
||||
pre .subst,
|
||||
pre .cdata {
|
||||
color: #cb4b16;
|
||||
}
|
||||
|
||||
pre .deletion {
|
||||
color: #dc322f;
|
||||
}
|
||||
|
||||
pre .tex .formula,
|
||||
pre .code {
|
||||
background: #073642;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
.highlight { background: #ffffff; }
|
||||
.highlight .c { color: #999988; font-style: italic } /* Comment */
|
||||
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
|
||||
.highlight .k { font-weight: bold } /* Keyword */
|
||||
.highlight .o { font-weight: bold } /* Operator */
|
||||
.highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */
|
||||
.highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */
|
||||
.highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */
|
||||
.highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */
|
||||
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
|
||||
.highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */
|
||||
.highlight .ge { font-style: italic } /* Generic.Emph */
|
||||
.highlight .gr { color: #aa0000 } /* Generic.Error */
|
||||
.highlight .gh { color: #999999 } /* Generic.Heading */
|
||||
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
|
||||
.highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */
|
||||
.highlight .go { color: #888888 } /* Generic.Output */
|
||||
.highlight .gp { color: #555555 } /* Generic.Prompt */
|
||||
.highlight .gs { font-weight: bold } /* Generic.Strong */
|
||||
.highlight .gu { color: #800080; font-weight: bold; } /* Generic.Subheading */
|
||||
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
|
||||
.highlight .kc { font-weight: bold } /* Keyword.Constant */
|
||||
.highlight .kd { font-weight: bold } /* Keyword.Declaration */
|
||||
.highlight .kn { font-weight: bold } /* Keyword.Namespace */
|
||||
.highlight .kp { font-weight: bold } /* Keyword.Pseudo */
|
||||
.highlight .kr { font-weight: bold } /* Keyword.Reserved */
|
||||
.highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */
|
||||
.highlight .m { color: #009999 } /* Literal.Number */
|
||||
.highlight .s { color: #d14 } /* Literal.String */
|
||||
.highlight .na { color: #008080 } /* Name.Attribute */
|
||||
.highlight .nb { color: #0086B3 } /* Name.Builtin */
|
||||
.highlight .nc { color: #445588; font-weight: bold } /* Name.Class */
|
||||
.highlight .no { color: #008080 } /* Name.Constant */
|
||||
.highlight .ni { color: #800080 } /* Name.Entity */
|
||||
.highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */
|
||||
.highlight .nf { color: #990000; font-weight: bold } /* Name.Function */
|
||||
.highlight .nn { color: #555555 } /* Name.Namespace */
|
||||
.highlight .nt { color: #000080 } /* Name.Tag */
|
||||
.highlight .nv { color: #008080 } /* Name.Variable */
|
||||
.highlight .ow { font-weight: bold } /* Operator.Word */
|
||||
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
|
||||
.highlight .mf { color: #009999 } /* Literal.Number.Float */
|
||||
.highlight .mh { color: #009999 } /* Literal.Number.Hex */
|
||||
.highlight .mi { color: #009999 } /* Literal.Number.Integer */
|
||||
.highlight .mo { color: #009999 } /* Literal.Number.Oct */
|
||||
.highlight .sb { color: #d14 } /* Literal.String.Backtick */
|
||||
.highlight .sc { color: #d14 } /* Literal.String.Char */
|
||||
.highlight .sd { color: #d14 } /* Literal.String.Doc */
|
||||
.highlight .s2 { color: #d14 } /* Literal.String.Double */
|
||||
.highlight .se { color: #d14 } /* Literal.String.Escape */
|
||||
.highlight .sh { color: #d14 } /* Literal.String.Heredoc */
|
||||
.highlight .si { color: #d14 } /* Literal.String.Interpol */
|
||||
.highlight .sx { color: #d14 } /* Literal.String.Other */
|
||||
.highlight .sr { color: #009926 } /* Literal.String.Regex */
|
||||
.highlight .s1 { color: #d14 } /* Literal.String.Single */
|
||||
.highlight .ss { color: #990073 } /* Literal.String.Symbol */
|
||||
.highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */
|
||||
.highlight .vc { color: #008080 } /* Name.Variable.Class */
|
||||
.highlight .vg { color: #008080 } /* Name.Variable.Global */
|
||||
.highlight .vi { color: #008080 } /* Name.Variable.Instance */
|
||||
.highlight .il { color: #009999 } /* Literal.Number.Integer.Long */
|
||||
|
||||
.type-csharp .highlight .k { color: #0000FF }
|
||||
.type-csharp .highlight .kt { color: #0000FF }
|
||||
.type-csharp .highlight .nf { color: #000000; font-weight: normal }
|
||||
.type-csharp .highlight .nc { color: #2B91AF }
|
||||
.type-csharp .highlight .nn { color: #000000 }
|
||||
.type-csharp .highlight .s { color: #A31515 }
|
||||
.type-csharp .highlight .sc { color: #A31515 }
|
||||
@@ -0,0 +1,255 @@
|
||||
@import url(https://fonts.googleapis.com/css?family=Lato:300italic,700italic,300,700);
|
||||
|
||||
body {
|
||||
padding:50px;
|
||||
font:14px/1.5 Lato, "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
color:#777;
|
||||
font-weight:300;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color:#222;
|
||||
margin:0 0 20px;
|
||||
}
|
||||
|
||||
p, ul, ol, table, pre, dl {
|
||||
margin:0 0 20px;
|
||||
}
|
||||
|
||||
h1, h2, h3 {
|
||||
line-height:1.1;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size:28px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color:#393939;
|
||||
}
|
||||
|
||||
h3, h4, h5, h6 {
|
||||
color:#494949;
|
||||
}
|
||||
|
||||
a {
|
||||
color:#39c;
|
||||
font-weight:400;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
a small {
|
||||
font-size:11px;
|
||||
color:#777;
|
||||
margin-top:-0.6em;
|
||||
display:block;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
width:860px;
|
||||
margin:0 auto;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left:1px solid #e5e5e5;
|
||||
margin:0;
|
||||
padding:0 0 0 20px;
|
||||
font-style:italic;
|
||||
}
|
||||
|
||||
code, pre {
|
||||
font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal;
|
||||
color:#333;
|
||||
font-size:12px;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding:8px 15px;
|
||||
background: #f8f8f8;
|
||||
border-radius:5px;
|
||||
border:1px solid #e5e5e5;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
table {
|
||||
width:100%;
|
||||
border-collapse:collapse;
|
||||
}
|
||||
|
||||
th, td {
|
||||
text-align:left;
|
||||
padding:5px 10px;
|
||||
border-bottom:1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
dt {
|
||||
color:#444;
|
||||
font-weight:700;
|
||||
}
|
||||
|
||||
th {
|
||||
color:#444;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width:100%;
|
||||
}
|
||||
|
||||
header {
|
||||
width:270px;
|
||||
float:left;
|
||||
position:fixed;
|
||||
}
|
||||
|
||||
header ul {
|
||||
list-style:none;
|
||||
height:40px;
|
||||
|
||||
padding:0;
|
||||
|
||||
background: #eee;
|
||||
background: -moz-linear-gradient(top, #f8f8f8 0%, #dddddd 100%);
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f8f8f8), color-stop(100%,#dddddd));
|
||||
background: -webkit-linear-gradient(top, #f8f8f8 0%,#dddddd 100%);
|
||||
background: -o-linear-gradient(top, #f8f8f8 0%,#dddddd 100%);
|
||||
background: -ms-linear-gradient(top, #f8f8f8 0%,#dddddd 100%);
|
||||
background: linear-gradient(top, #f8f8f8 0%,#dddddd 100%);
|
||||
|
||||
border-radius:5px;
|
||||
border:1px solid #d2d2d2;
|
||||
box-shadow:inset #fff 0 1px 0, inset rgba(0,0,0,0.03) 0 -1px 0;
|
||||
width:270px;
|
||||
}
|
||||
|
||||
header li {
|
||||
width:89px;
|
||||
float:left;
|
||||
border-right:1px solid #d2d2d2;
|
||||
height:40px;
|
||||
}
|
||||
|
||||
header ul a {
|
||||
line-height:1;
|
||||
font-size:11px;
|
||||
color:#999;
|
||||
display:block;
|
||||
text-align:center;
|
||||
padding-top:6px;
|
||||
height:40px;
|
||||
}
|
||||
|
||||
strong {
|
||||
color:#222;
|
||||
font-weight:700;
|
||||
}
|
||||
|
||||
header ul li + li {
|
||||
width:88px;
|
||||
border-left:1px solid #fff;
|
||||
}
|
||||
|
||||
header ul li + li + li {
|
||||
border-right:none;
|
||||
width:89px;
|
||||
}
|
||||
|
||||
header ul a strong {
|
||||
font-size:14px;
|
||||
display:block;
|
||||
color:#222;
|
||||
}
|
||||
|
||||
section {
|
||||
width:500px;
|
||||
float:right;
|
||||
padding-bottom:50px;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size:11px;
|
||||
}
|
||||
|
||||
hr {
|
||||
border:0;
|
||||
background:#e5e5e5;
|
||||
height:1px;
|
||||
margin:0 0 20px;
|
||||
}
|
||||
|
||||
footer {
|
||||
width:270px;
|
||||
float:left;
|
||||
position:fixed;
|
||||
bottom:50px;
|
||||
}
|
||||
|
||||
@media print, screen and (max-width: 960px) {
|
||||
|
||||
div.wrapper {
|
||||
width:auto;
|
||||
margin:0;
|
||||
}
|
||||
|
||||
header, section, footer {
|
||||
float:none;
|
||||
position:static;
|
||||
width:auto;
|
||||
}
|
||||
|
||||
header {
|
||||
padding-right:320px;
|
||||
}
|
||||
|
||||
section {
|
||||
border:1px solid #e5e5e5;
|
||||
border-width:1px 0;
|
||||
padding:20px 0;
|
||||
margin:0 0 20px;
|
||||
}
|
||||
|
||||
header a small {
|
||||
display:inline;
|
||||
}
|
||||
|
||||
header ul {
|
||||
position:absolute;
|
||||
right:50px;
|
||||
top:52px;
|
||||
}
|
||||
}
|
||||
|
||||
@media print, screen and (max-width: 720px) {
|
||||
body {
|
||||
word-wrap:break-word;
|
||||
}
|
||||
|
||||
header {
|
||||
padding:0;
|
||||
}
|
||||
|
||||
header ul, header p.view {
|
||||
position:static;
|
||||
}
|
||||
|
||||
pre, code {
|
||||
word-wrap:normal;
|
||||
}
|
||||
}
|
||||
|
||||
@media print, screen and (max-width: 480px) {
|
||||
body {
|
||||
padding:15px;
|
||||
}
|
||||
|
||||
header ul {
|
||||
display:none;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
padding:0.4in;
|
||||
font-size:12pt;
|
||||
color:#444;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user