Latest Code

This commit is contained in:
Dobie Wollert
2015-04-06 03:28:20 -04:00
parent 966152a631
commit d3089dcd17
105 changed files with 8731 additions and 96 deletions

View File

@ -4,12 +4,18 @@ var mongoose = require('mongoose'),
var md5 = require('MD5');
var fs = require('fs');
var markdown = require('markdown').markdown;
var log = require('log4node');
exports.index = function(req, res) {
var criteria = {};
var page = req.param('page');
if (page) {
criteria = { pages: page };
}
log.info('posts.index');
var query = Post.find()
var query = Post.find(criteria)
.populate('author', 'name')
.sort('-createdOn')
.exec(function(err, results) {
@ -32,19 +38,64 @@ exports.get = function(req, res, next) {
});
};
function renderHtml(input) {
if (input) {
try {
return markdown.toHTML(input);
} catch (err) {
console.log('Failed to render html', err);
return input;
}
} else {
return input;
}
}
function cleanTags(tags) {
if (!tags) {
return [];
}
if (!Array.isArray(tags)) {
tags = [tags];
}
var results = [];
for (var i = 0; i < tags.length; i++) {
var tag = tags[i].toString()
.replace(/^#/, '')
.replace(/(\W|\d)/g, '$1 ')
.replace(/\b\w+/g, function(txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
})
.replace(/\s/g, '')
if (tag) {
results.push(tag);
}
}
return results;
}
exports.create = function(req, res, next) {
log.info('posts.create %j', req.body);
var post = new Post({
title: req.body.title,
preview: req.body.preview,
previewHtml: renderHtml(req.body.preview),
details: req.body.details,
detailsHtml: renderHtml(req.body.details),
image: req.body.image,
gallery: req.body.gallery,
status: req.body.status,
createdOn: req.body.createdOn,
postedOn: req.body.postedOn,
author: req.user
author: req.user,
pages: req.body.pages,
tags: cleanTags(req.body.tags),
});
return post.save(function(err) {
@ -60,13 +111,17 @@ exports.update = function(req, res, next) {
console.log('updating post');
return Post.findById(id, function(err, post) {
post.title = req.body.title;
post.preview = req.body.preview;
post.details = req.body.details;
post.image = req.body.image;
post.gallery = req.body.gallery;
post.status = req.body.status;
post.postedOn = req.body.postedOn;
post.title = req.body.title;
post.preview = req.body.preview;
post.previewHtml = renderHtml(req.body.preview);
post.details = req.body.details;
post.detailsHtml = renderHtml(req.body.details);
post.image = req.body.image;
post.gallery = req.body.gallery;
post.status = req.body.status;
post.postedOn = req.body.postedOn;
post.pages = req.body.pages;
post.tags = cleanTags(req.body.tags);
return post.save(function(err) {
if (err)

View File

@ -130,18 +130,47 @@ module.exports = function(config, calendar) {
if (!notify)
return callback(null);
var to = generateToLine(techs).concat(req.body.emails);
if (!to)
return callback(null);
var description = generateDescription(client, workorder, req.user, null, techs);
var techDescription = appendNotes(description, client);
server.send({
text: generateDescription(client, workorder, req.user, null, techs),
from: config.email.user,
to: to,
subject: 'Workorder created: ' + workorder.biomedId
}, function(err, message) {
callback(err);
});
var to = req.body.emails;
var techTo = generateToLine(techs);
var subject = 'Workorder created: ' + workorder.biomedId;
console.log('-------------------------');
console.log(to);
async.waterfall([
function(cb) {
if (to && to.length > 0) {
var msg = {
text: description,
from: config.email.user,
to: to,
subject: subject
};
console.log(msg);
server.send(msg, function(err, message) { cb(err); });
} else {
cb();
}
},
function(cb) {
if (techTo) {
var msg = {
text: techDescription,
from: config.email.user,
to: techTo,
subject: subject
};
console.log(msg);
server.send(msg, function(err, message) { cb(err); });
} else {
cb();
}
}
], callback);
},
function(callback) {
workorder.save(function(err, result) { callback(err, result); });
@ -265,18 +294,45 @@ module.exports = function(config, calendar) {
if (!notify)
return callback(null);
var to = generateToLine(techs);
if (!to)
return callback(null);
server.send({
text: generateDescription(client, workorder, createdBy, modifiedBy, techs),
from: config.email.user,
to: to,
subject: 'Workorder updated: ' + workorder.biomedId
}, function(err, message) {
callback(err);
});
var description = generateDescription(client, workorder, createdBy, modifiedBy, techs);
var techDescription = appendNotes(description, client);
var to = req.body.emails;
var techTo = generateToLine(techs);
var subject = 'Workorder updated: ' + workorder.biomedId;
async.waterfall([
function(cb) {
if (to && to.length > 0) {
var msg = {
text: description,
from: config.email.user,
to: to,
subject: subject
};
console.log(msg);
server.send(msg, function(err, message) { cb(err); });
} else {
cb();
}
},
function(cb) {
if (techTo) {
var msg = {
text: techDescription,
from: config.email.user,
to: techTo,
subject: subject
};
console.log(msg);
server.send(msg, function(err, message) { cb(err); });
} else {
cb();
}
}
], callback);
},
function(callback) {
workorder.save(function(err) {
@ -332,6 +388,26 @@ function generateLocation(client) {
return sprintf("%(street1)s %(street2)s %(city)s, %(state)s. %(zip)s", data);
}
function appendNotes(message, client) {
var template =
"%(message)s\n" +
"Tech Notes:\n" +
" %(notes)s\n" +
"\n";
if (client.notes && client.notes['tech']) {
var resources = {
message: message || '',
notes: client.notes['tech'] || ''
};
return sprintf(template, resources);
} else {
return message;
}
}
function generateDescription(client, workorder, createdBy, modifiedBy) {
var template =
"Workorder ID:\n" +

View File

@ -5,13 +5,17 @@ var mongoose = require('mongoose'),
var postSchema = new Schema({
title: { type: String },
preview: { type: String },
previewHtml: { type: String },
details: { type: String },
detailsHtml: { type: String },
image: { type: String },
gallery: [{ type: String }],
status: { type: String },
createdOn: { type: Date },
postedOn: { type: Date },
author: { type: ObjectId, ref: 'User' }
author: { type: ObjectId, ref: 'User' },
tags: [{ type: String }],
pages: [{ type: String }]
});
var Post = module.exports = mongoose.model('Post', postSchema);

View File

@ -63,6 +63,8 @@ html(lang="en", ng-app="biomed", ng-controller="biomed.PageCtrl")
a(href='/admin')
i.icon-wrench
| Admin
li.day-of-year
{{dayOfYear}}
.container-fluid
ng-view
!{js}

View File

@ -38,7 +38,7 @@ html(lang="en", ng-app="site", ng-controller="site.PageCtrl")
.controls
.dropzone(dropzone='titleImageOptions')
.control-group
.control-group(ng-show='model.image')
label.control-label Gallery
.controls
.dropzone(dropzone='galleryImageOptions')

View File

@ -1,4 +1,5 @@
var express = require('express');
var ClusterStore = require('strong-cluster-connect-store')(express.session);
module.exports = function(app, config, passport, piler) {
app.set('showStackError', true);
@ -16,9 +17,7 @@ module.exports = function(app, config, passport, piler) {
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.session({
secret: 'atlantic_biomed_server_secret'
}));
app.use(express.session({ store: new ClusterStore(), secret: 'atlantic_biomed_server_secret' }));
// use passport session
app.use(passport.initialize());

22
launcher.js Normal file
View File

@ -0,0 +1,22 @@
var cluster = require('cluster');
require('strong-cluster-connect-store').setup();
cluster.setupMaster({
exec: 'server.js'
});
var cpus = 1;
for (var i = 0; i < cpus; i++) {
cluster.fork();
}
cluster.on('online', function(worker) {
console.log('worker ' + worker.process.pid + ' started.');
});
cluster.on('exit', function(worker, code, signal) {
console.log('worker ' + worker.process.pid + ' died');
cluster.fork();
});

1
node_modules/.bin/md2html generated vendored Symbolic link
View File

@ -0,0 +1 @@
../markdown/bin/md2html.js

2
node_modules/markdown/.npmignore generated vendored Normal file
View File

@ -0,0 +1,2 @@
.seed.yml
test

7
node_modules/markdown/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,7 @@
language: node_js
node_js:
- "0.6"
- "0.8"
- "0.9"
- "0.10"
- "0.11"

35
node_modules/markdown/Changes.markdown generated vendored Normal file
View File

@ -0,0 +1,35 @@
# Changelog for markdown
## v0.5.0 - 2013-07-26
There might be other bug fixes then the ones listed - I've been a bit lax at
updating the changes file, sorry :(
- Fix 'undefined' appearing in output for some cases with blockquotes
- Fix (multiple) global variable leaks. Ooops
- Fix IE8 issues (#68, #74, #97)
- Fix IE8 issue (#86)
- Handle windows line endings (#58)
- Allow spaces in img/link paths (#48)
- Add explicit text of the license to the readme (#74)
- Style tweaks by Xhmikosr (#83, #81, #82)
- Build now tested by TravisCI thanks to sebs (#85)
- Fix 'cuddled' header parsing (#94)
- Fix images inside links mistakenly requiring a title attribute to parse
correctly (#71)
## v0.4.0 - 2012-06-09
- Fix for anchors enclosed by parenthesis (issue #46)
- `npm test` will now run the entire test suite cleanly. (switch tests over to
node-tap). (#21)
- Allow inline elements to appear inside link text (#27)
- Improve link parsing when link is inside parenthesis (#38)
- Actually render image references (#36)
- Improve link parsing when multiple on a line (#5)
- Make it work in IE7/8 (#37)
- Fix blockquote merging/implicit conversion between string/String (#44, #24)
- md2html can now process stdin (#43)
- Fix jslint warnings (#42)
- Fix to correctly render self-closing tags (#40, #35, #28)

185
node_modules/markdown/README.markdown generated vendored Normal file
View File

@ -0,0 +1,185 @@
# markdown-js
Yet another markdown parser, this time for JavaScript. There's a few
options that precede this project but they all treat markdown to HTML
conversion as a single step process. You pass markdown in and get HTML
out, end of story. We had some pretty particular views on how the
process should actually look, which include:
* producing well-formed HTML. This means that `em` and `strong` nesting
is important, as is the ability to output as both HTML and XHTML
* having an intermediate representation to allow processing of parsed
data (we in fact have two, both [JsonML]: a markdown tree and an HTML tree)
* being easily extensible to add new dialects without having to
rewrite the entire parsing mechanics
* having a good test suite. The only test suites we could find tested
massive blocks of input, and passing depended on outputting the HTML
with exactly the same whitespace as the original implementation
[JsonML]: http://jsonml.org/ "JSON Markup Language"
## Installation
Just the `markdown` library:
npm install markdown
Optionally, install `md2html` into your path
npm install -g markdown
## Usage
### Node
The simple way to use it with node is:
```js
var markdown = require( "markdown" ).markdown;
console.log( markdown.toHTML( "Hello *World*!" ) );
```
### Browser
It also works in a browser; here is a complete example:
```html
<!DOCTYPE html>
<html>
<body>
<textarea id="text-input" oninput="this.editor.update()"
rows="6" cols="60">Type **Markdown** here.</textarea>
<div id="preview"> </div>
<script src="lib/markdown.js"></script>
<script>
function Editor(input, preview) {
this.update = function () {
preview.innerHTML = markdown.toHTML(input.value);
};
input.editor = this;
this.update();
}
var $ = function (id) { return document.getElementById(id); };
new Editor($("text-input"), $("preview"));
</script>
</body>
</html>
```
### Command line
Assuming you've installed the `md2html` script (see Installation,
above), you can convert markdown to html:
```bash
# read from a file
md2html /path/to/doc.md > /path/to/doc.html
# or from stdin
echo 'Hello *World*!' | md2html
```
### More options
If you want more control check out the documentation in
[lib/markdown.js] which details all the methods and parameters
available (including examples!). One day we'll get the docs generated
and hosted somewhere for nicer browsing.
[lib/markdown.js]: http://github.com/evilstreak/markdown-js/blob/master/lib/markdown.js
Meanwhile, here's an example of using the multi-step processing to
make wiki-style linking work by filling in missing link references:
```js
var md = require( "markdown" ).markdown,
text = "[Markdown] is a simple text-based [markup language]\n" +
"created by [John Gruber]\n\n" +
"[John Gruber]: http://daringfireball.net";
// parse the markdown into a tree and grab the link references
var tree = md.parse( text ),
refs = tree[ 1 ].references;
// iterate through the tree finding link references
( function find_link_refs( jsonml ) {
if ( jsonml[ 0 ] === "link_ref" ) {
var ref = jsonml[ 1 ].ref;
// if there's no reference, define a wiki link
if ( !refs[ ref ] ) {
refs[ ref ] = {
href: "http://en.wikipedia.org/wiki/" + ref.replace(/\s+/, "_" )
};
}
}
else if ( Array.isArray( jsonml[ 1 ] ) ) {
jsonml[ 1 ].forEach( find_link_refs );
}
else if ( Array.isArray( jsonml[ 2 ] ) ) {
jsonml[ 2 ].forEach( find_link_refs );
}
} )( tree );
// convert the tree into html
var html = md.renderJsonML( md.toHTMLTree( tree ) );
console.log( html );
```
## Intermediate Representation
Internally the process to convert a chunk of markdown into a chunk of
HTML has three steps:
1. Parse the markdown into a JsonML tree. Any references found in the
parsing are stored in the attribute hash of the root node under the
key `references`.
2. Convert the markdown tree into an HTML tree. Rename any nodes that
need it (`bulletlist` to `ul` for example) and lookup any references
used by links or images. Remove the references attribute once done.
3. Stringify the HTML tree being careful not to wreck whitespace where
whitespace is important (surrounding inline elements for example).
Each step of this process can be called individually if you need to do
some processing or modification of the data at an intermediate stage.
For example, you may want to grab a list of all URLs linked to in the
document before rendering it to HTML which you could do by recursing
through the HTML tree looking for `a` nodes.
## Running tests
To run the tests under node you will need tap installed (it's listed as a
`devDependencies` so `npm install` from the checkout should be enough), then do
$ npm test
## Contributing
Do the usual github fork and pull request dance. Add yourself to the
contributors section of [package.json](/package.json) too if you want to.
## License
Released under the MIT license.
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.

52
node_modules/markdown/bin/md2html.js generated vendored Executable file
View File

@ -0,0 +1,52 @@
#!/usr/bin/env node
(function () {
"use strict";
var fs = require("fs")
, markdown = require("markdown").markdown
, nopt = require("nopt")
, stream
, opts
, buffer = ""
;
opts = nopt(
{ "dialect": [ "Gruber", "Maruku"]
, "help": Boolean
}
);
if (opts.help) {
var name = process.argv[1].split("/").pop()
console.warn( require("util").format(
"usage: %s [--dialect=DIALECT] FILE\n\nValid dialects are Gruber (the default) or Maruku",
name
) );
process.exit(0);
}
var fullpath = opts.argv.remain[0];
if (fullpath && fullpath !== "-") {
stream = fs.createReadStream(fullpath);
} else {
stream = process.stdin;
}
stream.resume();
stream.setEncoding("utf8");
stream.on("error", function(error) {
console.error(error.toString());
process.exit(1);
});
stream.on("data", function(data) {
buffer += data;
});
stream.on("end", function() {
var html = markdown.toHTML(buffer, opts.dialect);
console.log(html);
});
}())

3
node_modules/markdown/lib/index.js generated vendored Normal file
View File

@ -0,0 +1,3 @@
// super simple module for the most common nodejs use case.
exports.markdown = require("./markdown");
exports.parse = exports.markdown.toHTML;

1725
node_modules/markdown/lib/markdown.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

10
node_modules/markdown/markdown-js.sublime-project generated vendored Normal file
View File

@ -0,0 +1,10 @@
{
"folders":
[
{
"path": "/Users/ash/code/js/markdown-js",
"folder_exclude_patterns": ["node_modules"],
"file_exclude_patterns": ["*.sublime-*"]
}
]
}

1993
node_modules/markdown/markdown-js.sublime-workspace generated vendored Normal file

File diff suppressed because it is too large Load Diff

1
node_modules/markdown/node_modules/.bin/nopt generated vendored Symbolic link
View File

@ -0,0 +1 @@
../nopt/bin/nopt.js

1
node_modules/markdown/node_modules/nopt/.npmignore generated vendored Normal file
View File

@ -0,0 +1 @@
node_modules

23
node_modules/markdown/node_modules/nopt/LICENSE generated vendored Normal file
View File

@ -0,0 +1,23 @@
Copyright 2009, 2010, 2011 Isaac Z. Schlueter.
All rights reserved.
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.

210
node_modules/markdown/node_modules/nopt/README.md generated vendored Normal file
View File

@ -0,0 +1,210 @@
If you want to write an option parser, and have it be good, there are
two ways to do it. The Right Way, and the Wrong Way.
The Wrong Way is to sit down and write an option parser. We've all done
that.
The Right Way is to write some complex configurable program with so many
options that you go half-insane just trying to manage them all, and put
it off with duct-tape solutions until you see exactly to the core of the
problem, and finally snap and write an awesome option parser.
If you want to write an option parser, don't write an option parser.
Write a package manager, or a source control system, or a service
restarter, or an operating system. You probably won't end up with a
good one of those, but if you don't give up, and you are relentless and
diligent enough in your procrastination, you may just end up with a very
nice option parser.
## USAGE
// my-program.js
var nopt = require("nopt")
, Stream = require("stream").Stream
, path = require("path")
, knownOpts = { "foo" : [String, null]
, "bar" : [Stream, Number]
, "baz" : path
, "bloo" : [ "big", "medium", "small" ]
, "flag" : Boolean
, "pick" : Boolean
, "many" : [String, Array]
}
, shortHands = { "foofoo" : ["--foo", "Mr. Foo"]
, "b7" : ["--bar", "7"]
, "m" : ["--bloo", "medium"]
, "p" : ["--pick"]
, "f" : ["--flag"]
}
// everything is optional.
// knownOpts and shorthands default to {}
// arg list defaults to process.argv
// slice defaults to 2
, parsed = nopt(knownOpts, shortHands, process.argv, 2)
console.log(parsed)
This would give you support for any of the following:
```bash
$ node my-program.js --foo "blerp" --no-flag
{ "foo" : "blerp", "flag" : false }
$ node my-program.js ---bar 7 --foo "Mr. Hand" --flag
{ bar: 7, foo: "Mr. Hand", flag: true }
$ node my-program.js --foo "blerp" -f -----p
{ foo: "blerp", flag: true, pick: true }
$ node my-program.js -fp --foofoo
{ foo: "Mr. Foo", flag: true, pick: true }
$ node my-program.js --foofoo -- -fp # -- stops the flag parsing.
{ foo: "Mr. Foo", argv: { remain: ["-fp"] } }
$ node my-program.js --blatzk 1000 -fp # unknown opts are ok.
{ blatzk: 1000, flag: true, pick: true }
$ node my-program.js --blatzk true -fp # but they need a value
{ blatzk: true, flag: true, pick: true }
$ node my-program.js --no-blatzk -fp # unless they start with "no-"
{ blatzk: false, flag: true, pick: true }
$ node my-program.js --baz b/a/z # known paths are resolved.
{ baz: "/Users/isaacs/b/a/z" }
# if Array is one of the types, then it can take many
# values, and will always be an array. The other types provided
# specify what types are allowed in the list.
$ node my-program.js --many 1 --many null --many foo
{ many: ["1", "null", "foo"] }
$ node my-program.js --many foo
{ many: ["foo"] }
```
Read the tests at the bottom of `lib/nopt.js` for more examples of
what this puppy can do.
## Types
The following types are supported, and defined on `nopt.typeDefs`
* String: A normal string. No parsing is done.
* path: A file system path. Gets resolved against cwd if not absolute.
* url: A url. If it doesn't parse, it isn't accepted.
* Number: Must be numeric.
* Date: Must parse as a date. If it does, and `Date` is one of the options,
then it will return a Date object, not a string.
* Boolean: Must be either `true` or `false`. If an option is a boolean,
then it does not need a value, and its presence will imply `true` as
the value. To negate boolean flags, do `--no-whatever` or `--whatever
false`
* NaN: Means that the option is strictly not allowed. Any value will
fail.
* Stream: An object matching the "Stream" class in node. Valuable
for use when validating programmatically. (npm uses this to let you
supply any WriteStream on the `outfd` and `logfd` config options.)
* Array: If `Array` is specified as one of the types, then the value
will be parsed as a list of options. This means that multiple values
can be specified, and that the value will always be an array.
If a type is an array of values not on this list, then those are
considered valid values. For instance, in the example above, the
`--bloo` option can only be one of `"big"`, `"medium"`, or `"small"`,
and any other value will be rejected.
When parsing unknown fields, `"true"`, `"false"`, and `"null"` will be
interpreted as their JavaScript equivalents, and numeric values will be
interpreted as a number.
You can also mix types and values, or multiple types, in a list. For
instance `{ blah: [Number, null] }` would allow a value to be set to
either a Number or null. When types are ordered, this implies a
preference, and the first type that can be used to properly interpret
the value will be used.
To define a new type, add it to `nopt.typeDefs`. Each item in that
hash is an object with a `type` member and a `validate` method. The
`type` member is an object that matches what goes in the type list. The
`validate` method is a function that gets called with `validate(data,
key, val)`. Validate methods should assign `data[key]` to the valid
value of `val` if it can be handled properly, or return boolean
`false` if it cannot.
You can also call `nopt.clean(data, types, typeDefs)` to clean up a
config object and remove its invalid properties.
## Error Handling
By default, nopt outputs a warning to standard error when invalid
options are found. You can change this behavior by assigning a method
to `nopt.invalidHandler`. This method will be called with
the offending `nopt.invalidHandler(key, val, types)`.
If no `nopt.invalidHandler` is assigned, then it will console.error
its whining. If it is assigned to boolean `false` then the warning is
suppressed.
## Abbreviations
Yes, they are supported. If you define options like this:
```javascript
{ "foolhardyelephants" : Boolean
, "pileofmonkeys" : Boolean }
```
Then this will work:
```bash
node program.js --foolhar --pil
node program.js --no-f --pileofmon
# etc.
```
## Shorthands
Shorthands are a hash of shorter option names to a snippet of args that
they expand to.
If multiple one-character shorthands are all combined, and the
combination does not unambiguously match any other option or shorthand,
then they will be broken up into their constituent parts. For example:
```json
{ "s" : ["--loglevel", "silent"]
, "g" : "--global"
, "f" : "--force"
, "p" : "--parseable"
, "l" : "--long"
}
```
```bash
npm ls -sgflp
# just like doing this:
npm ls --loglevel silent --global --force --long --parseable
```
## The Rest of the args
The config object returned by nopt is given a special member called
`argv`, which is an object with the following fields:
* `remain`: The remaining args after all the parsing has occurred.
* `original`: The args as they originally appeared.
* `cooked`: The args after flags and shorthands are expanded.
## Slicing
Node programs are called with more or less the exact argv as it appears
in C land, after the v8 and node-specific options have been plucked off.
As such, `argv[0]` is always `node` and `argv[1]` is always the
JavaScript program being run.
That's usually not very useful to you. So they're sliced off by
default. If you want them, then you can pass in `0` as the last
argument, or any other number that you'd like to slice off the start of
the list.

51
node_modules/markdown/node_modules/nopt/bin/nopt.js generated vendored Executable file
View File

@ -0,0 +1,51 @@
#!/usr/bin/env node
var nopt = require("../lib/nopt")
, types = { num: Number
, bool: Boolean
, help: Boolean
, list: Array
, "num-list": [Number, Array]
, "str-list": [String, Array]
, "bool-list": [Boolean, Array]
, str: String
, clear: Boolean
, config: Boolean
, length: Number
}
, shorthands = { s: [ "--str", "astring" ]
, b: [ "--bool" ]
, nb: [ "--no-bool" ]
, tft: [ "--bool-list", "--no-bool-list", "--bool-list", "true" ]
, "?": ["--help"]
, h: ["--help"]
, H: ["--help"]
, n: [ "--num", "125" ]
, c: ["--config"]
, l: ["--length"]
}
, parsed = nopt( types
, shorthands
, process.argv
, 2 )
console.log("parsed", parsed)
if (parsed.help) {
console.log("")
console.log("nopt cli tester")
console.log("")
console.log("types")
console.log(Object.keys(types).map(function M (t) {
var type = types[t]
if (Array.isArray(type)) {
return [t, type.map(function (type) { return type.name })]
}
return [t, type && type.name]
}).reduce(function (s, i) {
s[i[0]] = i[1]
return s
}, {}))
console.log("")
console.log("shorthands")
console.log(shorthands)
}

View File

@ -0,0 +1,30 @@
#!/usr/bin/env node
//process.env.DEBUG_NOPT = 1
// my-program.js
var nopt = require("../lib/nopt")
, Stream = require("stream").Stream
, path = require("path")
, knownOpts = { "foo" : [String, null]
, "bar" : [Stream, Number]
, "baz" : path
, "bloo" : [ "big", "medium", "small" ]
, "flag" : Boolean
, "pick" : Boolean
}
, shortHands = { "foofoo" : ["--foo", "Mr. Foo"]
, "b7" : ["--bar", "7"]
, "m" : ["--bloo", "medium"]
, "p" : ["--pick"]
, "f" : ["--flag", "true"]
, "g" : ["--flag"]
, "s" : "--flag"
}
// everything is optional.
// knownOpts and shorthands default to {}
// arg list defaults to process.argv
// slice defaults to 2
, parsed = nopt(knownOpts, shortHands, process.argv, 2)
console.log("parsed =\n"+ require("util").inspect(parsed))

612
node_modules/markdown/node_modules/nopt/lib/nopt.js generated vendored Normal file
View File

@ -0,0 +1,612 @@
// info about each config option.
var debug = process.env.DEBUG_NOPT || process.env.NOPT_DEBUG
? function () { console.error.apply(console, arguments) }
: function () {}
var url = require("url")
, path = require("path")
, Stream = require("stream").Stream
, abbrev = require("abbrev")
module.exports = exports = nopt
exports.clean = clean
exports.typeDefs =
{ String : { type: String, validate: validateString }
, Boolean : { type: Boolean, validate: validateBoolean }
, url : { type: url, validate: validateUrl }
, Number : { type: Number, validate: validateNumber }
, path : { type: path, validate: validatePath }
, Stream : { type: Stream, validate: validateStream }
, Date : { type: Date, validate: validateDate }
}
function nopt (types, shorthands, args, slice) {
args = args || process.argv
types = types || {}
shorthands = shorthands || {}
if (typeof slice !== "number") slice = 2
debug(types, shorthands, args, slice)
args = args.slice(slice)
var data = {}
, key
, remain = []
, cooked = args
, original = args.slice(0)
parse(args, data, remain, types, shorthands)
// now data is full
clean(data, types, exports.typeDefs)
data.argv = {remain:remain,cooked:cooked,original:original}
Object.defineProperty(data.argv, 'toString', { value: function () {
return this.original.map(JSON.stringify).join(" ")
}, enumerable: false })
return data
}
function clean (data, types, typeDefs) {
typeDefs = typeDefs || exports.typeDefs
var remove = {}
, typeDefault = [false, true, null, String, Number, Array]
Object.keys(data).forEach(function (k) {
if (k === "argv") return
var val = data[k]
, isArray = Array.isArray(val)
, type = types[k]
if (!isArray) val = [val]
if (!type) type = typeDefault
if (type === Array) type = typeDefault.concat(Array)
if (!Array.isArray(type)) type = [type]
debug("val=%j", val)
debug("types=", type)
val = val.map(function (val) {
// if it's an unknown value, then parse false/true/null/numbers/dates
if (typeof val === "string") {
debug("string %j", val)
val = val.trim()
if ((val === "null" && ~type.indexOf(null))
|| (val === "true" &&
(~type.indexOf(true) || ~type.indexOf(Boolean)))
|| (val === "false" &&
(~type.indexOf(false) || ~type.indexOf(Boolean)))) {
val = JSON.parse(val)
debug("jsonable %j", val)
} else if (~type.indexOf(Number) && !isNaN(val)) {
debug("convert to number", val)
val = +val
} else if (~type.indexOf(Date) && !isNaN(Date.parse(val))) {
debug("convert to date", val)
val = new Date(val)
}
}
if (!types.hasOwnProperty(k)) {
return val
}
// allow `--no-blah` to set 'blah' to null if null is allowed
if (val === false && ~type.indexOf(null) &&
!(~type.indexOf(false) || ~type.indexOf(Boolean))) {
val = null
}
var d = {}
d[k] = val
debug("prevalidated val", d, val, types[k])
if (!validate(d, k, val, types[k], typeDefs)) {
if (exports.invalidHandler) {
exports.invalidHandler(k, val, types[k], data)
} else if (exports.invalidHandler !== false) {
debug("invalid: "+k+"="+val, types[k])
}
return remove
}
debug("validated val", d, val, types[k])
return d[k]
}).filter(function (val) { return val !== remove })
if (!val.length) delete data[k]
else if (isArray) {
debug(isArray, data[k], val)
data[k] = val
} else data[k] = val[0]
debug("k=%s val=%j", k, val, data[k])
})
}
function validateString (data, k, val) {
data[k] = String(val)
}
function validatePath (data, k, val) {
data[k] = path.resolve(String(val))
return true
}
function validateNumber (data, k, val) {
debug("validate Number %j %j %j", k, val, isNaN(val))
if (isNaN(val)) return false
data[k] = +val
}
function validateDate (data, k, val) {
debug("validate Date %j %j %j", k, val, Date.parse(val))
var s = Date.parse(val)
if (isNaN(s)) return false
data[k] = new Date(val)
}
function validateBoolean (data, k, val) {
if (val instanceof Boolean) val = val.valueOf()
else if (typeof val === "string") {
if (!isNaN(val)) val = !!(+val)
else if (val === "null" || val === "false") val = false
else val = true
} else val = !!val
data[k] = val
}
function validateUrl (data, k, val) {
val = url.parse(String(val))
if (!val.host) return false
data[k] = val.href
}
function validateStream (data, k, val) {
if (!(val instanceof Stream)) return false
data[k] = val
}
function validate (data, k, val, type, typeDefs) {
// arrays are lists of types.
if (Array.isArray(type)) {
for (var i = 0, l = type.length; i < l; i ++) {
if (type[i] === Array) continue
if (validate(data, k, val, type[i], typeDefs)) return true
}
delete data[k]
return false
}
// an array of anything?
if (type === Array) return true
// NaN is poisonous. Means that something is not allowed.
if (type !== type) {
debug("Poison NaN", k, val, type)
delete data[k]
return false
}
// explicit list of values
if (val === type) {
debug("Explicitly allowed %j", val)
// if (isArray) (data[k] = data[k] || []).push(val)
// else data[k] = val
data[k] = val
return true
}
// now go through the list of typeDefs, validate against each one.
var ok = false
, types = Object.keys(typeDefs)
for (var i = 0, l = types.length; i < l; i ++) {
debug("test type %j %j %j", k, val, types[i])
var t = typeDefs[types[i]]
if (t && type === t.type) {
var d = {}
ok = false !== t.validate(d, k, val)
val = d[k]
if (ok) {
// if (isArray) (data[k] = data[k] || []).push(val)
// else data[k] = val
data[k] = val
break
}
}
}
debug("OK? %j (%j %j %j)", ok, k, val, types[i])
if (!ok) delete data[k]
return ok
}
function parse (args, data, remain, types, shorthands) {
debug("parse", args, data, remain)
var key = null
, abbrevs = abbrev(Object.keys(types))
, shortAbbr = abbrev(Object.keys(shorthands))
for (var i = 0; i < args.length; i ++) {
var arg = args[i]
debug("arg", arg)
if (arg.match(/^-{2,}$/)) {
// done with keys.
// the rest are args.
remain.push.apply(remain, args.slice(i + 1))
args[i] = "--"
break
}
var hadEq = false
if (arg.charAt(0) === "-" && arg.length > 1) {
if (arg.indexOf("=") !== -1) {
hadEq = true
var v = arg.split("=")
arg = v.shift()
v = v.join("=")
args.splice.apply(args, [i, 1].concat([arg, v]))
}
// see if it's a shorthand
// if so, splice and back up to re-parse it.
var shRes = resolveShort(arg, shorthands, shortAbbr, abbrevs)
debug("arg=%j shRes=%j", arg, shRes)
if (shRes) {
debug(arg, shRes)
args.splice.apply(args, [i, 1].concat(shRes))
if (arg !== shRes[0]) {
i --
continue
}
}
arg = arg.replace(/^-+/, "")
var no = null
while (arg.toLowerCase().indexOf("no-") === 0) {
no = !no
arg = arg.substr(3)
}
if (abbrevs[arg]) arg = abbrevs[arg]
var isArray = types[arg] === Array ||
Array.isArray(types[arg]) && types[arg].indexOf(Array) !== -1
// allow unknown things to be arrays if specified multiple times.
if (!types.hasOwnProperty(arg) && data.hasOwnProperty(arg)) {
if (!Array.isArray(data[arg]))
data[arg] = [data[arg]]
isArray = true
}
var val
, la = args[i + 1]
var isBool = typeof no === 'boolean' ||
types[arg] === Boolean ||
Array.isArray(types[arg]) && types[arg].indexOf(Boolean) !== -1 ||
(typeof types[arg] === 'undefined' && !hadEq) ||
(la === "false" &&
(types[arg] === null ||
Array.isArray(types[arg]) && ~types[arg].indexOf(null)))
if (isBool) {
// just set and move along
val = !no
// however, also support --bool true or --bool false
if (la === "true" || la === "false") {
val = JSON.parse(la)
la = null
if (no) val = !val
i ++
}
// also support "foo":[Boolean, "bar"] and "--foo bar"
if (Array.isArray(types[arg]) && la) {
if (~types[arg].indexOf(la)) {
// an explicit type
val = la
i ++
} else if ( la === "null" && ~types[arg].indexOf(null) ) {
// null allowed
val = null
i ++
} else if ( !la.match(/^-{2,}[^-]/) &&
!isNaN(la) &&
~types[arg].indexOf(Number) ) {
// number
val = +la
i ++
} else if ( !la.match(/^-[^-]/) && ~types[arg].indexOf(String) ) {
// string
val = la
i ++
}
}
if (isArray) (data[arg] = data[arg] || []).push(val)
else data[arg] = val
continue
}
if (la && la.match(/^-{2,}$/)) {
la = undefined
i --
}
val = la === undefined ? true : la
if (isArray) (data[arg] = data[arg] || []).push(val)
else data[arg] = val
i ++
continue
}
remain.push(arg)
}
}
function resolveShort (arg, shorthands, shortAbbr, abbrevs) {
// handle single-char shorthands glommed together, like
// npm ls -glp, but only if there is one dash, and only if
// all of the chars are single-char shorthands, and it's
// not a match to some other abbrev.
arg = arg.replace(/^-+/, '')
// if it's an exact known option, then don't go any further
if (abbrevs[arg] === arg)
return null
// if it's an exact known shortopt, same deal
if (shorthands[arg]) {
// make it an array, if it's a list of words
if (shorthands[arg] && !Array.isArray(shorthands[arg]))
shorthands[arg] = shorthands[arg].split(/\s+/)
return shorthands[arg]
}
// first check to see if this arg is a set of single-char shorthands
var singles = shorthands.___singles
if (!singles) {
singles = Object.keys(shorthands).filter(function (s) {
return s.length === 1
}).reduce(function (l,r) {
l[r] = true
return l
}, {})
shorthands.___singles = singles
debug('shorthand singles', singles)
}
var chrs = arg.split("").filter(function (c) {
return singles[c]
})
if (chrs.join("") === arg) return chrs.map(function (c) {
return shorthands[c]
}).reduce(function (l, r) {
return l.concat(r)
}, [])
// if it's an arg abbrev, and not a literal shorthand, then prefer the arg
if (abbrevs[arg] && !shorthands[arg])
return null
// if it's an abbr for a shorthand, then use that
if (shortAbbr[arg])
arg = shortAbbr[arg]
// make it an array, if it's a list of words
if (shorthands[arg] && !Array.isArray(shorthands[arg]))
shorthands[arg] = shorthands[arg].split(/\s+/)
return shorthands[arg]
}
if (module === require.main) {
var assert = require("assert")
, util = require("util")
, shorthands =
{ s : ["--loglevel", "silent"]
, d : ["--loglevel", "info"]
, dd : ["--loglevel", "verbose"]
, ddd : ["--loglevel", "silly"]
, noreg : ["--no-registry"]
, reg : ["--registry"]
, "no-reg" : ["--no-registry"]
, silent : ["--loglevel", "silent"]
, verbose : ["--loglevel", "verbose"]
, h : ["--usage"]
, H : ["--usage"]
, "?" : ["--usage"]
, help : ["--usage"]
, v : ["--version"]
, f : ["--force"]
, desc : ["--description"]
, "no-desc" : ["--no-description"]
, "local" : ["--no-global"]
, l : ["--long"]
, p : ["--parseable"]
, porcelain : ["--parseable"]
, g : ["--global"]
}
, types =
{ aoa: Array
, nullstream: [null, Stream]
, date: Date
, str: String
, browser : String
, cache : path
, color : ["always", Boolean]
, depth : Number
, description : Boolean
, dev : Boolean
, editor : path
, force : Boolean
, global : Boolean
, globalconfig : path
, group : [String, Number]
, gzipbin : String
, logfd : [Number, Stream]
, loglevel : ["silent","win","error","warn","info","verbose","silly"]
, long : Boolean
, "node-version" : [false, String]
, npaturl : url
, npat : Boolean
, "onload-script" : [false, String]
, outfd : [Number, Stream]
, parseable : Boolean
, pre: Boolean
, prefix: path
, proxy : url
, "rebuild-bundle" : Boolean
, registry : url
, searchopts : String
, searchexclude: [null, String]
, shell : path
, t: [Array, String]
, tag : String
, tar : String
, tmp : path
, "unsafe-perm" : Boolean
, usage : Boolean
, user : String
, username : String
, userconfig : path
, version : Boolean
, viewer: path
, _exit : Boolean
}
; [["-v", {version:true}, []]
,["---v", {version:true}, []]
,["ls -s --no-reg connect -d",
{loglevel:"info",registry:null},["ls","connect"]]
,["ls ---s foo",{loglevel:"silent"},["ls","foo"]]
,["ls --registry blargle", {}, ["ls"]]
,["--no-registry", {registry:null}, []]
,["--no-color true", {color:false}, []]
,["--no-color false", {color:true}, []]
,["--no-color", {color:false}, []]
,["--color false", {color:false}, []]
,["--color --logfd 7", {logfd:7,color:true}, []]
,["--color=true", {color:true}, []]
,["--logfd=10", {logfd:10}, []]
,["--tmp=/tmp -tar=gtar",{tmp:"/tmp",tar:"gtar"},[]]
,["--tmp=tmp -tar=gtar",
{tmp:path.resolve(process.cwd(), "tmp"),tar:"gtar"},[]]
,["--logfd x", {}, []]
,["a -true -- -no-false", {true:true},["a","-no-false"]]
,["a -no-false", {false:false},["a"]]
,["a -no-no-true", {true:true}, ["a"]]
,["a -no-no-no-false", {false:false}, ["a"]]
,["---NO-no-No-no-no-no-nO-no-no"+
"-No-no-no-no-no-no-no-no-no"+
"-no-no-no-no-NO-NO-no-no-no-no-no-no"+
"-no-body-can-do-the-boogaloo-like-I-do"
,{"body-can-do-the-boogaloo-like-I-do":false}, []]
,["we are -no-strangers-to-love "+
"--you-know=the-rules --and=so-do-i "+
"---im-thinking-of=a-full-commitment "+
"--no-you-would-get-this-from-any-other-guy "+
"--no-gonna-give-you-up "+
"-no-gonna-let-you-down=true "+
"--no-no-gonna-run-around false "+
"--desert-you=false "+
"--make-you-cry false "+
"--no-tell-a-lie "+
"--no-no-and-hurt-you false"
,{"strangers-to-love":false
,"you-know":"the-rules"
,"and":"so-do-i"
,"you-would-get-this-from-any-other-guy":false
,"gonna-give-you-up":false
,"gonna-let-you-down":false
,"gonna-run-around":false
,"desert-you":false
,"make-you-cry":false
,"tell-a-lie":false
,"and-hurt-you":false
},["we", "are"]]
,["-t one -t two -t three"
,{t: ["one", "two", "three"]}
,[]]
,["-t one -t null -t three four five null"
,{t: ["one", "null", "three"]}
,["four", "five", "null"]]
,["-t foo"
,{t:["foo"]}
,[]]
,["--no-t"
,{t:["false"]}
,[]]
,["-no-no-t"
,{t:["true"]}
,[]]
,["-aoa one -aoa null -aoa 100"
,{aoa:["one", null, 100]}
,[]]
,["-str 100"
,{str:"100"}
,[]]
,["--color always"
,{color:"always"}
,[]]
,["--no-nullstream"
,{nullstream:null}
,[]]
,["--nullstream false"
,{nullstream:null}
,[]]
,["--notadate=2011-01-25"
,{notadate: "2011-01-25"}
,[]]
,["--date 2011-01-25"
,{date: new Date("2011-01-25")}
,[]]
,["-cl 1"
,{config: true, length: 1}
,[]
,{config: Boolean, length: Number, clear: Boolean}
,{c: "--config", l: "--length"}]
,["--acount bla"
,{"acount":true}
,["bla"]
,{account: Boolean, credentials: Boolean, options: String}
,{a:"--account", c:"--credentials",o:"--options"}]
,["--clear"
,{clear:true}
,[]
,{clear:Boolean,con:Boolean,len:Boolean,exp:Boolean,add:Boolean,rep:Boolean}
,{c:"--con",l:"--len",e:"--exp",a:"--add",r:"--rep"}]
,["--file -"
,{"file":"-"}
,[]
,{file:String}
,{}]
,["--file -"
,{"file":true}
,["-"]
,{file:Boolean}
,{}]
].forEach(function (test) {
var argv = test[0].split(/\s+/)
, opts = test[1]
, rem = test[2]
, actual = nopt(test[3] || types, test[4] || shorthands, argv, 0)
, parsed = actual.argv
delete actual.argv
console.log(util.inspect(actual, false, 2, true), parsed.remain)
for (var i in opts) {
var e = JSON.stringify(opts[i])
, a = JSON.stringify(actual[i] === undefined ? null : actual[i])
if (e && typeof e === "object") {
assert.deepEqual(e, a)
} else {
assert.equal(e, a)
}
}
assert.deepEqual(rem, parsed.remain)
})
}

View File

@ -0,0 +1,3 @@
To get started, <a
href="http://www.clahub.com/agreements/isaacs/abbrev-js">sign the
Contributor License Agreement</a>.

View File

@ -0,0 +1,23 @@
Copyright 2009, 2010, 2011 Isaac Z. Schlueter.
All rights reserved.
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.

View File

@ -0,0 +1,23 @@
# abbrev-js
Just like [ruby's Abbrev](http://apidock.com/ruby/Abbrev).
Usage:
var abbrev = require("abbrev");
abbrev("foo", "fool", "folding", "flop");
// returns:
{ fl: 'flop'
, flo: 'flop'
, flop: 'flop'
, fol: 'folding'
, fold: 'folding'
, foldi: 'folding'
, foldin: 'folding'
, folding: 'folding'
, foo: 'foo'
, fool: 'fool'
}
This is handy for command-line scripts, or other cases where you want to be able to accept shorthands.

View File

@ -0,0 +1,62 @@
module.exports = exports = abbrev.abbrev = abbrev
abbrev.monkeyPatch = monkeyPatch
function monkeyPatch () {
Object.defineProperty(Array.prototype, 'abbrev', {
value: function () { return abbrev(this) },
enumerable: false, configurable: true, writable: true
})
Object.defineProperty(Object.prototype, 'abbrev', {
value: function () { return abbrev(Object.keys(this)) },
enumerable: false, configurable: true, writable: true
})
}
function abbrev (list) {
if (arguments.length !== 1 || !Array.isArray(list)) {
list = Array.prototype.slice.call(arguments, 0)
}
for (var i = 0, l = list.length, args = [] ; i < l ; i ++) {
args[i] = typeof list[i] === "string" ? list[i] : String(list[i])
}
// sort them lexicographically, so that they're next to their nearest kin
args = args.sort(lexSort)
// walk through each, seeing how much it has in common with the next and previous
var abbrevs = {}
, prev = ""
for (var i = 0, l = args.length ; i < l ; i ++) {
var current = args[i]
, next = args[i + 1] || ""
, nextMatches = true
, prevMatches = true
if (current === next) continue
for (var j = 0, cl = current.length ; j < cl ; j ++) {
var curChar = current.charAt(j)
nextMatches = nextMatches && curChar === next.charAt(j)
prevMatches = prevMatches && curChar === prev.charAt(j)
if (!nextMatches && !prevMatches) {
j ++
break
}
}
prev = current
if (j === cl) {
abbrevs[current] = current
continue
}
for (var a = current.substr(0, j) ; j <= cl ; j ++) {
abbrevs[a] = current
a += current.charAt(j)
}
}
return abbrevs
}
function lexSort (a, b) {
return a === b ? 0 : a > b ? 1 : -1
}

View File

@ -0,0 +1,29 @@
{
"name": "abbrev",
"version": "1.0.5",
"description": "Like ruby's abbrev module, but in js",
"author": {
"name": "Isaac Z. Schlueter",
"email": "i@izs.me"
},
"main": "abbrev.js",
"scripts": {
"test": "node test.js"
},
"repository": {
"type": "git",
"url": "http://github.com/isaacs/abbrev-js"
},
"license": {
"type": "MIT",
"url": "https://github.com/isaacs/abbrev-js/raw/master/LICENSE"
},
"readme": "# abbrev-js\n\nJust like [ruby's Abbrev](http://apidock.com/ruby/Abbrev).\n\nUsage:\n\n var abbrev = require(\"abbrev\");\n abbrev(\"foo\", \"fool\", \"folding\", \"flop\");\n \n // returns:\n { fl: 'flop'\n , flo: 'flop'\n , flop: 'flop'\n , fol: 'folding'\n , fold: 'folding'\n , foldi: 'folding'\n , foldin: 'folding'\n , folding: 'folding'\n , foo: 'foo'\n , fool: 'fool'\n }\n\nThis is handy for command-line scripts, or other cases where you want to be able to accept shorthands.\n",
"readmeFilename": "README.md",
"_id": "abbrev@1.0.5",
"dist": {
"shasum": "8878621df7d367d2b65a37fd163e59df351fbfa4"
},
"_from": "abbrev@1",
"_resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz"
}

View File

@ -0,0 +1,47 @@
var abbrev = require('./abbrev.js')
var assert = require("assert")
var util = require("util")
console.log("TAP Version 13")
var count = 0
function test (list, expect) {
count++
var actual = abbrev(list)
assert.deepEqual(actual, expect,
"abbrev("+util.inspect(list)+") === " + util.inspect(expect) + "\n"+
"actual: "+util.inspect(actual))
actual = abbrev.apply(exports, list)
assert.deepEqual(abbrev.apply(exports, list), expect,
"abbrev("+list.map(JSON.stringify).join(",")+") === " + util.inspect(expect) + "\n"+
"actual: "+util.inspect(actual))
console.log('ok - ' + list.join(' '))
}
test([ "ruby", "ruby", "rules", "rules", "rules" ],
{ rub: 'ruby'
, ruby: 'ruby'
, rul: 'rules'
, rule: 'rules'
, rules: 'rules'
})
test(["fool", "foom", "pool", "pope"],
{ fool: 'fool'
, foom: 'foom'
, poo: 'pool'
, pool: 'pool'
, pop: 'pope'
, pope: 'pope'
})
test(["a", "ab", "abc", "abcd", "abcde", "acde"],
{ a: 'a'
, ab: 'ab'
, abc: 'abc'
, abcd: 'abcd'
, abcde: 'abcde'
, ac: 'acde'
, acd: 'acde'
, acde: 'acde'
})
console.log("0..%d", count)

36
node_modules/markdown/node_modules/nopt/package.json generated vendored Normal file

File diff suppressed because one or more lines are too long

68
node_modules/markdown/package.json generated vendored Normal file

File diff suppressed because one or more lines are too long

5
node_modules/markdown/seed.yml generated vendored Normal file
View File

@ -0,0 +1,5 @@
---
name: markdown-js
description: JavaScript implementation of Markdown
tags: markdown parser
version: 0.1.2

1
node_modules/pushover-notifications/.npmignore generated vendored Normal file
View File

@ -0,0 +1 @@
npm-debug.log

16
node_modules/pushover-notifications/LICENSE generated vendored Normal file
View File

@ -0,0 +1,16 @@
/*
* Copyright (c) 2012 Aaron Bieber <deftly@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

89
node_modules/pushover-notifications/README.md generated vendored Normal file
View File

@ -0,0 +1,89 @@
![Pushover](https://pushover.net/assets/pushover-header-0f47af8e08d8bef658a999a9e6584fcc.png)
Send [pushover.net](http://pushover.net) notifications from Node.JS
## Usage
### Install
npm install pushover-notifications
### Pushover API values
Any API paramaters, as found on https://pushover.net/api, can be passed in the object. For example, `retry` and `expire` can be added to the object being passed to `.send`! Here's an example with many different parameters.
```javascript
var msg = {
message: "This is a message",
title: "Well - this is fantastic",
sound: 'magic',
device: 'test_device',
priority: 2,
url: "http://pushover.net",
url_title: "Pushover Website"
};
```
## Examples
### Sending a message
```javascript
var push = require( 'pushover-notifications' );
var p = new push( {
user: process.env['PUSHOVER_USER'],
token: process.env['PUSHOVER_TOKEN'],
// onerror: function(error) {},
// update_sounds: true // update the list of sounds every day - will
// prevent app from exiting.
});
var msg = {
// These values correspond to the parameters detailed on https://pushover.net/api
// 'message' is required. All other values are optional.
message: 'omg node test', // required
title: "Well - this is fantastic",
sound: 'magic',
device: 'devicename',
priority: 1
};
p.send( msg, function( err, result ) {
if ( err ) {
throw err;
}
console.log( result );
});
```
### Sending a message to multiple users
```javascript
var users = [
'token1',
'token2',
'token3'
];
var msg = {
message: 'omg node test',
title: "Well - this is fantastic",
sound: 'magic' // optional
priority: 1 // optional,
};
for ( var i = 0, l = users.length; i < l; i++ ) {
msg.user = users[i];
// token can be overwritten as well.
p.send( msg, function( err, result ) {
if ( err ) {
throw err;
}
console.log( result );
});
}
```

1
node_modules/pushover-notifications/index.js generated vendored Normal file
View File

@ -0,0 +1 @@
module.exports = require( './lib/pushover' );

180
node_modules/pushover-notifications/lib/pushover.js generated vendored Normal file
View File

@ -0,0 +1,180 @@
var https = require('https'),
url = require('url'),
qs = require('querystring'),
p_url = 'https://api.pushover.net/1/messages.json';
function setDefaults(o) {
var def = [
'device',
'title',
'url',
'url_title',
'priority',
'timestamp',
'sound'
];
var i = 0; l = def.length;
for (; i < l; i++) {
if (!o[def[i]]) {
o[def[i]] = '';
}
}
return o;
}
function Pushover(opts) {
var self = this;
this.token = opts.token;
this.user = opts.user;
this.httpOptions = opts.httpOptions;
this.sounds = {
"pushover":"Pushover (default)",
"bike":"Bike",
"bugle":"Bugle",
"cashregister":"Cash Register",
"classical":"Classical",
"cosmic":"Cosmic",
"falling":"Falling",
"gamelan":"Gamelan",
"incoming":"Incoming",
"intermission":"Intermission",
"magic":"Magic",
"mechanical":"Mechanical",
"pianobar":"Piano Bar",
"siren":"Siren",
"spacealarm":"Space Alarm",
"tugboat":"Tug Boat",
"alien":"Alien Alarm (long)",
"climb":"Climb (long)",
"persistent":"Persistent (long)",
"echo":"Pushover Echo (long)",
"updown":"Up Down (long)",
"none":"None (silent)"
};
if (opts.debug) {
this.debug = opts.debug;
}
if (opts.onerror) {
this.onerror = opts.onerror;
}
if (opts.update_sounds) {
self.updateSounds();
setInterval(function() {
self.updateSounds();
}, 86400000);
}
}
Pushover.prototype.errors = function(d) {
if (typeof d === 'string') {
d = JSON.parse(d);
}
if (d.errors) {
if (this.onerror) {
this.onerror.call(null, d.errors[0]);
} else {
throw new Error(d.errors[0]);
}
}
};
Pushover.prototype.updateSounds = function() {
var self = this, data = '';
var surl = 'https://api.pushover.net/1/sounds.json?token=' + self.token;
var req = https.request(url.parse(surl) , function(res) {
res.on('end', function() {
var j = JSON.parse(data);
self.errors(data);
self.sounds = j.sounds;
});
res.on('data', function(chunk) {
data += chunk;
});
});
req.on('error', function(e) {
err = e;
});
req.write('');
req.end();
};
Pushover.prototype.send = function(obj, fn) {
var self = this;
var o = url.parse(p_url);
o.method = "POST";
obj = setDefaults(obj);
if (! self.sounds[ obj.sound ]) {
obj.sound = 'pushover';
}
var req_string = {
token: self.token || obj.token,
user: self.user || obj.user
};
var p;
for (p in obj) {
req_string[ p ] = obj[p];
}
req_string = qs.stringify(req_string);
o.headers = {
'Content-Length': req_string.length
};
var httpOpts = self.httpOptions;
if (httpOpts) {
Object.keys(httpOpts).forEach(function(key) {
o[key] = httpOpts[key];
});
}
var req = https.request(o, function(res) {
if (self.debug) {
console.log(res.statusCode);
}
var err;
var data = '';
res.on('end', function() {
self.errors(data);
if (fn) {
fn.call(null, err, data);
}
});
res.on('data', function(chunk) {
data += chunk;
});
});
req.on('error', function(err) {
if (fn) {
fn.call(null, err);
}
// In the tests the "end" event did not get emitted if "error" was emitted,
// but to be sure that the callback is not get called twice, null the callback function
fn = null;
});
if (self.debug) {
console.log (req_string);
}
req.write(req_string);
req.end();
};
exports = module.exports = Pushover;

28
node_modules/pushover-notifications/package.json generated vendored Normal file
View File

@ -0,0 +1,28 @@
{
"author": {
"name": "Aaron Bieber",
"email": "aaron@qbit.io"
},
"name": "pushover-notifications",
"description": "Pushover API for node.js",
"version": "0.2.2",
"homepage": "http://github.com/qbit/node-pushover",
"repository": {
"type": "git",
"url": "https://github.com/qbit/node-pushover.git"
},
"dependencies": {},
"devDependencies": {},
"optionalDependencies": {},
"engines": {
"node": "*"
},
"readme": "![Pushover](https://pushover.net/assets/pushover-header-0f47af8e08d8bef658a999a9e6584fcc.png)\n\nSend [pushover.net](http://pushover.net) notifications from Node.JS\n\n## Usage\n\n### Install\n\n\tnpm install pushover-notifications\n\t\n### Pushover API values\n\nAny API paramaters, as found on https://pushover.net/api, can be passed in the object. For example, `retry` and `expire` can be added to the object being passed to `.send`! Here's an example with many different parameters.\n```javascript\nvar msg = {\n\tmessage: \"This is a message\",\n\ttitle: \"Well - this is fantastic\",\n\tsound: 'magic',\n\tdevice: 'test_device',\n\tpriority: 2,\n\turl: \"http://pushover.net\",\n\turl_title: \"Pushover Website\"\n};\n```\n## Examples\n\n### Sending a message\n```javascript\n\nvar push = require( 'pushover-notifications' );\n\nvar p = new push( {\n\tuser: process.env['PUSHOVER_USER'],\n\ttoken: process.env['PUSHOVER_TOKEN'],\n\t// onerror: function(error) {},\n\t// update_sounds: true // update the list of sounds every day - will\n\t// prevent app from exiting.\n});\n\nvar msg = {\n\t// These values correspond to the parameters detailed on https://pushover.net/api\n\t// 'message' is required. All other values are optional.\n\tmessage: 'omg node test',\t// required\n\ttitle: \"Well - this is fantastic\",\n\tsound: 'magic',\n\tdevice: 'devicename',\n\tpriority: 1\n};\n\np.send( msg, function( err, result ) {\n\tif ( err ) {\n\t\tthrow err;\n\t}\n\n\tconsole.log( result );\n});\n```\n\n### Sending a message to multiple users\n```javascript\n\nvar users = [\n 'token1',\n 'token2',\n 'token3'\n];\n\nvar msg = {\n message: 'omg node test',\n title: \"Well - this is fantastic\",\n sound: 'magic' // optional\n priority: 1 // optional,\n};\n\nfor ( var i = 0, l = users.length; i < l; i++ ) {\n\n msg.user = users[i];\n // token can be overwritten as well.\n\n p.send( msg, function( err, result ) {\n if ( err ) {\n throw err;\n }\n\n console.log( result );\n });\n}\n\n```\n",
"readmeFilename": "README.md",
"_id": "pushover-notifications@0.2.2",
"dist": {
"shasum": "b151e5729b7014d84dcb71b1a86c247c124eb277"
},
"_from": "pushover-notifications@",
"_resolved": "https://registry.npmjs.org/pushover-notifications/-/pushover-notifications-0.2.2.tgz"
}

View File

@ -0,0 +1,25 @@
var push = require( '../lib/pushover.js' );
var p = new push( {
user: process.env['PUSHOVER_USER'],
token: process.env['PUSHOVER_TOKEN'],
update_sounds: false,
debug: true,
onerror: function(err) {
console.log('ERROR!', err);
}
});
var msg = {
message: 'omg node test',
sound: 'magic',
title: "Well - this is fantastic",
};
// console.log( p );
p.send( msg, function( err, result ) {
console.log( 'error', err );
console.log( 'result', result );
// process.exit(0);
});

22
node_modules/pushover-notifications/test/test.js generated vendored Normal file
View File

@ -0,0 +1,22 @@
var push = require( '../lib/pushover.js' );
var p = new push( {
user: process.env['PUSHOVER_USER'],
token: process.env['PUSHOVER_TOKEN'],
update_sounds: false,
debug: true
});
var msg = {
message: 'omg node test',
sound: 'magic',
title: "Well - this is fantastic",
};
// console.log( p );
p.send( msg, function( err, result ) {
console.log( 'error', err );
console.log( 'result', result );
// process.exit(0);
});

21
node_modules/pushover-notifications/test/test_multi.js generated vendored Normal file
View File

@ -0,0 +1,21 @@
var push = require( '../lib/pushover.js' );
var p = new push( {
// user: process.env['PUSHOVER_USER'],
token: process.env['PUSHOVER_TOKEN'],
debug: true
});
var msg = {
message: 'omg node test',
title: "Well - this is fantastic",
user: process.env['PUSHOVER_USER']
};
// console.log( p );
p.send( msg, function( err, result ) {
console.log( err );
console.log( result );
process.exit(0);
});

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
</project>

View File

@ -0,0 +1,9 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0" is_locked="false">
<option name="myName" value="Project Default" />
<option name="myLocal" value="false" />
<inspection_tool class="JSHint" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="JSLastCommaInArrayLiteral" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSLastCommaInObjectLiteral" enabled="false" level="ERROR" enabled_by_default="false" />
</profile>
</component>

View File

@ -0,0 +1,7 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="PROJECT_PROFILE" value="Project Default" />
<option name="USE_PROJECT_PROFILE" value="true" />
<version value="1.0" />
</settings>
</component>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<file url="file://$PROJECT_DIR$" libraries="{Node.js Dependencies for strong-cluster-connect-store}" />
<file url="PROJECT" libraries="{Node.js v0.11.3 Core Modules}" />
<includedPredefinedLibrary name="Node.js Globals" />
<excludedPredefinedLibrary name="HTML" />
<excludedPredefinedLibrary name="HTML5 / EcmaScript 5" />
</component>
</project>

View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JSHintConfiguration" version="2.1.4" use-config-file="true">
<option bitwise="true" />
<option camelcase="false" />
<option curly="true" />
<option eqeqeq="true" />
<option forin="true" />
<option immed="false" />
<option latedef="false" />
<option newcap="false" />
<option noarg="true" />
<option noempty="true" />
<option nonew="true" />
<option plusplus="false" />
<option undef="true" />
<option unused="false" />
<option strict="true" />
<option trailing="false" />
<option asi="false" />
<option boss="false" />
<option debug="false" />
<option eqnull="false" />
<option esnext="false" />
<option evil="false" />
<option expr="false" />
<option funcscope="false" />
<option globalstrict="false" />
<option iterator="false" />
<option lastsemic="false" />
<option laxbreak="false" />
<option laxcomma="false" />
<option loopfunc="false" />
<option multistr="false" />
<option proto="false" />
<option scripturl="false" />
<option smarttabs="false" />
<option shadow="false" />
<option sub="false" />
<option supernew="false" />
<option validthis="false" />
<option browser="true" />
<option couch="false" />
<option devel="false" />
<option dojo="false" />
<option jquery="false" />
<option mootools="false" />
<option node="false" />
<option nonstandard="false" />
<option prototypejs="false" />
<option rhino="false" />
<option worker="false" />
<option wsh="false" />
<option yui="false" />
<option nomen="false" />
<option onevar="false" />
<option passfail="false" />
<option white="false" />
<option maxerr="50" />
</component>
</project>

View File

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Node.js Dependencies for strong-cluster-connect-store" type="javaScript">
<properties>
<sourceFilesUrls>
<item url="file://$PROJECT_DIR$/node_modules" />
</sourceFilesUrls>
</properties>
<CLASSES>
<root url="file://$PROJECT_DIR$/node_modules" />
</CLASSES>
<SOURCES />
</library>
</component>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" />
</project>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/strong-cluster-connect-store.iml" filepath="$PROJECT_DIR$/.idea/strong-cluster-connect-store.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,5 @@
<component name="DependencyValidationManager">
<state>
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
</state>
</component>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Node.js v0.11.3 Core Modules" level="application" />
<orderEntry type="library" name="Node.js Dependencies for strong-cluster-connect-store" level="project" />
</component>
</module>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@ -0,0 +1,476 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="c9cd60f5-0112-4cd0-aadd-d8bb05bbf943" name="Default" comment="">
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/test/cluster-store.js" afterPath="$PROJECT_DIR$/test/cluster-store.js" />
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/package.json" afterPath="$PROJECT_DIR$/package.json" />
</list>
<ignored path="strong-cluster-connect-store.iws" />
<ignored path=".idea/workspace.xml" />
<option name="TRACKING_ENABLED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="ChangesViewManager" flattened_view="true" show_ignored="false" />
<component name="CreatePatchCommitExecutor">
<option name="PATCH_PATH" value="" />
</component>
<component name="DaemonCodeAnalyzer">
<disable_hints />
</component>
<component name="ExecutionTargetManager" SELECTED_TARGET="default_target" />
<component name="FavoritesManager">
<favorites_list name="strong-cluster-connect-store" />
</component>
<component name="FileEditorManager">
<leaf>
<file leaf-file-name="cluster-store.js" pinned="false" current="true" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/test/cluster-store.js">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.12448133" vertical-offset="1920" max-vertical-offset="2415">
<caret line="132" column="22" selection-start-line="132" selection-start-column="22" selection-end-line="132" selection-end-column="22" />
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="index.js" pinned="false" current="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/node_modules/express-session/index.js">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="3660">
<caret line="167" column="10" selection-start-line="167" selection-start-column="10" selection-end-line="167" selection-end-column="10" />
<folding />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="cluster-store.js" pinned="false" current="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/lib/cluster-store.js">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.0" vertical-offset="945" max-vertical-offset="1815">
<caret line="47" column="32" selection-start-line="47" selection-start-column="32" selection-end-line="47" selection-end-column="32" />
<folding />
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component name="FindManager">
<FindUsagesManager>
<setting name="OPEN_NEW_TAB" value="false" />
</FindUsagesManager>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="GitLogSettings">
<option name="myDateState">
<MyDateState />
</option>
</component>
<component name="IdeDocumentHistory">
<option name="changedFiles">
<list>
<option value="$PROJECT_DIR$/lib/ClusterStore.js" />
<option value="$PROJECT_DIR$/test/ClusterStore.js" />
<option value="$PROJECT_DIR$/README.md" />
<option value="$PROJECT_DIR$/lib/cluster-store.js" />
<option value="$PROJECT_DIR$/test/cluster-store.js" />
</list>
</option>
</component>
<component name="NodeJsMochaPackageDirSetting">
<data>$PROJECT_DIR$/node_modules/mocha</data>
</component>
<component name="ProjectFrameBounds">
<option name="x" value="55" />
<option name="y" value="22" />
<option name="width" value="1385" />
<option name="height" value="878" />
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
<OptionsSetting value="true" id="Add" />
<OptionsSetting value="true" id="Remove" />
<OptionsSetting value="true" id="Checkout" />
<OptionsSetting value="true" id="Update" />
<OptionsSetting value="true" id="Status" />
<OptionsSetting value="true" id="Edit" />
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
<component name="ProjectReloadState">
<option name="STATE" value="0" />
</component>
<component name="ProjectView">
<navigator currentView="ProjectPane" proportions="" version="1">
<flattenPackages />
<showMembers />
<showModules />
<showLibraryContents />
<hideEmptyPackages />
<abbreviatePackageNames />
<autoscrollToSource />
<autoscrollFromSource />
<sortByType />
</navigator>
<panes>
<pane id="ProjectPane">
<subPane>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="cluster-connect-store" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="cluster-connect-store" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="cluster-connect-store" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="cluster-connect-store" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="cluster-connect-store" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="test" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="cluster-connect-store" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="cluster-connect-store" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="lib" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
</PATH>
</subPane>
</pane>
<pane id="Scope" />
</panes>
</component>
<component name="PropertiesComponent">
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="options.lastSelected" value="settings.nodejs" />
<property name="recentsLimit" value="5" />
<property name="restartRequiresConfirmation" value="true" />
<property name="GoToClass.includeJavaFiles" value="false" />
</component>
<component name="RunManager" selected="Mocha.Unit-tests">
<configuration default="true" type="DartUnitRunConfigurationType" factoryName="DartUnit">
<option name="VMOptions" />
<option name="arguments" />
<option name="filePath" />
<option name="scope" value="ALL" />
<option name="testName" />
<method />
</configuration>
<configuration default="true" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application">
<option name="VMOptions" />
<option name="arguments" />
<option name="filePath" />
<option name="name" value="Dart" />
<option name="saveOutputToFile" value="false" />
<option name="showConsoleOnStdErr" value="false" />
<option name="showConsoleOnStdOut" value="false" />
<method />
</configuration>
<configuration default="true" type="JSTestDriver:ConfigurationType" factoryName="JsTestDriver">
<setting name="configLocationType" value="CONFIG_FILE" />
<setting name="settingsFile" value="" />
<setting name="serverType" value="INTERNAL" />
<setting name="preferredDebugBrowser" value="Chrome" />
<method />
</configuration>
<configuration default="true" type="JavaScriptTestRunnerKarma" factoryName="Karma" config-file="">
<envs />
<method />
</configuration>
<configuration default="true" type="CucumberJavaScriptRunConfigurationType" factoryName="Cucumber.js">
<option name="cucumberJsArguments" />
<option name="executablePath" />
<option name="filePath" />
<method />
</configuration>
<configuration default="true" type="JavascriptDebugType" factoryName="JavaScript Debug">
<method />
</configuration>
<configuration default="true" type="mocha-javascript-test-runner" factoryName="Mocha">
<node-options />
<working-directory>$PROJECT_DIR$</working-directory>
<pass-parent-env>true</pass-parent-env>
<envs />
<ui>BDD</ui>
<extra-mocha-options />
<test-directory />
<recursive>false</recursive>
<method />
</configuration>
<configuration default="true" type="NodeJSConfigurationType" factoryName="Node.js" working-dir="">
<method />
</configuration>
<configuration default="false" name="Unit-tests" type="mocha-javascript-test-runner" factoryName="Mocha">
<node-options />
<working-directory>$PROJECT_DIR$</working-directory>
<pass-parent-env>true</pass-parent-env>
<envs />
<ui>BDD</ui>
<extra-mocha-options />
<test-directory>$PROJECT_DIR$/test</test-directory>
<recursive>false</recursive>
<RunnerSettings RunnerId="MochaDebugRunner" />
<RunnerSettings RunnerId="RunnerForMochaJavaScript" />
<ConfigurationWrapper RunnerId="MochaDebugRunner" />
<ConfigurationWrapper RunnerId="RunnerForMochaJavaScript" />
<method />
</configuration>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="Mocha.Unit-tests" />
</list>
</component>
<component name="ShelveChangesManager" show_recycled="false" />
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="c9cd60f5-0112-4cd0-aadd-d8bb05bbf943" name="Default" comment="" />
<created>1373452074532</created>
<updated>1373452074532</updated>
</task>
<servers />
</component>
<component name="ToolWindowManager">
<frame x="55" y="22" width="1385" height="878" extended-state="6" />
<editor active="true" />
<layout>
<window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
<window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" />
<window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
<window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.24926686" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
<window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.3986842" sideWeight="0.7503671" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.2496329" sideWeight="0.5" order="2" side_tool="true" content_ui="tabs" />
<window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="true" content_ui="tabs" />
<window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.32894737" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
<window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
<window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
<window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32894737" sideWeight="0.7503671" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="SLIDING" type="SLIDING" visible="false" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
<window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" />
</layout>
</component>
<component name="Vcs.Log.UiProperties">
<option name="RECENTLY_FILTERED_USER_GROUPS">
<collection />
</option>
<option name="RECENTLY_FILTERED_BRANCH_GROUPS">
<collection />
</option>
</component>
<component name="VcsContentAnnotationSettings">
<option name="myLimit" value="2678400000" />
</component>
<component name="VcsManagerConfiguration">
<option name="myTodoPanelSettings">
<TodoPanelSettings />
</option>
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<option name="time" value="1" />
</breakpoint-manager>
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/test/cluster-store.js">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.0" vertical-offset="105" max-vertical-offset="2385">
<caret line="7" column="34" selection-start-line="7" selection-start-column="34" selection-end-line="7" selection-end-column="34" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/cluster-store.js">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.0" vertical-offset="721" max-vertical-offset="1605">
<caret line="25" column="40" selection-start-line="25" selection-start-column="40" selection-end-line="25" selection-end-column="40" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/test/cluster-store.js">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="2370">
<caret line="11" column="21" selection-start-line="11" selection-start-column="21" selection-end-line="11" selection-end-column="21" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/cluster-store.js">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.0" vertical-offset="1155" max-vertical-offset="1545">
<caret line="77" column="9" selection-start-line="77" selection-start-column="9" selection-end-line="77" selection-end-column="9" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/test/cluster-store.js">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.0" vertical-offset="1005" max-vertical-offset="2325">
<caret line="104" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/test/cluster-store.js">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.0" vertical-offset="1005" max-vertical-offset="2325">
<caret line="104" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/cluster-store.js">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.0" vertical-offset="360" max-vertical-offset="1170">
<caret line="67" column="33" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/test/cluster-store.js">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.0" vertical-offset="555" max-vertical-offset="2340">
<caret line="38" column="24" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/node_modules/connect/lib/middleware/session/session.js">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.0" vertical-offset="1020" max-vertical-offset="1830">
<caret line="99" column="18" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/index.js">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="105">
<caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/cluster-store.js">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.0" vertical-offset="80" max-vertical-offset="1095">
<caret line="31" column="8" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/test/cluster-store.js">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.0" vertical-offset="840" max-vertical-offset="1935">
<caret line="81" column="4" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/node_modules/connect/lib/middleware/session/store.js">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="1350">
<caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/node_modules/connect/lib/middleware/session.js">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.2260274" vertical-offset="0" max-vertical-offset="5340">
<caret line="16" column="28" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/node_modules/connect/lib/middleware/session/session.js">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.6352459" vertical-offset="1020" max-vertical-offset="1830">
<caret line="99" column="18" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/index.js">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="730">
<caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/README.md">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.062586926" vertical-offset="0" max-vertical-offset="1185">
<caret line="3" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/package.json">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.0" vertical-offset="0" max-vertical-offset="750">
<caret line="0" column="0" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/lib/cluster-store.js">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.0" vertical-offset="945" max-vertical-offset="1815">
<caret line="47" column="32" selection-start-line="47" selection-start-column="32" selection-end-line="47" selection-end-column="32" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/node_modules/express-session/index.js">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.0" vertical-offset="2330" max-vertical-offset="3660">
<caret line="167" column="10" selection-start-line="167" selection-start-column="10" selection-end-line="167" selection-end-column="10" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/node_modules/body-parser/index.js">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.062240664" vertical-offset="75" max-vertical-offset="1590">
<caret line="7" column="8" selection-start-line="7" selection-start-column="8" selection-end-line="7" selection-end-column="8" />
<folding />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/test/cluster-store.js">
<provider selected="true" editor-type-id="text-editor">
<state vertical-scroll-proportion="0.12448133" vertical-offset="1920" max-vertical-offset="2415">
<caret line="132" column="22" selection-start-line="132" selection-start-column="22" selection-end-line="132" selection-end-column="22" />
<folding />
</state>
</provider>
</entry>
</component>
</project>

5
node_modules/strong-cluster-connect-store/.jshintrc generated vendored Normal file
View File

@ -0,0 +1,5 @@
{
"camelcase": true
, "quotmark": "single"
, "eqnull": true
}

18
node_modules/strong-cluster-connect-store/.npmignore generated vendored Normal file
View File

@ -0,0 +1,18 @@
lib-cov
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
pids
logs
results
npm-debug.log
strong-cluster-connect-store-*.tgz
coverage.html
node_modules

View File

@ -0,0 +1,4 @@
language: node_js
node_js:
- "0.10"
- "0.11"

311
node_modules/strong-cluster-connect-store/LICENSE generated vendored Normal file
View File

@ -0,0 +1,311 @@
Copyright (c) 2013-2014 StrongLoop, Inc.
strong-cluster-connect-store uses a 'dual license' model. Users may use strong-cluster-connect-store under
the terms of the MIT license, or under the StrongLoop License. The text of both
is included below.
MIT license
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.
StrongLoop License
STRONGLOOP SUBSCRIPTION AGREEMENT
PLEASE READ THIS AGREEMENT CAREFULLY BEFORE YOU AGREE TO THESE TERMS. IF YOU
ARE ACTING ON BEHALF OF AN ENTITY, THEN YOU REPRESENT THAT YOU HAVE THE
AUTHORITY TO ENTER INTO THIS AGREEMENT ON BEHALF OF THAT ENTITY. IF YOU DO NOT
AGREE TO THESE TERMS, YOU SHOULD NOT AGREE TO THE TERMS OF THIS AGREEMENT OR
INSTALL OR USE THE SOFTWARE.
This StrongLoop Subscription Agreement ("Agreement") is made by and between
StrongLoop, Inc. ("StrongLoop") with its principal place of business at 107 S.
B St, Suite 220, San Mateo, CA 94401 and the person or entity entering into this
Agreement ("Customer"). The effective date ("Effective Date") of this Agreement
is the date Customer agrees to these terms or installs or uses the Software (as
defined below). This Agreement applies to Customer's use of the Software but it
shall be superseded by any signed agreement between you and StrongLoop
concerning the Software.
1. Subscriptions and Licenses.
1.1 Subscriptions. StrongLoop offers five different subscription levels to its
customers, each as more particularly described on StrongLoop's website located
at www.strongloop.com (the "StrongLoop Site"): (1) Free; (2) Developer; (3)
Professional; (4) Gold; and (5) Platinum. The actual subscription level
applicable to Customer (the "Subscription") will be specified in the purchase
order that Customer issues to StrongLoop. This Agreement applies to Customer
regardless of the level of the Subscription selected by Customer and whether or
not Customer upgrades or downgrades its Subscription. StrongLoop hereby agrees
to provide the services as described on the StrongLoop Site for each
Subscription level during the term for which Customer has purchased the
applicable Subscription, subject to Customer paying the fees applicable to the
Subscription level purchased, if any (the "Subscription Fees"). StrongLoop may
modify the services to be provided under any Subscription upon notice to
Customer.
1.2 License Grant. Subject to the terms and conditions of this Agreement,
StrongLoop grants to Customer, during the Subscription Term (as defined in
Section 7.1 (Term and Termination) of this Agreement, a limited, non-exclusive,
non-transferable right and license, to install and use the StrongLoop Suite
software (the "Software") and the documentation made available electronically as
part of the Software (the "Documentation"), either of which may be modified
during the Term (as defined in Section 7.1 below), solely for development,
production and commercial purposes so long as Customer is using the Software to
run only one process on a given operating system at a time. This Agreement,
including but not limited to the license and restrictions contained herein,
apply to Customer regardless of whether Customer accesses the Software via
download from the StrongLoop Site or through a third-party website or service,
even if Customer acquired the Software prior to agreeing to this Agreement.
1.3 License Restrictions. Customer shall not itself, or through any parent,
subsidiary, affiliate, agent or other third party:
1.3.1 sell, lease, license, distribute, sublicense or otherwise transfer
in whole or in part, any Software or the Documentation to a third party;
or
1.3.2 decompile, disassemble, translate, reverse engineer or otherwise
attempt to derive source code from the Software, in whole or in part, nor
shall Customer use any mechanical, electronic or other method to trace,
decompile, disassemble, or identify the source code of the Software or
encourage others to do so, except to the limited extent, if any, that
applicable law permits such acts notwithstanding any contractual
prohibitions, provided, however, before Customer exercises any rights that
Customer believes to be entitled to based on mandatory law, Customer shall
provide StrongLoop with thirty (30) days prior written notice and provide
all reasonably requested information to allow StrongLoop to assess
Customer's claim and, at StrongLoop's sole discretion, to provide
alternatives that reduce any adverse impact on StrongLoop's intellectual
property or other rights; or
1.3.3 allow access or permit use of the Software by any users other than
Customer's employees or authorized third-party contractors who are
providing services to Customer and agree in writing to abide by the terms
of this Agreement, provided further that Customer shall be liable for any
failure by such employees and third-party contractors to comply with the
terms of this Agreement and no usage restrictions, if any, shall be
exceeded; or
1.3.4 create, develop, license, install, use, or deploy any third party
software or services to circumvent or provide access, permissions or
rights which violate the license keys embedded within the Software; or
1.3.5 modify or create derivative works based upon the Software or
Documentation; or disclose the results of any benchmark test of the
Software to any third party without StrongLoop's prior written approval;
or
1.3.6 change any proprietary rights notices which appear in the Software
or Documentation; or
1.3.7 use the Software as part of a time sharing or service bureau
purposes or in any other resale capacity.
1.4 Third-Party Software. The Software may include individual certain software
that is owned by third parties, including individual open source software
components (the "Third-Party Software"), each of which has its own copyright and
its own applicable license conditions. Such third-party software is licensed to
Customer under the terms of the applicable third-party licenses and/or copyright
notices that can be found in the LICENSES file, the Documentation or other
materials accompanying the Software, except that Sections 5 (Warranty
Disclaimer) and 6 (Limitation of Liability) also govern Customer's use of the
third-party software. Customer agrees to comply with the terms and conditions
of the relevant third-party software licenses.
2. Support Services. StrongLoop has no obligation to provide any support for
the Software other than the support services specifically described on the
StrongLoop Site for the Subscription level procured by Customer. However,
StrongLoop has endeavored to establish a community of users of the Software who
have provided their own feedback, hints and advice regarding their experiences
in using the Software. You can find that community and user feedback on the
StrongLoop Site. The use of any information, content or other materials from,
contained in or on the StrongLoop Site are subject to the StrongLoop website
terms of use located here http://www.strongloop.com/terms-of-service.
3. Confidentiality. For purposes of this Agreement, "Confidential Information"
means any and all information or proprietary materials (in every form and media)
not generally known in the relevant trade or industry and which has been or is
hereafter disclosed or made available by StrongLoop to Customer in connection
with the transactions contemplated under this Agreement, including (i) all trade
secrets, (ii) existing or contemplated Software, services, designs, technology,
processes, technical data, engineering, techniques, methodologies and concepts
and any related information, and (iii) information relating to business plans,
sales or marketing methods and customer lists or requirements. For a period of
five (5) years from the date of disclosure of the applicable Confidential
Information, Customer shall (i) hold the Confidential Information in trust and
confidence and avoid the disclosure or release thereof to any other person or
entity by using the same degree of care as it uses to avoid unauthorized use,
disclosure, or dissemination of its own Confidential Information of a similar
nature, but not less than reasonable care, and (ii) not use the Confidential
Information for any purpose whatsoever except as expressly contemplated under
this Agreement; provided that, to the extent the Confidential Information
constitutes a trade secret under law, Customer agrees to protect such
information for so long as it qualifies as a trade secret under applicable law.
Customer shall disclose the Confidential Information only to those of its
employees and contractors having a need to know such Confidential Information
and shall take all reasonable precautions to ensure that such employees and
contractors comply with the provisions of this Section. The obligations of
Customer under this Section shall not apply to information that Customer can
demonstrate (i) was in its possession at the time of disclosure and without
restriction as to confidentiality, (ii) at the time of disclosure is generally
available to the public or after disclosure becomes generally available to the
public through no breach of agreement or other wrongful act by Customer, (iii)
has been received from a third party without restriction on disclosure and
without breach of agreement by Customer, or (iv) is independently developed by
Customer without regard to the Confidential Information. In addition, Customer
may disclose Confidential Information as required to comply with binding orders
of governmental entities that have jurisdiction over it; provided that Customer
gives StrongLoop reasonable written notice to allow StrongLoop to seek a
protective order or other appropriate remedy, discloses only such Confidential
Information as is required by the governmental entity, and uses commercially
reasonable efforts to obtain confidential treatment for any Confidential
Information disclosed. Notwithstanding the above, Customer agrees that
StrongLoop, its employees and agents shall be free to use and employ their
general skills, know-how, and expertise, and to use, disclose, and employ any
generalized ideas, concepts, know-how, methods, techniques or skills gained or
learned during the Term or thereafter.
4. Ownership. StrongLoop shall retain all intellectual property and proprietary
rights in the Software, Documentation, and related works, including but not
limited to any derivative work of the foregoing and StrongLoop's licensors shall
retain all intellectual property and proprietary rights in any Third-Party
Software that may be provided with or as a part of the Software. Customer shall
do nothing inconsistent with StrongLoop's or its licensors' title to the
Software and the intellectual property rights embodied therein, including, but
not limited to, transferring, loaning, selling, assigning, pledging, or
otherwise disposing, encumbering, or suffering a lien or encumbrance upon or
against any interest in the Software. The Software (including any Third-Party
Software) contain copyrighted material, trade secrets and other proprietary
material of StrongLoop and/or its licensors.
5. Warranty Disclaimer. THE SOFTWARE (INCLUDING ANY THIRD-PARTY SOFTWARE) AND
DOCUMENTATION MADE AVAILABLE TO CUSTOMER ARE PROVIDED "AS-IS" AND STRONGLOOP,
ON BEHALF OF ITSELF AND ITS LICENSORS, EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, ANY IMPLIED WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, TITLE,
PERFORMANCE, AND ACCURACY AND ANY IMPLIED WARRANTIES ARISING FROM STATUTE,
COURSE OF DEALING, COURSE OF PERFORMANCE, OR USAGE OF TRADE. STRONGLOOP DOES
NOT WARRANT THAT THE OPERATION OF THE SOFTWARE WILL BE UNINTERRUPTED OR
ERROR-FREE, THAT DEFECTS IN THE SOFTWARE WILL BE CORRECTED OR THAT THE SOFTWARE
WILL PROVIDE OR ENSURE ANY PARTICULAR RESULTS OR OUTCOME. NO ORAL OR WRITTEN
INFORMATION OR ADVICE GIVEN BY STRONGLOOP OR ITS AUTHORIZED REPRESENTATIVES
SHALL CREATE A WARRANTY OR IN ANY WAY INCREASE THE SCOPE OF THIS WARRANTY.
STRONGLOOP IS NOT OBLIGATED TO PROVIDE CUSTOMER WITH UPGRADES TO THE SOFTWARE,
BUT MAY ELECT TO DO SO IN ITS SOLE DISCRETION. SOME JURISDICTIONS DO NOT ALLOW
THE EXCLUSION OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY NOT APPLY TO
CUSTOMER.WITHOUT LIMITING THE GENERALITY OF THE FOREGOING DISCLAIMER, THE
SOFTWARE AND DOCUMENTATION ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE IN
THE PLANNING, CONSTRUCTION, MAINTENANCE, CONTROL, OR DIRECT OPERATION OF NUCLEAR
FACILITIES, AIRCRAFT NAVIGATION, CONTROL OR COMMUNICATION SYSTEMS, WEAPONS
SYSTEMS, OR DIRECT LIFE SUPPORT SYSTEMS.
6. Limitation of Liability.
6.1 Exclusion of Liability. IN NO EVENT WILL STRONGLOOP OR ITS LICENSORS
BE LIABLE UNDER THIS AGREEMENT FOR ANY INDIRECT, RELIANCE, PUNITIVE,
CONSEQUENTIAL, SPECIAL, EXEMPLARY, OR INCIDENTAL DAMAGES OF ANY KIND AND
HOWEVER CAUSED (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF
BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION AND
THE LIKE), EVEN IF STRONGLOOP HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES. CUSTOMER BEARS FULL RESPONSIBILITY FOR USE OF THE SOFTWARE AND
THE SUBSCRIPTION AND STRONGLOOP DOES NOT GUARANTEE THAT THE USE OF THE
SOFTWARE AND SUBSCRIPTION WILL ENSURE THAT CUSTOMER'S NETWORK WILL BE
AVAILABLE, SECURE, MONITORED OR PROTECTED AGAINST ANY DOWNTIME, DENIAL OF
SERVICE ATTACKS, SECUITY BREACHES, HACKERS AND THE LIKE. IN NO EVENT WILL
STRONGLOOP'S CUMULATIVE LIABILITY FOR ANY DAMAGES, LOSSES AND CAUSES OF
ACTION (WHETHER IN CONTRACT, TORT, INCLUDING NEGLIGENCE, OR OTHERWISE)
ARISING OUT OF OR RELATED TO THIS AGREEMENT EXCEED THE GREATER OF ONE
HUNDRED DOLLARS (US$100) OR THE TOTAL SUBSCRIPTION FEES PAID BY CUSTOMER
TO STRONGLOOP IN THE TWELVE (12) MONTHS PRECEDING THE DATE THE CLAIM
ARISES.
6.2 Limitation of Damages. IN NO EVENT WILL STRONGLOOP'S LICENSORS HAVE
ANY LIABILITY FOR ANY CLAIM ARISING IN CONNECTION WITH THIS AGREEMENT.
THE PROVISIONS OF THIS SECTION 6 ALLOCATE RISKS UNDER THIS AGREEMENT
BETWEEN CUSTOMER, STRONGLOOP AND STRONGLOOP'S SUPPLIERS. THE FOREGOING
LIMITATIONS, EXCLUSIONS AND DISCLAIMERS APPLY TO THE MAXIMUM EXTENT
PERMITTED BY APPLICABLE LAW, EVEN IF ANY REMEDY FAILS IN ITS ESSENTIAL
PURPOSE.
6.3 Failure of Essential Purpose. THE PARTIES AGREE THAT THESE
LIMITATIONS SHALL APPLY EVEN IF THIS AGREEMENT OR ANY LIMITED REMEDY
SPECIFIED HEREIN IS FOUND TO HAVE FAILED OF ITS ESSENTIAL PURPOSE.
6.4 Allocation of Risk. The sections on limitation of liability and
disclaimer of warranties allocate the risks in the Agreement between the
parties. This allocation is an essential element of the basis of the
bargain between the parties.
7. Term and Termination.
7.1 This Agreement shall commence on the Effective Date and continue for so long
as Customer has a valid Subscription and is current on the payment of any
Subscription Fees required to be paid for that Subscription (the "Subscription
Term"). Either party may terminate this Agreement immediately upon written
notice to the other party, and the Subscription and licenses granted hereunder
automatically terminate upon the termination of this Agreement. This Agreement
will terminate immediately without notice from StrongLoop if Customer fails to
comply with or otherwise breaches any provision of this Agreement.
7.2 All Sections other than Section 1.1 (Subscriptions) and 1.2 (Licenses) shall
survive the expiration or termination of this Agreement.
8. Subscription Fees and Payments. StrongLoop, Customer agrees to pay
StrongLoop the Subscription Fees as described on the StrongLoop Site for the
Subscription purchased unless a different amount has been agreed to in a
separate agreement between Customer and StrongLoop. In addition, Customer shall
pay all sales, use, value added, withholding, excise taxes and other tax, duty,
custom and similar fees levied upon the delivery or use of the Software and the
Subscriptions described in this Agreement. Fees shall be invoiced in full upon
StrongLoop's acceptance of Customer's purchase order for the Subscription. All
invoices shall be paid in US dollars and are due upon receipt and shall be paid
within thirty (30) days. Payments shall be made without right of set-off or
chargeback. If Customer does not pay the invoices when due, StrongLoop may
charge interest at one percent (1%) per month or the highest rate permitted by
law, whichever is lower, on the unpaid balance from the original due date. If
Customer fails to pay fees in accordance with this Section, StrongLoop may
suspend fulfilling its obligations under this Agreement (including but not
limited to suspending the services under the Subscription) until payment is
received by StrongLoop. If any applicable law requires Customer to withhold
amounts from any payments to StrongLoop under this Agreement, (a) Customer shall
effect such withholding, remit such amounts to the appropriate taxing
authorities and promptly furnish StrongLoop with tax receipts evidencing the
payments of such amounts and (b) the sum payable by Customer upon which the
deduction or withholding is based shall be increased to the extent necessary to
ensure that, after such deduction or withholding, StrongLoop receives and
retains, free from liability for such deduction or withholding, a net amount
equal to the amount StrongLoop would have received and retained absent the
required deduction or withholding.
9. General.
9.1 Compliance with Laws. Customer shall abide by all local, state, federal and
international laws, rules, regulations and orders applying to Customer's use of
the Software, including, without limitation, the laws and regulations of the
United States that may restrict the export and re-export of certain commodities
and technical data of United States origin, including the Software. Customer
agrees that it will not export or re-export the Software without the appropriate
United States or foreign government licenses.
9.2 Entire Agreement. This Agreement constitutes the entire agreement between
the parties concerning the subject matter hereof. This Agreement supersedes all
prior or contemporaneous discussions, proposals and agreements between the
parties relating to the subject matter hereof. No amendment, modification or
waiver of any provision of this Agreement shall be effective unless in writing
and signed by both parties. Any additional or different terms on any purchase
orders issued by Customer to StrongLoop shall not be binding on either party,
are hereby rejected by StrongLoop and void.
9.3 Severability. If any provision of this Agreement is held to be invalid or
unenforceable, the remaining portions shall remain in full force and effect and
such provision shall be enforced to the maximum extent possible so as to effect
the intent of the parties and shall be reformed to the extent necessary to make
such provision valid and enforceable.
9.4 Waiver. No waiver of rights by either party may be implied from any actions
or failures to enforce rights under this Agreement.
9.5 Force Majeure. Neither party shall be liable to the other for any delay or
failure to perform due to causes beyond its reasonable control (excluding
payment of monies due).
9.6 No Third Party Beneficiaries. Unless otherwise specifically stated, the
terms of this Agreement are intended to be and are solely for the benefit of
StrongLoop and Customer and do not create any right in favor of any third party.
9.7 Governing Law and Jurisdiction. This Agreement shall be governed by the
laws of the State of California, without reference to the principles of
conflicts of law. The provisions of the Uniform Computerized Information
Transaction Act and United Nations Convention on Contracts for the International
Sale of Goods shall not apply to this Agreement. The parties shall attempt to
resolve any dispute related to this Agreement informally, initially through
their respective management, and then by non-binding mediation in San Francisco
County, California. Any litigation related to this Agreement shall be brought
in the state or federal courts located in San Francisco County, California, and
only in those courts and each party irrevocably waives any objections to such
venue.
9.8 Notices. All notices must be in writing and shall be effective three (3)
days after the date sent to the other party's headquarters, Attention Chief
Financial Officer.

28
node_modules/strong-cluster-connect-store/README.md generated vendored Normal file
View File

@ -0,0 +1,28 @@
# Connect Session Store for Cluster
[![Build Status](https://travis-ci.org/strongloop/strong-cluster-connect-store.png?branch=master)](https://travis-ci.org/strongloop/strong-cluster-connect-store)
[![NPM version](https://badge.fury.io/js/strong-cluster-connect-store.png)](http://badge.fury.io/js/strong-cluster-connect-store)
## Overview
Strong-cluster-connect-store is an implementation of connect session store
using node's native cluster messaging. It provides an easy way for using
sessions in connect/express based applications running in a node cluster.
Features:
- Supports both connect and express.
- No dependencies on external services.
- Module is shipped without connect, it will use *your* version of connect
or express.
- Covered by unit-tests.
## Documentation
For complete documentation, see [StrongLoop Documentation | Strong Cluster Connect Store](http://docs.strongloop.com/display/DOC/Strong+Cluster+Connect+Store).
## Installation
```sh
$ npm install strong-cluster-connect-store
```

7
node_modules/strong-cluster-connect-store/docs.json generated vendored Normal file
View File

@ -0,0 +1,7 @@
{
"content": [
{"title": "Strong Cluster Connect Store API", "depth": 2},
"lib/cluster-store.js"
],
"codeSectionDepth": 3
}

1
node_modules/strong-cluster-connect-store/index.js generated vendored Normal file
View File

@ -0,0 +1 @@
module.exports = require('./lib/cluster-store.js');

View File

@ -0,0 +1,115 @@
var inherits = require('util').inherits;
var cluster = require('cluster');
var NativeStore = require('strong-store-cluster');
/**
* Documentation marker for explicit setup of the shared-state server
* in the master process. The initialization happens when this module
* is required, thus calling this function is entirely optional.
* @private
*/
function setup() {
// no-op
}
/**
* Return the `ClusterStore` constructor that can be called to create
* a session Store to use with
* the [express-session](https://www.npmjs.org/package/express-session)
* middleware.
*
* #### Example
* ```
// express v3.x
var session = express.session;
var SessionStore = require('strong-cluster-connect-store')(session);
// express v4.x
var session = require('express-session');
var SessionStore = require('strong-cluster-connect-store')(session);
// express v3.x (backwards compatibility)
var SessionStore = require('strong-cluster-connect-store')(express);
* ```
*
* @param {Object} connectOrSession express session or connect/express itself
* @return {function} The ClusterStore constructor.
*/
module.exports = function(connectOrSession) {
var session = connectOrSession.session || connectOrSession;
/**
* Connect's Store.
* @private
*/
var Store = session.Store;
var COLLECTION_NAME = 'strong-cluster-connect-session-store';
/**
* Initialize a ClusterStore object with the given `options`.
* This is an internal constructor called by express-session middleware,
* you should not need to call it directly.
* @param {Object} options Options for the ClusterStore object.
* @constructor
* @extends {session.Store}
*/
function ClusterStore(options) {
Store.call(this, options);
this._collection = NativeStore.collection(COLLECTION_NAME);
}
inherits(ClusterStore, Store);
/**
* Fetch a session by an id and receive the session in the callback.
* @param {String} sid A string id for the session.
* @end
* @callback {Function} fn
* @param {Error} err if present, indicates an error condition.
* @param value The data stored in the collection, could be any type.
* @end
*/
ClusterStore.prototype.get = function(sid, fn) {
this._collection.get(sid, fn);
};
/**
* Commit the given `session` object associated with the given `sid` to the
* session store.
* @param {String} sid A string id identifying the session.
* @param {Object} session The session object.
* @end
* @callback {Function} fn
* @param {Error} err If defined, indicates an error occured.
* @end
*/
ClusterStore.prototype.set = function(sid, session, fn) {
this._collection.set(sid, session, fn);
};
/**
* Destroy the session associated with the given `sid`.
* @param {String} sid A String with the id of the session.
* @end
* @callback {Function} fn
* @param {Error} err If defined, indicates an error occured.
* @end
*
*/
ClusterStore.prototype.destroy = function(sid, fn){
this._collection.del(sid, fn);
};
/**
* Same as `setup()` (see above).
* @private
*/
ClusterStore.setup = setup;
return ClusterStore;
};
module.exports.setup = setup;

55
node_modules/strong-cluster-connect-store/package.json generated vendored Normal file
View File

@ -0,0 +1,55 @@
{
"name": "strong-cluster-connect-store",
"version": "1.0.0",
"description": "Implementation of connect session store using node's native cluster messaging",
"license": {
"name": "Dual MIT/StrongLoop",
"url": "https://github.com/strongloop/strong-cluster-connect-store/blob/master/LICENSE"
},
"main": "index.js",
"scripts": {
"test": "mocha --reporter spec",
"lint": "./node_modules/.bin/jshint *.js test lib"
},
"repository": {
"type": "git",
"url": "https://github.com/strongloop/strong-cluster-connect-store.git"
},
"keywords": [
"connect",
"express",
"cluster",
"session",
"store"
],
"author": {
"name": "Miroslav Bajtos",
"email": "miroslav@strongloop.com"
},
"peerDependencies": {
"strong-store-cluster": "~0.1.0"
},
"devDependencies": {
"mocha": "~1.9.0",
"jshint": "~2.0.1",
"chai": "~1.7.2",
"cookie-parser": "^1.0.1",
"strong-store-cluster": "latest",
"request": "~2.22.0",
"async": "~0.2.9",
"express-session": "^1.0.2",
"express": "^4.1.1",
"body-parser": "^1.0.2"
},
"engines": {
"node": ">=0.10.0"
},
"readme": "# Connect Session Store for Cluster\n\n[![Build Status](https://travis-ci.org/strongloop/strong-cluster-connect-store.png?branch=master)](https://travis-ci.org/strongloop/strong-cluster-connect-store)\n[![NPM version](https://badge.fury.io/js/strong-cluster-connect-store.png)](http://badge.fury.io/js/strong-cluster-connect-store)\n\n## Overview\n\nStrong-cluster-connect-store is an implementation of connect session store\nusing node's native cluster messaging. It provides an easy way for using\nsessions in connect/express based applications running in a node cluster.\n\nFeatures:\n\n- Supports both connect and express.\n- No dependencies on external services.\n- Module is shipped without connect, it will use *your* version of connect\n or express.\n- Covered by unit-tests.\n \n## Documentation\n\nFor complete documentation, see [StrongLoop Documentation | Strong Cluster Connect Store](http://docs.strongloop.com/display/DOC/Strong+Cluster+Connect+Store).\n\n## Installation\n\n```sh\n$ npm install strong-cluster-connect-store\n```\n",
"readmeFilename": "README.md",
"_id": "strong-cluster-connect-store@1.0.0",
"dist": {
"shasum": "c1ee462f23b1dee4d6381fe986d83a227c32c62e"
},
"_from": "strong-cluster-connect-store@",
"_resolved": "https://registry.npmjs.org/strong-cluster-connect-store/-/strong-cluster-connect-store-1.0.0.tgz"
}

View File

@ -0,0 +1,155 @@
var cluster = require('cluster');
var http = require('http');
var expect = require('chai').expect;
var express = require('express');
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var session = require('express-session');
var request = require('request');
var async = require('async');
var ClusterStore = require('..')(session);
var workerUrl;
// verify we can call setup without connect in master and workers
require('..').setup();
if (cluster.isWorker) {
startConnectServer();
return;
}
describe('clustered connect server', function() {
before(setupWorkers);
after(stopWorkers);
var KEY = 'a-key';
var PAYLOAD = 'a-value';
// NOTE We assume that the cluster does a perfect round-robin
// distribution of requests among the workers
it('shares sessions between workers', function(done) {
async.series(
[
save,
load
],
function(err, results) {
if (err) {
return done(err);
}
expect(results.pop().value).to.equal(PAYLOAD);
done();
}
);
});
it('destroys a session shared between workers', function(done) {
async.series(
[
save,
destroy,
load
],
function(err, results) {
if (err) {
return done(err);
}
expect(results.pop().value).to.equal(undefined);
done();
}
);
});
function save(next) {
sendCommand({ cmd: 'set', key: KEY, value: PAYLOAD }, next);
}
function destroy(next) {
sendCommand({ cmd: 'del', key: KEY }, next);
}
function load(next) {
sendCommand({ cmd: 'get', key: KEY }, next);
}
});
function sendCommand(command, cb) {
request(
{
url: workerUrl,
method: 'POST',
json: command
},
function(err, res, body) {
if (err) {
return cb(err);
}
cb(null, body);
}
);
}
var WORKER_COUNT = 2;
function getNumberOfWorkers() {
return Object.keys(cluster.workers).length;
}
function setupWorkers(done) {
if (getNumberOfWorkers() > 0) {
var msg = 'Cannot setup workers: there are already other workers running.';
return done(new Error(msg));
}
cluster.setupMaster({ exec: __filename });
ClusterStore.setup();
var workersListening = 0;
cluster.on('listening', function(w, addr) {
if (!workerUrl) workerUrl = 'http://localhost:' + addr.port;
workersListening++;
if (workersListening == WORKER_COUNT) {
done();
}
});
for (var i = 0; i < WORKER_COUNT; i++) {
cluster.fork();
}
}
function stopWorkers(done) {
cluster.disconnect(done);
}
function startConnectServer() {
var PORT = 0; // Let the OS pick any available port
var app = express()
.use(cookieParser())
.use(session({ store: new ClusterStore(), secret: 'a-secret', key: 'sid' }))
.use(bodyParser.json())
.use(requestHandler);
var server = http.createServer(app).listen(PORT);
function requestHandler(req, res) {
var result = {};
switch (req.body.cmd) {
case 'set':
req.session[req.body.key] = req.body.value;
break;
case 'get':
result.value = req.session[req.body.key];
break;
case 'del':
req.session.destroy();
break;
}
res.setHeader('Content-Type', 'text/json');
res.end(JSON.stringify(result));
}
}

2
node_modules/strong-store-cluster/BUG.txt generated vendored Normal file
View File

@ -0,0 +1,2 @@
A new Client() is created for each 'online' worker... but clients are never
released.

19
node_modules/strong-store-cluster/LICENSE generated vendored Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2013 Strongloop, Inc.
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.

19
node_modules/strong-store-cluster/Makefile generated vendored Normal file
View File

@ -0,0 +1,19 @@
# Makefile
-include local.mk
.PHONY: test default
default: test
test:
@npm test
jenkins-build: jenkins-install jenkins-test
jenkins-install:
npm install
jenkins-test:
./node_modules/.bin/mocha --timeout 5000 --slow 1000 --ui tdd --reporter xunit > xunit.xml

189
node_modules/strong-store-cluster/README.md generated vendored Normal file
View File

@ -0,0 +1,189 @@
# Strong Store Cluster
Strong Store for Cluster provides a key/value collection that can be accesses by
all processes in a node cluster.
## Example
```javascript
// get the collection and give it a name
var collection = require('strong-store-cluster').collection('test');
// don't let keys expire, ever - the number represents seconds
collection.configure({ expireKeys: 0 });
collection.set('ThisIsMyKey', { a: 0, b: 'Hiya', c: { d: 99}}, function(err) {
if (err) {
console.error('There was an error');
return;
}
collection.get('ThisIsMyKey', function(err, obj) {
if (err) {
console.error('There was an error in collection.get.');
return;
}
console.log('The object: ',obj);
});
});
```
## API documentation
### store.collection(name)
Returns a Collection object which lets you share data between node processes.
### Class: Collection
A `Collection` instance provides access to a shared key-value store
shared by multiple node instances.
How collections are named and stored is determined by the storage backend. The
`strong-store-cluster` implementation stores collections in the master process
(if you're using cluster), and accepts any arbitrary string as a collection
name.
A `Collection` object is also an `EventEmitter`.
#### collection.configure([options])
* `options` (`Object`) contains configurations options to be changed
* `expireKeys` (`Number`) seconds after which keys in this
collection are to be expired.
Set configuration options for the collection.
Currently only one configurable option is supported: `expireKeys`. When set
to a nonzero value, keys will automatically expire after they've not been
read or updated for some time. The timeout is specified in seconds. There's no
guarantee that the key will be discared after exactly that number of seconds
has passed. However keys will never be automatically deleted _sooner_ than what
the `expireKeys` setting allows.
It is perfectly legal to call the `configure` method from multiple node
processes (e.g. both in a worker and in the master process). However you
should be careful to set the _same_ option values every time, otherwise the
effect is undefined.
#### collection.get(key, callback)
* `key` (`String`) key to retrieve
* `callback` (`Function`) called when the value has been retrieved
Read the value associated with a particular key. The callback is called with
two arguments, `(err, value)`. When the key wasn't found in the collection, it
is automatically created and it's `value` is set to `undefined`.
#### collection.set(key, [value], [callback])
* `key` (`String`) key to set or update
* `value` (`object`) value to associate with the key
* `callback` (`Function`) called when the value has been retrieved
Set the value associated with `key`. The `value` must be either undefined or
a value that can be serialized with JSON.stringify.
When the `value` parameter is omitted or set to `undefined`, the key is
deleted, so effectively it's the same as calling `collection.del(key)`.
The `callback` function receives only one argument, `err`. When the
callback is omitted, the master process does not send a confirmation
after updating the key, and any errors are silently ignored.
#### collection.del(key, [callback])
* `key` (`String`) key to delete
* `callback` (`Function`) called when the value has been retrieved
Delete a key from the collection.
This operation is the equivalent of setting the key to `undefined`.
The `callback` function receives only one argument, `err`. When the
callback is omitted, the master process does not send a confirmation
after deleting the key, and any errors are silently ignored.
#### collection.acquire(key, callback)
* `key` (`String`) key to delete
* `callback` (`Function`) called when the key has been locked
Lock a key for exclusive read and write access.
The `acquire` methods waits until it can grab an exclusive lock on the
specified key. When the lock is acquired, no other process can read, write or
delete this particular key. When the lock is no longer needed, it should be
relinquished with `keylock.release()`.
Three parameters are passed to the `callback` function:
`(err, keylock, value)`. The `keylock` argument receives a `KeyLock` class
instance, which lets you read and manipulate the key's value as well as
eventually release the lock. The `value` argument is set to the initial value
associated with the key.
#### Event: 'error'
* `err` (`Error`)
The error event is emitted whenever an unrecoverable error is encountered.
### Class: KeyLock
A `KeyLock` instance represents a key that has been locked. The `KeyLock`
class implements methods that lets you manipulate the key and release
the lock.
#### keylock.get()
* Returns: (`Object`) value that's currently associated with the key
This function returns the value that's currently associated with the locked
key.
Initially this is the same as the `value` argument that was passed to the
`collection.acquire()` callback, but it does immediately reflect changes that
are made with `keylock.set()` and `keylock.del()`.
#### keylock.set([value])
Updates the value associated with a locked key.
The change isn't pushed back to the master process immediately; the change
is committed when the lock is released again. The change however is reflected
immediately in the return value from `keylock.get()`.
After the lock has been released, the key can no longer be updated through the
`KeyLock` instance. Any attempt to do so will make it throw.
Setting the value to `undefined` marks the key for deletion, e.g. it's
equivalent to `keylock.del()`.
#### keylock.del()
Mark a locked key for deletion. See `keylock.set()`.
#### keylock.release([callback])
Release the lock that protects a key. If the key was updated with
`keylock.set()` or `keylock.del()`, these changes are committed.
When a lock has been released, it is no longer possible to manipulate the
key using `KeyLock` methods. Releasing the lock twice isn't allowed either.
The `get()` method will still work but it won't reflect any value changes
that were made after releasing.
The `callback` function receives only one argument, `err`. When the
callback is omitted, the master process does not send a confirmation
after releasing the key, and any errors are silently ignored.

6
node_modules/strong-store-cluster/docs.json generated vendored Normal file
View File

@ -0,0 +1,6 @@
{
"title": "Strong Store for Cluster",
"content": [
"README.md"
]
}

18
node_modules/strong-store-cluster/index.js generated vendored Normal file
View File

@ -0,0 +1,18 @@
var assert = require('assert');
var cluster = require('cluster');
var VERSION = require('./package.json').version;
if(cluster._strongStoreCluster) {
assert(
cluster._strongStoreCluster.VERSION === VERSION,
'Multiple versions of strong-strore-cluster are being initialized.\n' +
'This version ' + VERSION + ' is incompatible with already initialized\n' +
'version ' + cluster._strongStoreCluster.VERSION + '.\n'
);
module.exports = cluster._strongStoreCluster;
return;
}
module.exports = require('./lib/lib.js');
module.exports.VERSION = VERSION;
cluster._strongStoreCluster = module.exports;

22
node_modules/strong-store-cluster/lib/collection.js generated vendored Normal file
View File

@ -0,0 +1,22 @@
module.exports = collection;
var hasOwnProperty = Object.prototype.hasOwnProperty;
var cluster = require('cluster');
if (cluster.isMaster)
var Collection = require('./master/Collection.js');
else
var Collection = require('./worker/Collection.js');
var collections = {};
function collection(name) {
if (!hasOwnProperty.call(collections, name))
return collections[name] = new Collection(name);
else
return collections[name];
}

8
node_modules/strong-store-cluster/lib/lib.js generated vendored Normal file
View File

@ -0,0 +1,8 @@
var cluster = require('cluster'),
collection = require('./collection.js');
exports.collection = collection;
if (cluster.isMaster)
require('./master/setup.js');

107
node_modules/strong-store-cluster/lib/master/Client.js generated vendored Normal file
View File

@ -0,0 +1,107 @@
module.exports = Client;
var collection = require('../collection.js');
function Client(worker) {
this._worker = worker;
this._id = worker.id;
this._locks = {};
this._onMessage = this.onMessage.bind(this);
worker.on('message', this._onMessage);
}
Client.prototype.onMessage = function(msg) {
if (msg.type === 'DSM_REQUEST')
this[msg.method](msg);
};
Client.prototype.get = function(msg) {
var self = this,
entry = collection(msg.collection)._entry(msg.key);
entry.get(this._id, function(err, json) {
if (err) self._sendError(msg, err);
else self._sendReply(msg, { json: json });
});
};
Client.prototype.set = function(msg) {
var self = this,
entry = collection(msg.collection)._entry(msg.key);
entry.set(msg.json, this._id, function(err) {
if (err) self._sendError(msg, err);
else self._sendReply(msg);
});
};
Client.prototype.acquire = function(msg) {
var self = this,
entry = collection(msg.collection)._entry(msg.key);
entry.acquire(this._id, function(err, json) {
if (err) self._sendError(msg, err);
else self._sendReply(msg, { json: json });
});
};
Client.prototype.release = function(msg) {
var self = this,
entry = collection(msg.collection)._entry(msg.key);
entry.release(this._id, function(err, json) {
if (err) self._sendError(msg, err);
else self._sendReply(msg);
});
};
Client.prototype.setRelease = function(msg) {
var self = this,
entry = collection(msg.collection)._entry(msg.key);
entry.setRelease(msg.json, this._id, function(err) {
if (err) self._sendError(msg, err);
else self._sendReply(msg);
});
};
Client.prototype.configure = function(msg) {
var coll = collection(msg.collection),
err = undefined;
try {
coll._applyConfig(msg.config);
this._sendReply(msg);
} catch (err) {
this._sendError(msg, err);
}
};
// This function clobbers the data argument if specified!
Client.prototype._sendReply = function(msg, data) {
if (!msg.requestId)
return;
data = data || {};
data.type = 'DSM_REPLY';
data.requestId = msg.requestId;
data.err = undefined;
this._worker.send(data);
};
Client.prototype._sendError = function(msg, err) {
if (!msg.requestId)
return;
var data = {};
data.type = 'DSM_REPLY';
data.requestId = msg.requestId;
data.err = err;
this._worker.send(data);
};

View File

@ -0,0 +1,153 @@
module.exports = Collection;
var hasOwnProperty = Object.prototype.hasOwnProperty;
var assert = require('assert'),
inherits = require('util').inherits,
EventEmitter = require('events').EventEmitter;
var Entry = require('./Entry.js'),
KeyLock = require('./KeyLock.js');
function Collection(name) {
this._name = name;
this._entries = {};
this._count = 0;
this._expireKeys = null;
this._expireKeysTimer = null;
this._sweep = this._sweep.bind(this);
}
inherits(Collection, EventEmitter);
Collection.prototype._entry = function(key) {
if (!hasOwnProperty.call(this._entries, key)) {
if (!this._count++ && this._expireKeys)
this._startExpireKeysTimer();
return this._entries[key] = new Entry(this, key);
} else {
return this._entries[key];
}
};
Collection.prototype._remove = function(key) {
assert(hasOwnProperty.call(this._entries, key));
delete this._entries[key];
if (!--this._count && this.expireKeys)
this._stopExpireKeysTimer();
};
Collection.prototype.get = function(key, cb) {
this._entry(key).get(-1, function(err, json) {
if (err)
cb(err);
else if (!json)
cb(null, undefined);
else
cb(null, JSON.parse(json));
});
};
Collection.prototype.set = function(key, value, cb) {
cb = cb || noop;
this._entry(key).set(JSON.stringify(value), -1, cb);
};
Collection.prototype.del = function(key, cb) {
cb = cb || noop;
this._entry(key).set(undefined, -1, cb);
};
Collection.prototype.acquire = function(key, cb) {
var self = this,
entry = this._entry(key);
entry.acquire(-1, function(err, json) {
var lock = new KeyLock(entry, json);
return cb(null, lock, lock.get());
});
};
Collection.prototype._applyConfig = function(config) {
config = config || {};
for (var key in config) {
if (!hasOwnProperty.call(config, key))
continue;
switch (key) {
case 'expireKeys':
if (config.expireKeys === this._expireKeys)
break;
this._stopExpireKeysTimer();
this._expireKeys = config.expireKeys;
this._startExpireKeysTimer();
break;
default:
throw new Error('Unspported configuration option: ' + key);
}
}
};
Collection.prototype._startExpireKeysTimer = function() {
assert(!this._expireKeysTimer);
if (!this._expireKeys || !this._count)
return;
var interval = Math.ceil(this._expireKeys * 1000 / 2);
this._expireKeysTimer = setInterval(this._sweep, interval);
this._expireKeysTimer.unref();
};
Collection.prototype._stopExpireKeysTimer = function() {
if (!this._expireKeysTimer)
return;
clearInterval(this._expireKeysTimer);
this._expireKeysTimer = null;
};
Collection.prototype._sweep = function() {
var entries = this._entries,
key;
for (key in entries) {
if (!hasOwnProperty.call(entries, key))
continue;
if (entries[key].age(1) > 2)
this._remove(key);
}
if (!this._count)
this._stopExpireKeysTimer();
};
Collection.prototype.configure = function(config) {
var self = this;
try {
this._applyConfig(config);
} catch (err) {
process.nextTick(function() {
self.emit('error', err);
});
}
return this;
};
function noop() {
}

103
node_modules/strong-store-cluster/lib/master/Entry.js generated vendored Normal file
View File

@ -0,0 +1,103 @@
module.exports = Entry;
var assert = require('assert');
function Entry(collection, key) {
this._collection = collection;
this._key = key;
this._value = undefined;
this._queue = [];
this._age = 0;
}
Entry.prototype.get = function(requestor, cb) {
var self = this;
if (!this._queue.length) {
var value = self._value;
self._age = 0;
process.nextTick(function() {
cb(null, value);
});
} else {
this.acquire(requestor, function(err) {
self.release(requestor, noop);
cb(null, self._value);
});
}
};
Entry.prototype.set = function(newValue, requestor, cb) {
var self = this;
if (!this._queue.length) {
this._value = newValue;
this._age = 0;
if (newValue === undefined)
this._collection._remove(this._key);
process.nextTick(cb);
} else {
this.acquire(requestor, function() {
self._value = newValue;
if (newValue !== undefined)
self.release(requestor, cb);
else {
self._collection._remove(self._key);
cb();
}
});
}
};
Entry.prototype.acquire = function(requestor, cb) {
var self = this;
this._queue.push([cb, requestor]);
if (this._queue.length === 1) {
process.nextTick(function() {
cb(null, self._value);
});
}
};
Entry.prototype.release = function(requestor, cb) {
var self = this,
queue = this._queue;
setImmediate(function() {
assert.strictEqual(requestor, queue.shift()[1]);
self._age = 0;
cb();
if (queue.length)
queue[0][0](null, self._value);
});
};
Entry.prototype.setRelease = function(newValue, requestor, cb) {
this._value = newValue;
this.release(requestor, cb);
};
Entry.prototype.age = function(d) {
if (this._queue.length)
return this._age = 0;
else
return this._age += (d || 0);
};
function noop() {
}

View File

@ -0,0 +1,51 @@
module.exports = KeyLock;
function KeyLock(entry, json) {
this._entry = entry;
this._json = json;
this._updated = false;
this._released = false;
}
KeyLock.prototype.get = function() {
if (!this._json)
return undefined;
else
return JSON.parse(this._json);
};
KeyLock.prototype.set = function(newValue) {
if (this._released)
throw new Error("Can't set after releasing a lock.");
this._json = JSON.stringify(newValue);
this._updated = true;
};
KeyLock.prototype.del = function() {
if (this._released)
throw new Error("Can't delete after releasing a lock.");
this._json = undefined;
this._updated = true;
};
KeyLock.prototype.release = function(cb) {
if (this._released)
throw new Error('KeyLock has already been released.');
cb = cb || noop;
this._released = true;
if (!this._updated)
this._entry.release(-1, cb);
else
this._entry.setRelease(this._json, -1, cb);
};
function noop() {
}

10
node_modules/strong-store-cluster/lib/master/setup.js generated vendored Normal file
View File

@ -0,0 +1,10 @@
var cluster = require('cluster'),
Client = require('./Client.js');
for (var i = 0; i < cluster.workers.length; i++)
new Client(cluster.workers[i]);
cluster.on('online', function(worker) {
new Client(worker);
});

View File

@ -0,0 +1,68 @@
module.exports = Collection;
var inherits = require('util').inherits,
EventEmitter = require('events').EventEmitter,
KeyLock = require('./KeyLock.js'),
request = require('./request.js');
function Collection(name) {
this._name = name;
}
inherits(Collection, EventEmitter);
Collection.prototype.get = function(key, cb) {
this._request('get', key, null, function(err, msg) {
if (err)
return cb(err);
else if (!msg.json)
return cb(null, undefined);
else
return cb(null, JSON.parse(msg.json));
});
};
Collection.prototype.set = function(key, value, cb) {
var data = { json: JSON.stringify(value) };
this._request('set', key, data, cb && function(err, msg) {
return cb(err);
});
};
Collection.prototype.del = function(key, cb) {
return this._request('set', key, null, cb && function(err, msg) {
return cb(err);
});
};
Collection.prototype.acquire = function(key, cb) {
var self = this;
this._request('acquire', key, null, function(err, msg) {
if (err)
return cb(err);
var json = msg.json;
var lock = new KeyLock(self, key, json);
cb(null, lock, lock.get());
});
};
Collection.prototype.configure = function(config) {
var self = this;
this._request('configure', null, { config: config }, function(err, msg) {
if (err)
self.emit('error', err);
});
return this;
};
// This function clobbers `data` if specified
Collection.prototype._request = function(method, key, data, cb) {
request(method, this._name, key, data, cb);
};

View File

@ -0,0 +1,56 @@
module.exports = KeyLock;
var request = require('./request.js');
function KeyLock(collection, key, json) {
this._collection = collection;
this._key = key;
this._json = json;
this._updated = false;
this._released = false;
}
KeyLock.prototype.get = function() {
if (!this._json)
return undefined;
else
return JSON.parse(this._json);
};
KeyLock.prototype.set = function(newValue) {
if (this._released)
throw new Error("Can't set after releasing a lock.");
this._json = JSON.stringify(newValue);
this._updated = true;
};
KeyLock.prototype.del = function() {
if (this._released)
throw new Error("Can't delete after releasing a lock.");
this._json = undefined;
this._updated = true;
};
KeyLock.prototype.release = function(cb) {
if (this._released)
throw new Error('KeyLock has already been released.');
this._released = true;
if (!this._updated)
this._collection._request('release', this._key, null, cb && afterRelease);
else
this._collection._request('setRelease',
this._key,
{ json: this._json },
cb && afterRelease);
function afterRelease(err, msg) {
return cb(err);
}
};

View File

@ -0,0 +1,51 @@
module.exports = request;
process.on('message', onMessage);
var requestIdCounter = 0;
var requestCallbacks = {};
// This function clobbers `data` if specified
function request(method, collection, key, data, cb) {
data = data || {};
data.type = 'DSM_REQUEST';
data.method = method;
data.collection = collection;
data.key = key;
if (cb) {
var requestId = getRequestId();
requestCallbacks[requestId] = cb;
data.requestId = requestId;
}
process.send(data);
}
function onMessage(msg) {
if (msg.type !== 'DSM_REPLY')
return;
var requestId = msg.requestId;
var cb = requestCallbacks[requestId];
delete requestCallbacks[requestId];
if (msg.err) {
var err = new Error('Master error: ' + msg.err);
return cb(err);
}
cb(null, msg);
}
function getRequestId() {
return ++requestIdCounter;
}

44
node_modules/strong-store-cluster/package.json generated vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,17 @@
var helper = require('./helper/cluster-helper');
suite('concurrent increment', function() {
test('master only', function(cb) {
helper.run('do-concurrent-inc', true, 0, 4, cb);
});
test('four workers', function(cb) {
helper.run('do-concurrent-inc', false, 4, 4, cb);
});
test('master and four workers', function(cb) {
helper.run('do-concurrent-inc', true, 4, 4, cb);
});
});

12
node_modules/strong-store-cluster/test/get-set-del.js generated vendored Normal file
View File

@ -0,0 +1,12 @@
var helper = require('./helper/cluster-helper');
suite('get-set-del', function() {
test('master', function(cb) {
helper.run('do-get-set-del', true, 0, 1, cb);
});
test('worker', function(cb) {
helper.run('do-get-set-del', false, 1, 1, cb);
});
});

View File

@ -0,0 +1,83 @@
var assert = require('assert'),
cluster = require('cluster');
if (!process.env.CLUSTER_TEST) {
// We're require()'d by the test harness. Export a function that start
// the test.
exports.run = function(filename, inMaster, workers, concurrency, cb) {
assert(filename);
assert(inMaster || workers);
assert(concurrency > 0);
var env = {};
for (var key in process.env)
env[key] = process.env[key];
env.CLUSTER_TEST = require.resolve('./' + filename);
env.CLUSTER_TEST_IN_MASTER = inMaster;
env.CLUSTER_TEST_WORKERS = workers;
env.CLUSTER_TEST_CONCURRENCY = concurrency;
var cp = require('child_process').fork(module.filename,
{env: env, stdio: 'inherit'});
cp.on('exit', function(exitCode, termSig) {
assert(exitCode === 0);
assert(!termSig);
cb();
});
};
} else {
// We're being spawned as a standalone process. Execute the test and/or spawn
// cluster workers that do.
var filename = process.env.CLUSTER_TEST,
inMaster = !!+process.env.CLUSTER_TEST_IN_MASTER,
workers = ~~ + process.env.CLUSTER_TEST_WORKERS,
concurrency = ~~ + process.env.CLUSTER_TEST_CONCURRENCY;
var test = require(filename);
// Both the master and the worker have .setup() always called once.
test.setup && test.setup();
var waiting = 0;
// If we're the master process, spawn a number of workers.
if (cluster.isMaster && workers) {
for (var i = 0; i < workers; i++)
var worker = cluster.fork();
waiting += workers;
cluster.on('exit', function(worker, exitCode, termSig) {
assert(exitCode === 0);
assert(!termSig);
done();
});
}
// If we're either a worker, or the master is supposed to run the tests,
// run the test cases.
if (cluster.isWorker || inMaster) {
waiting += concurrency;
for (var i = 0; i < concurrency; i++)
test.run(done);
}
}
function done() {
assert(--waiting >= 0);
if (waiting === 0)
return test.teardown && test.teardown();
}

View File

@ -0,0 +1,51 @@
exports.run = run;
exports.teardown = teardown;
var ROUNDS = 100;
assert = require('assert'),
cluster = require('cluster'),
store = require('../..');
function run(cb) {
var left = ROUNDS,
coll = store.collection('counter');
increment();
function increment() {
coll.acquire('counter', function(err, lock, val) {
assert(!err);
if (!val)
val = 1;
else
val++;
lock.set(val);
lock.release();
if (--left > 0)
increment();
else
cb();
});
}
}
function teardown() {
if (cluster.isWorker)
process._channel.unref();
if (cluster.isMaster) {
store.collection('counter').get('counter', function(err, value) {
assert(value % ROUNDS === 0);
assert(value >= ROUNDS);
});
}
}

View File

@ -0,0 +1,60 @@
exports.run = run;
exports.teardown = teardown;
var assert = require('assert'),
cluster = require('cluster'),
store = require('../..');
function run(cb) {
var testsRun = 0;
testWith('test1', 'key1', 'zulis', onDone);
testWith('test2', 'quux', 'stoll', onDone);
testWith('test2', 'key1', 'urals', onDone);
testWith('test2', 'key2', 'quipp', onDone);
function onDone() {
if (++testsRun === 4)
cb();
}
}
function testWith(collectionName, key, testValue, cb) {
var coll = store.collection(collectionName);
coll.get(key, function(err, value) {
assert(!err);
assert(value === undefined);
coll.set(key, testValue, function(err) {
assert(!err);
coll.get(key, function(err, value) {
assert(!err);
assert(value === testValue);
coll.del(key, function(err) {
assert(!err);
coll.get(key, function(err, value) {
assert(!err);
assert(value === undefined);
cb();
});
});
});
});
});
}
function teardown() {
if (cluster.isWorker)
process._channel.unref();
}

View File

@ -0,0 +1,80 @@
exports.run = run;
exports.teardown = teardown;
var assert = require('assert'),
cluster = require('cluster'),
store = require('../..');
function run(cb) {
var testsRun = 0;
testWith('test1', 'key1', {foo: 'zulis'}, onDone);
testWith('test2', 'quux', ['stoll'], onDone);
testWith('test2', 'key1', 'urals', onDone);
testWith('test2', 'key2', 42, onDone);
function onDone() {
if (++testsRun === 4)
cb();
}
}
function testWith(collectionName, key, testValue, cb) {
var coll = store.collection(collectionName);
coll.set(key, testValue, function(err, value) {
assert(!err);
coll.acquire(key, function(err, lock, value) {
assert(!err);
assert.deepEqual(testValue, value);
assert.deepEqual(testValue, lock.get());
// Non-primitive values should be deep-cloned.
if (typeof testValue === 'object') {
assert(testValue !== value);
assert(testValue !== lock.get());
}
lock.set('other');
assert('other' === lock.get());
lock.release(function(err) {
assert(!err);
});
coll.acquire(key, function(err, lock, value) {
assert(!err);
assert('other' === value);
assert('other' === lock.get());
lock.del();
assert(undefined === lock.get());
lock.release(function(err) {
assert(!err);
});
coll.get(key, function(err, value) {
assert(!err);
assert(undefined === value);
// That was it!
cb();
});
});
});
});
}
function teardown() {
if (cluster.isWorker)
process._channel.unref();
}

View File

@ -0,0 +1,12 @@
var helper = require('./helper/cluster-helper');
suite('lock-get-set-del', function() {
test('master', function(cb) {
helper.run('do-lock-get-set-del', true, 0, 1, cb);
});
test('worker', function(cb) {
helper.run('do-lock-get-set-del', false, 1, 1, cb);
});
});

View File

@ -20,8 +20,10 @@
"moment": "",
"async": "",
"passport-oauth": "",
"markdown": "",
"node-uuid": "~1.4.1",
"MD5": "~1.2.1"
"MD5": "~1.2.1",
"pushover-notifications": "~0.2.2"
},
"devDependencies": {
"supervisor": ""

View File

@ -46,6 +46,10 @@
min-height: 40px;
}
.nav {
width: 100%;
}
.nav > li {
border-right: 1px solid #41bedd;
}
@ -76,3 +80,13 @@
background-color: @navbarSecondaryLinkBackgroundActive;
}
}
.day-of-year {
float: right !important;
position: relative;
color: white;
font-size: 18px;
top: 10px;
right: 10px;
}

View File

@ -288,6 +288,10 @@ header {
width: 140px;
}
.tech-current {
font-weight: bold;
}
.enteries {
margin-left: 150px;
position: relative;
@ -719,3 +723,33 @@ header {
background-position: -7px -400px;
}
}
th.sort-true::after {
width: 0;
height: 0;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-top: 8px solid #000;
content: "";
top: 12px;
left: 10px;
position: relative;
}
th.sort-false::after {
width: 0;
height: 0;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-bottom: 8px solid #000;
content: "";
bottom: 11px;
left: 10px;
position: relative;
}

View File

@ -9,6 +9,7 @@ angular.module('biomed', ['biomed.filters', 'biomed.services', 'biomed.directive
sales: 'Sales',
other: 'Others'
};
$rootScope.dayOfYear = moment().dayOfYear();
})
.config(function($routeProvider, $locationProvider, $httpProvider) {

View File

@ -18,9 +18,10 @@ biomed.TechScheduleCtrl = function($scope, $routeParams, $location, Schedule, Us
function updateDate() {
Schedule.index({
tech: $routeParams.id,
start: $scope.date.toJSON(),
end: moment($scope.date).add('days', 7).toDate().toJSON()
start: moment($scope.date).subtract('days', 10).toDate().toJSON(),
end: moment($scope.date).add('days', 21).toDate().toJSON()
}, function(result) {
console.log(result);
$scope.schedule = result;
});
}
@ -85,6 +86,25 @@ biomed.SchedulePmsCtrl = function($scope, Clients) {
});
}
$scope.sort = {
column: 'client.name',
descending: false
};
$scope.selectedCls = function(column) {
return column == $scope.sort.column && 'sort-' + $scope.sort.descending;
}
$scope.changeSorting = function(column) {
var sort = $scope.sort;
if (sort.column == column) {
sort.descending = !sort.descending;
} else {
sort.column = column;
sort.descending = false;
}
};
$scope.$watch('month', filter);
};
@ -181,17 +201,60 @@ biomed.UserClockCtrl = function($scope, $routeParams, Users) {
};
biomed.PostIndexCtrl = function($scope, $routeParams, Posts, LocationBinder) {
$scope.loading = true;
var updatePosts = function() {
$scope.loading = true;
$scope.posts = Posts.index(function() {
$scope.loading = false;
});
$scope.posts = Posts.index(
{page: $scope.page},
function() {
$scope.loading = false;
$scope.posted = 0;
angular.forEach($scope.posts, function(value) {
if (value.status === "posted") {
$scope.posted += 1;
}
});
});
};
$scope.selectPage = function(page) {
$scope.page = page;
};
$scope.$watch('page', updatePosts);
};
biomed.PostAddCtrl = function($scope, Posts, $location) {
$scope.tagOptions = {
'multiple': true,
'simple_tags': true,
'tags': [],
'formatNoMatches': function() { return 'Type a tag and press return to add it.'; }
};
$scope.pages = [
{ value: 'front', label: 'Front Page' },
{ value: 'about-us', label: 'About Us' },
{ value: 'sales', label: 'Sales' },
{ value: 'service', label: 'Service' }
];
$scope.togglePage = function(page) {
var idx = $scope.model.pages.indexOf(page.value);
if (idx > -1) {
$scope.model.pages.splice(idx, 1);
} else {
$scope.model.pages.push(page.value);
}
}
$scope.model = {
gallery: []
gallery: [],
pages: [],
postedOn: new Date()
};
$scope.titleImageOptions = {
@ -255,10 +318,6 @@ biomed.PostAddCtrl = function($scope, Posts, $location) {
$scope.model.status = status;
$scope.model.createdOn = new Date();
if (status === 'posted') {
$scope.model.postedOn = new Date();
}
Posts.create($scope.model, function(result) {
$location.path("/posts/" + result._id);
});
@ -276,9 +335,34 @@ biomed.PostAddCtrl = function($scope, Posts, $location) {
biomed.PostEditCtrl = function($scope, Posts, $routeParams, $location) {
var galleryImages = {};
$scope.tagOptions = {
'multiple': true,
'simple_tags': true,
'tags': [],
'formatNoMatches': function() { return 'Type a tag and press return to add it.'; }
};
$scope.pages = [
{ value: 'front', label: 'Front Page' },
{ value: 'about-us', label: 'About Us' },
{ value: 'sales', label: 'Sales' },
{ value: 'service', label: 'Service' }
];
$scope.togglePage = function(page) {
var idx = $scope.model.pages.indexOf(page.value);
if (idx > -1) {
$scope.model.pages.splice(idx, 1);
} else {
$scope.model.pages.push(page.value);
}
}
$scope.model = Posts.get($routeParams, function() {
$scope.loading = false;
console.log($scope.model);
if ($scope.model.image) {
$scope.existingTitleImages = [$scope.model.image];
}
@ -287,6 +371,10 @@ biomed.PostEditCtrl = function($scope, Posts, $routeParams, $location) {
for (var i = 0; i < $scope.model.gallery.length; i++) {
galleryImages[$scope.model.gallery[i]] = 1;
}
if (!$scope.model.postedOn) {
$scope.model.postedOn = new Date();
}
});
$scope.titleImageOptions = {
@ -349,11 +437,7 @@ biomed.PostEditCtrl = function($scope, Posts, $routeParams, $location) {
$scope.model.gallery = Object.keys(galleryImages);
$scope.model.status = status;
if (status === 'posted') {
$scope.model.postedOn = new Date();
} else {
$scope.model.postedOn = null;
}
console.log($scope.model);
Posts.update({id: $scope.model._id}, $scope.model, function(result) {
$location.path("/posts/");
@ -396,7 +480,7 @@ biomed.ClientIndexCtrl = function($scope, $filter, $routeParams, Clients, Locati
LocationBinder($scope, ['query']);
$scope.filter = function() {
filteredData = $filter('filter')(allData, $scope.query);
filteredData = $filter('orderBy')($filter('filter')(allData, $scope.query), $scope.sort.column, $scope.sort.descending);
index = initialPageSize;
$scope.canLoad = true;
$scope.clients = filteredData.slice(0, initialPageSize);
@ -407,6 +491,27 @@ biomed.ClientIndexCtrl = function($scope, $filter, $routeParams, Clients, Locati
index += pageSize;
$scope.canLoad = index < filteredData.length;
}
$scope.sort = {
column: 'name',
descending: false
};
$scope.selectedCls = function(column) {
return column == $scope.sort.column && 'sort-' + $scope.sort.descending;
}
$scope.changeSorting = function(column) {
var sort = $scope.sort;
if (sort.column == column) {
sort.descending = !sort.descending;
} else {
sort.column = column;
sort.descending = false;
}
$scope.filter();
};
};
biomed.ClientAddCtrl = function($scope, Clients, $location) {
@ -578,6 +683,10 @@ biomed.WorkorderIndexCtrl = function($scope, $filter, $routeParams, Workorders,
$scope.$watch('end', fetchData);
$scope.sort = {
column: 'scheduling.start',
descending: true
};
$scope.addItems = function() {
$scope.workorders = $scope.workorders.concat(filteredData.slice(index, index + pageSize));
@ -592,6 +701,20 @@ biomed.WorkorderIndexCtrl = function($scope, $filter, $routeParams, Workorders,
$scope.workorders = filteredData.slice(0, initialPageSize);
};
$scope.selectedCls = function(column) {
return column == $scope.sort.column && 'sort-' + $scope.sort.descending;
}
$scope.changeSorting = function(column) {
var sort = $scope.sort;
if (sort.column == column) {
sort.descending = !sort.descending;
} else {
sort.column = column;
sort.descending = false;
}
};
function fetchData() {
$scope.loading = true;
@ -657,6 +780,10 @@ biomed.WorkorderAddCtrl = function($scope, $location, Workorders, Schedule, Clie
$scope.$watch('group', updateUsers);
$scope.$watch('model.client', function() {
$scope.currentClient = Clients.get({ id: $scope.model.client });
});
Clients.index(function(result) {
$scope.clients = result;
});

View File

@ -89,10 +89,17 @@ angular.module('biomed.directives', [])
attr.$observe('value', update)();
attr.$observe('title', function(){ update(); a.text(tab.title); })();
attr.$observe('visible', function(){
update();
tab.tabElement[0].style.display = (tab.visible === "false") ? 'none' : 'block';
})();
function update() {
console.log(attr.visible);
tab.title = attr.title;
tab.value = attr.value || attr.title;
tab.visible = attr.visible;
if (!ngModel.$setViewValue && (!ngModel.$viewValue || tab == selectedTab)) {
// we are not part of angular
ngModel.$viewValue = tab.value;
@ -235,17 +242,17 @@ angular.module('biomed.directives', [])
function setupScale() {
x = d3.scale.linear()
.range([0, 100])
.domain([420, 1320])
.domain([420, 1140])
.clamp(true);
}
setupScale();
var color = d3.scale.category20();
var hourWidth = 100 / 15;
var hourWidth = 100 / 12;
$scope.hourMarkers = [];
for (var i = 7; i < 22; i++) {
for (var i = 7; i < 19; i++) {
$scope.hourMarkers.push({
date: moment({ hour: i }).toDate(),
style: {
@ -266,13 +273,16 @@ angular.module('biomed.directives', [])
function generateDate() {
var range = moment($scope.date);
var data = {};
var current = range.format('ddd MMM Do YYYY');
for (var i = 0; i < 7; i++) {
for (var i = -7; i < 22; i++) {
var day = range.clone().add(i, 'days');
var key = day.format('MM-DD-YYYY');
var label = day.format('ddd MMM Do YYYY');
data[key] = {
order: i,
current: current == label,
label: label,
values: []
};
@ -345,7 +355,12 @@ angular.module('biomed.directives', [])
})
});
$scope.data = data;
var dataArray = [];
for (var o in data) {
dataArray.push(data[o]);
}
$scope.data = dataArray;
}
}
};
@ -374,7 +389,7 @@ angular.module('biomed.directives', [])
rangeDate = moment($scope.date).startOf('day');
rangeStart = moment(rangeDate).add('hours', 7);
rangeEnd = moment(rangeDate).add('hours', 22);
rangeEnd = moment(rangeDate).add('hours', 19);
x = d3.time.scale()
.range([0, 100])

View File

@ -257,7 +257,7 @@
</div>
</div>
</div>
<div class="tab-pane" title="Frequency">
<div class="tab-pane" title="Frequency" visible="{{accountHasPermission('client.frequency')}}">
<table class="table frequency">
<thead>
<tr>

View File

@ -20,10 +20,10 @@
<table class="biomed-table" infinite-scroll="addItems()" can-load="canLoad" threshold="300">
<thead>
<tr>
<th style="width: 20%"></th>
<th style="width: 48%">Client Name</th>
<th style="width: 20%">Contact</th>
<th style="width: 12%">Phone</th>
<th style="width: 20%" ng-class="selectedCls('identifier')" ng-click="changeSorting('identifier')">ID</th>
<th style="width: 48%" ng-class="selectedCls('name')" ng-click="changeSorting('name')">Client Name</th>
<th style="width: 20%" ng-class="selectedCls('contacts[0].name')" ng-click="changeSorting('contacts[0].name')">Contact</th>
<th style="width: 12%" ng-class="selectedCls('contacts[0].phone')" ng-click="changeSorting('contacts[0].phone')">Phone</th>
</tr>
</thead>
<tbody>

View File

@ -29,6 +29,32 @@
<textarea ng-model="model.details" class="input-xlarge"></textarea>
</div>
</div>
<div class="control-group">
<label class="control-label">Posted On</label>
<div class="controls">
<span><input ng-model="model.postedOn" datepicker type="text" class="input-small"></span>
</div>
</div>
<div class="control-group">
<label class="control-label">Pages</label>
<div class="controls">
<label ng-repeat="page in pages">
<input
type="checkbox"
name="model.pages[]"
value="page.value"
ng-checked="model.pages.indexOf(page.value) > -1"
ng-click="togglePage(page)"
>{{page.label}}</input>
</label>
</div>
</div>
<div class="control-group">
<label class="control-label">Tags</label>
<div class="controls">
<input type="text" ui-select2="tagOptions" ng-model="model.tags" class="input-xxlarge" />
</div>
</div>
</div>
</div>
</div>
@ -42,7 +68,7 @@
<div class="dropzone" dropzone="titleImageOptions"></div>
</div>
</div>
<div class="control-group">
<div class="control-group" ng-show="model.image">
<label class="control-label">Gallery</label>
<div class="controls">
<div class="dropzone" dropzone="galleryImageOptions"></div>

View File

@ -29,6 +29,32 @@
<textarea ng-model="model.details" class="input-xlarge"></textarea>
</div>
</div>
<div class="control-group">
<label class="control-label">Posted On</label>
<div class="controls">
<span><input ng-model="model.postedOn" datepicker type="text" class="input-small"></span>
</div>
</div>
<div class="control-group">
<label class="control-label">Pages</label>
<div class="controls">
<label ng-repeat="page in pages">
<input
type="checkbox"
name="model.pages[]"
value="page.value"
ng-checked="model.pages.indexOf(page.value) > -1"
ng-click="togglePage(page)"
>{{page.label}}</input>
</label>
</div>
</div>
<div class="control-group">
<label class="control-label">Tags</label>
<div class="controls">
<input type="text" ui-select2="tagOptions" ng-model="model.tags" class="input-xxlarge" />
</div>
</div>
</div>
</div>
</div>
@ -42,7 +68,7 @@
<div class="dropzone" dropzone="titleImageOptions" existing="existingTitleImages"></div>
</div>
</div>
<div class="control-group">
<div class="control-group" ng-show="model.image">
<label class="control-label">Gallery</label>
<div class="controls">
<div class="dropzone" dropzone="galleryImageOptions" existing="existingGalleryImages"></div>

View File

@ -9,20 +9,28 @@
<div class="span12">
<div class="toolbelt">
<a href="/posts/add" class="btn btn-primary">Create new Post</a>
<a ng-click="selectPage('')" class="btn">All</a>
<a ng-click="selectPage('front')" class="btn">Front Page</a>
<a ng-click="selectPage('about-us')" class="btn">About Us</a>
<a ng-click="selectPage('sales')" class="btn">Sales</a>
<a ng-click="selectPage('service')" class="btn">Service</a>
<div class="pull-right">
<span class="toolbelt-text">Total Published: {{posted}}</span>
</div>
</div>
<table class="biomed-table" infinite-scroll="addItems()" can-load="canLoad" threshold="300">
<thead>
<tr>
<th style="width: 5%">Title</th>
<th style="width: 5%">Author</th>
<th style="width: 5%">Created on</th>
<th style="width: 5%">Posted on</th>
<th style="width: 5%">Status</th>
<th style="width: 45%">Title</th>
<th style="width: 15%">Author</th>
<th style="width: 15%">Created on</th>
<th style="width: 15%">Posted on</th>
<th style="width: 10%">Status</th>
</tr>
</thead>
<tbody>
<tr ng-show="loading"><td colspan="4" class="table-loading"><i class="loader"></i></td></tr>
<tr ng-hide="loading || posts.length"><td colspan="4" class="table-message">There is no information to display.</td></tr>
<tr ng-show="loading"><td colspan="5" class="table-loading"><i class="loader"></i></td></tr>
<tr ng-hide="loading || posts.length"><td colspan="5" class="table-message">There is no information to display.</td></tr>
<tr ng-hide="loading" ng-repeat="post in posts">
<td>
<a href="/posts/{{post._id}}">

View File

@ -33,20 +33,20 @@
<table class="biomed-table" infinite-scroll="addItems()" can-load="canLoad" threshold="300">
<thead>
<tr>
<th style="width: 48%">Client Name</th>
<th style="width: 20%">Reason</th>
<th style="width: 20%">Contact</th>
<th style="width: 12%">Phone</th>
<th style="width: 48%" ng-class="selectedCls('client.name')" ng-click="changeSorting('client.name')">Client Name</th>
<th style="width: 20%" ng-class="selectedCls('reason')" ng-click="changeSorting('reason')">Reason</th>
<th style="width: 20%" ng-class="selectedCls('client.contacts[0].name')" ng-click="changeSorting('client.contacts[0].name')">Contact</th>
<th style="width: 12%" ng-class="selectedCls('client.contacts[0].phone')" ng-click="changeSorting('client.contacts[0].phone')">Phone</th>
</tr>
</thead>
<tbody>
<tr ng-show="loading"><td colspan="4" class="table-loading"><i class="loader"></i></td></tr>
<tr ng-hide="loading || pms.length"><td colspan="4" class="table-message">There is no information to display.</td></tr>
<tr ng-hide="loading" ng-repeat="pm in pms">
<tr ng-hide="loading" ng-repeat="pm in pms | orderBy : sort.column : sort.descending">
<td><a ng-href="/workorders/add?workorderType=pm&amp;clientId={{pm.client._id}}&amp;type={{pm.reason}}">{{pm.client.name}} ({{pm.client.identifier | uppercase}})</a><br>
<td>{{pm.reason}}</td>
<td>{{pm.client.contacts[0].name}}</td>
<td>{{pm.lient.contacts[0].phone}}</td>
<td>{{pm.client.contacts[0].phone}}</td>
</tr>
</tbody>
</table>

Some files were not shown because too many files have changed in this diff Show More